feat(frontend): invite-based registration UI
Some checks failed
CI / Unit & Component Tests (push) Failing after 2m37s
CI / OCR Service Tests (push) Successful in 32s
CI / OCR Service Tests (pull_request) Successful in 30s
CI / Backend Unit Tests (push) Failing after 2m47s
CI / Unit & Component Tests (pull_request) Failing after 2m29s
CI / Backend Unit Tests (pull_request) Failing after 2m46s
Some checks failed
CI / Unit & Component Tests (push) Failing after 2m37s
CI / OCR Service Tests (push) Successful in 32s
CI / OCR Service Tests (pull_request) Successful in 30s
CI / Backend Unit Tests (push) Failing after 2m47s
CI / Unit & Component Tests (pull_request) Failing after 2m29s
CI / Backend Unit Tests (pull_request) Failing after 2m46s
- Add /register route with invite code prefill, password show/hide - Add /login?registered=1 success banner - Add /admin/invites page: list, create, revoke, copy link - Add Einladungen nav section to admin sidebar (ADMIN_USER perm) - Add invite error codes to errors.ts - Add 48 i18n keys across de/en/es - Update hooks.server.ts to allow public access to invite/register API Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
88
frontend/src/routes/admin/invites/+page.server.ts
Normal file
88
frontend/src/routes/admin/invites/+page.server.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { fail } from '@sveltejs/kit';
|
||||
import { env } from '$env/dynamic/private';
|
||||
import { parseBackendError } from '$lib/errors';
|
||||
import type { Actions, PageServerLoad } from './$types';
|
||||
|
||||
export interface InviteListItem {
|
||||
id: string;
|
||||
code: string;
|
||||
displayCode: string;
|
||||
label?: string;
|
||||
useCount: number;
|
||||
maxUses?: number;
|
||||
expiresAt?: string;
|
||||
revoked: boolean;
|
||||
status: string;
|
||||
createdAt: string;
|
||||
shareableUrl: string;
|
||||
}
|
||||
|
||||
export const load: PageServerLoad = async ({ url, fetch }) => {
|
||||
const status = url.searchParams.get('status') ?? 'active';
|
||||
const apiUrl = env.API_INTERNAL_URL || 'http://localhost:8080';
|
||||
const res = await fetch(`${apiUrl}/api/invites?status=${encodeURIComponent(status)}`);
|
||||
|
||||
if (!res.ok) {
|
||||
const backendError = await parseBackendError(res);
|
||||
return {
|
||||
invites: [] as InviteListItem[],
|
||||
status,
|
||||
loadError: backendError?.code ?? 'INTERNAL_ERROR'
|
||||
};
|
||||
}
|
||||
|
||||
const invites: InviteListItem[] = await res.json();
|
||||
return { invites, status, loadError: null };
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
create: async ({ request, fetch }) => {
|
||||
const formData = await request.formData();
|
||||
const label = (formData.get('label') as string) || undefined;
|
||||
const maxUsesRaw = formData.get('maxUses') as string;
|
||||
const maxUses = maxUsesRaw ? parseInt(maxUsesRaw, 10) : undefined;
|
||||
const prefillFirstName = (formData.get('prefillFirstName') as string) || undefined;
|
||||
const prefillLastName = (formData.get('prefillLastName') as string) || undefined;
|
||||
const prefillEmail = (formData.get('prefillEmail') as string) || undefined;
|
||||
const expiresAt = (formData.get('expiresAt') as string) || undefined;
|
||||
|
||||
const apiUrl = env.API_INTERNAL_URL || 'http://localhost:8080';
|
||||
const res = await fetch(`${apiUrl}/api/invites`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
label,
|
||||
maxUses,
|
||||
prefillFirstName,
|
||||
prefillLastName,
|
||||
prefillEmail,
|
||||
expiresAt
|
||||
})
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const backendError = await parseBackendError(res);
|
||||
return fail(res.status, { createError: backendError?.code ?? 'INTERNAL_ERROR' });
|
||||
}
|
||||
|
||||
const created: InviteListItem = await res.json();
|
||||
return { created };
|
||||
},
|
||||
|
||||
revoke: async ({ request, fetch }) => {
|
||||
const formData = await request.formData();
|
||||
const id = formData.get('id') as string;
|
||||
|
||||
const apiUrl = env.API_INTERNAL_URL || 'http://localhost:8080';
|
||||
const res = await fetch(`${apiUrl}/api/invites/${encodeURIComponent(id)}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const backendError = await parseBackendError(res);
|
||||
return fail(res.status, { revokeError: backendError?.code ?? 'INTERNAL_ERROR' });
|
||||
}
|
||||
|
||||
return { revoked: id };
|
||||
}
|
||||
} satisfies Actions;
|
||||
Reference in New Issue
Block a user