Rebuild admin pages around DataTable/AdminModal; restructure categories/groups and v2 groups API
Remove the generic $lib/svelte-admin CRUD layer and AdminTable.svelte; each admin route now owns its own form/modal logic. Add DataTable.svelte, superform-errors.ts, and profile-specific modal/field components. Flatten proclamation categories/groups and drop subscription levels; add the v2 groups API; update v1/v2 endpoints, serializers, auth, and regenerated Prisma client with the new migrations. Document the overhaul plan in docs/ADMIN_OVERHAUL.md.
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
<script lang="ts">
|
||||
let {
|
||||
record = null,
|
||||
fieldErrors = {} as Record<string, string>
|
||||
}: {
|
||||
record?: Record<string, unknown> | null;
|
||||
fieldErrors?: Record<string, string>;
|
||||
} = $props();
|
||||
|
||||
let email = $state('');
|
||||
let firstName = $state('');
|
||||
let lastName = $state('');
|
||||
let phoneNumber = $state('');
|
||||
|
||||
$effect(() => {
|
||||
const rec = record ?? {};
|
||||
const user = rec.user as Record<string, unknown> | undefined;
|
||||
email = String((user?.email as string) ?? rec.email ?? '');
|
||||
firstName = String(rec.firstName ?? '');
|
||||
lastName = String(rec.lastName ?? '');
|
||||
phoneNumber = String(rec.phoneNumber ?? '');
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-4">
|
||||
<div>
|
||||
<label for="field-email" class="mb-1 block text-sm font-medium text-slate-700">Email</label>
|
||||
<input
|
||||
id="field-email"
|
||||
type="email"
|
||||
name="email"
|
||||
required
|
||||
bind:value={email}
|
||||
placeholder="user@example.com"
|
||||
class="w-full rounded-md border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 shadow-sm transition focus:border-indigo-500 focus:outline-none"
|
||||
/>
|
||||
{#if fieldErrors.email}
|
||||
<p class="mt-1 text-sm text-red-600">{fieldErrors.email}</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="field-firstName" class="mb-1 block text-sm font-medium text-slate-700"
|
||||
>First Name</label
|
||||
>
|
||||
<input
|
||||
id="field-firstName"
|
||||
type="text"
|
||||
name="firstName"
|
||||
required
|
||||
bind:value={firstName}
|
||||
class="w-full rounded-md border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 shadow-sm transition focus:border-indigo-500 focus:outline-none"
|
||||
/>
|
||||
{#if fieldErrors.firstName}
|
||||
<p class="mt-1 text-sm text-red-600">{fieldErrors.firstName}</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="field-lastName" class="mb-1 block text-sm font-medium text-slate-700"
|
||||
>Last Name</label
|
||||
>
|
||||
<input
|
||||
id="field-lastName"
|
||||
type="text"
|
||||
name="lastName"
|
||||
required
|
||||
bind:value={lastName}
|
||||
class="w-full rounded-md border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 shadow-sm transition focus:border-indigo-500 focus:outline-none"
|
||||
/>
|
||||
{#if fieldErrors.lastName}
|
||||
<p class="mt-1 text-sm text-red-600">{fieldErrors.lastName}</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="field-phoneNumber" class="mb-1 block text-sm font-medium text-slate-700">
|
||||
Phone Number
|
||||
</label>
|
||||
<input
|
||||
id="field-phoneNumber"
|
||||
type="text"
|
||||
name="phoneNumber"
|
||||
required
|
||||
bind:value={phoneNumber}
|
||||
class="w-full rounded-md border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 shadow-sm transition focus:border-indigo-500 focus:outline-none"
|
||||
/>
|
||||
{#if fieldErrors.phoneNumber}
|
||||
<p class="mt-1 text-sm text-red-600">{fieldErrors.phoneNumber}</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user