fix(invite-ui): accessibility, i18n, and load function tests
Some checks failed
CI / Unit & Component Tests (push) Failing after 3m7s
CI / OCR Service Tests (push) Successful in 37s
CI / Backend Unit Tests (push) Failing after 2m47s
CI / Unit & Component Tests (pull_request) Failing after 2m34s
CI / OCR Service Tests (pull_request) Successful in 34s
CI / Backend Unit Tests (pull_request) Failing after 2m43s
Some checks failed
CI / Unit & Component Tests (push) Failing after 3m7s
CI / OCR Service Tests (push) Successful in 37s
CI / Backend Unit Tests (push) Failing after 2m47s
CI / Unit & Component Tests (pull_request) Failing after 2m34s
CI / OCR Service Tests (pull_request) Successful in 34s
CI / Backend Unit Tests (pull_request) Failing after 2m43s
- WCAG 1.3.1: add for/id pairs to all 6 fields in the create-invite form - WCAG 1.4.1: add status icon (●○✕⏱) to status badge alongside label - Add aria-label to copy-link buttons in the invite table - Replace hardcoded German strings with i18n keys (Alle, Widerrufen, Link kopieren, Kopiert, Abbrechen) - Increase filter button touch targets py-1.5 → py-2 - Add 5 unit tests for register page load function (no-code, ok, error-with-code, error-without-code, URL-encoding) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -62,6 +62,21 @@ function statusColor(status: string) {
|
||||
return 'text-gray-500 bg-gray-100';
|
||||
}
|
||||
}
|
||||
|
||||
function statusIcon(status: string) {
|
||||
switch (status) {
|
||||
case 'active':
|
||||
return '●';
|
||||
case 'exhausted':
|
||||
return '○';
|
||||
case 'revoked':
|
||||
return '✕';
|
||||
case 'expired':
|
||||
return '⏱';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -81,15 +96,15 @@ function statusColor(status: string) {
|
||||
>
|
||||
<a
|
||||
href="/admin/invites"
|
||||
class="px-3 py-1.5 transition-colors {data.status !== 'all' ? 'bg-primary text-primary-fg' : 'bg-surface text-ink-2 hover:bg-muted'}"
|
||||
class="px-3 py-2 transition-colors {data.status !== 'all' ? 'bg-primary text-primary-fg' : 'bg-surface text-ink-2 hover:bg-muted'}"
|
||||
>
|
||||
{m.admin_invite_status_active()}
|
||||
</a>
|
||||
<a
|
||||
href="/admin/invites?status=all"
|
||||
class="border-l border-line px-3 py-1.5 transition-colors {data.status === 'all' ? 'bg-primary text-primary-fg' : 'bg-surface text-ink-2 hover:bg-muted'}"
|
||||
class="border-l border-line px-3 py-2 transition-colors {data.status === 'all' ? 'bg-primary text-primary-fg' : 'bg-surface text-ink-2 hover:bg-muted'}"
|
||||
>
|
||||
Alle
|
||||
{m.admin_btn_show_all()}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -136,7 +151,7 @@ function statusColor(status: string) {
|
||||
onclick={() => copyLink(form!.created!.id, form!.created!.shareableUrl)}
|
||||
class="flex-shrink-0 rounded border border-green-300 bg-white px-3 py-1.5 font-sans text-xs font-bold text-green-700 transition-colors hover:bg-green-50"
|
||||
>
|
||||
{copiedId === form.created.id ? '✓' : 'Kopieren'}
|
||||
{copiedId === form.created.id ? m.admin_btn_copied() : m.admin_btn_copy_link()}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -155,11 +170,13 @@ function statusColor(status: string) {
|
||||
>
|
||||
<div class="sm:col-span-2">
|
||||
<label
|
||||
for="invite-label"
|
||||
class="mb-1 block font-sans text-xs font-bold tracking-widest text-ink-2 uppercase"
|
||||
>
|
||||
{m.admin_new_invite_label()}
|
||||
</label>
|
||||
<input
|
||||
id="invite-label"
|
||||
type="text"
|
||||
name="label"
|
||||
class="block w-full border border-line px-3 py-2 font-serif text-sm text-ink focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring"
|
||||
@@ -167,11 +184,13 @@ function statusColor(status: string) {
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
for="invite-max-uses"
|
||||
class="mb-1 block font-sans text-xs font-bold tracking-widest text-ink-2 uppercase"
|
||||
>
|
||||
{m.admin_new_invite_max_uses()}
|
||||
</label>
|
||||
<input
|
||||
id="invite-max-uses"
|
||||
type="number"
|
||||
name="maxUses"
|
||||
min="1"
|
||||
@@ -180,11 +199,13 @@ function statusColor(status: string) {
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
for="invite-expires-at"
|
||||
class="mb-1 block font-sans text-xs font-bold tracking-widest text-ink-2 uppercase"
|
||||
>
|
||||
{m.admin_new_invite_expires()}
|
||||
</label>
|
||||
<input
|
||||
id="invite-expires-at"
|
||||
type="datetime-local"
|
||||
name="expiresAt"
|
||||
class="block w-full border border-line px-3 py-2 font-serif text-sm text-ink focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring"
|
||||
@@ -192,11 +213,13 @@ function statusColor(status: string) {
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
for="invite-prefill-first"
|
||||
class="mb-1 block font-sans text-xs font-bold tracking-widest text-ink-2 uppercase"
|
||||
>
|
||||
{m.admin_new_invite_prefill_first()}
|
||||
</label>
|
||||
<input
|
||||
id="invite-prefill-first"
|
||||
type="text"
|
||||
name="prefillFirstName"
|
||||
class="block w-full border border-line px-3 py-2 font-serif text-sm text-ink focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring"
|
||||
@@ -204,11 +227,13 @@ function statusColor(status: string) {
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
for="invite-prefill-last"
|
||||
class="mb-1 block font-sans text-xs font-bold tracking-widest text-ink-2 uppercase"
|
||||
>
|
||||
{m.admin_new_invite_prefill_last()}
|
||||
</label>
|
||||
<input
|
||||
id="invite-prefill-last"
|
||||
type="text"
|
||||
name="prefillLastName"
|
||||
class="block w-full border border-line px-3 py-2 font-serif text-sm text-ink focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring"
|
||||
@@ -216,11 +241,13 @@ function statusColor(status: string) {
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label
|
||||
for="invite-prefill-email"
|
||||
class="mb-1 block font-sans text-xs font-bold tracking-widest text-ink-2 uppercase"
|
||||
>
|
||||
{m.admin_new_invite_prefill_email()}
|
||||
</label>
|
||||
<input
|
||||
id="invite-prefill-email"
|
||||
type="email"
|
||||
name="prefillEmail"
|
||||
class="block w-full border border-line px-3 py-2 font-serif text-sm text-ink focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring"
|
||||
@@ -237,7 +264,7 @@ function statusColor(status: string) {
|
||||
onclick={() => (showNewForm = false)}
|
||||
class="px-4 py-2 font-sans text-xs font-bold tracking-widest text-ink-2 uppercase transition-colors hover:text-ink"
|
||||
>
|
||||
Abbrechen
|
||||
{m.btn_cancel()}
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
@@ -301,8 +328,10 @@ function statusColor(status: string) {
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
<span
|
||||
class="rounded px-2 py-0.5 font-sans text-xs font-bold {statusColor(invite.status)}"
|
||||
class="inline-flex items-center gap-1 rounded px-2 py-0.5 font-sans text-xs font-bold {statusColor(invite.status)}"
|
||||
aria-label={statusLabel(invite.status)}
|
||||
>
|
||||
<span aria-hidden="true">{statusIcon(invite.status)}</span>
|
||||
{statusLabel(invite.status)}
|
||||
</span>
|
||||
</td>
|
||||
@@ -311,9 +340,10 @@ function statusColor(status: string) {
|
||||
type="button"
|
||||
onclick={() => copyLink(invite.id, invite.shareableUrl)}
|
||||
class="font-sans text-xs text-brand-navy/60 transition-colors hover:text-brand-navy"
|
||||
aria-label="{m.admin_btn_copy_link()}: {invite.displayCode}"
|
||||
title={invite.shareableUrl}
|
||||
>
|
||||
{copiedId === invite.id ? '✓ Kopiert' : 'Link kopieren'}
|
||||
{copiedId === invite.id ? m.admin_btn_copied() : m.admin_btn_copy_link()}
|
||||
</button>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-right">
|
||||
@@ -327,7 +357,7 @@ function statusColor(status: string) {
|
||||
}}
|
||||
class="font-sans text-xs font-bold tracking-widest text-red-500 uppercase transition-colors hover:text-red-700"
|
||||
>
|
||||
Widerrufen
|
||||
{m.admin_btn_revoke()}
|
||||
</button>
|
||||
</form>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user