- Replace per-model admin forms with the shared generic AdminTable/AdminModal CRUD - Add ProclamationGroup model and admin + API v2 routes (replaces subscription levels) - Drop subscription levels and flatten proclamation categories - Update v2 serializers and seed data accordingly
119 lines
2.7 KiB
Svelte
119 lines
2.7 KiB
Svelte
<script lang="ts">
|
|
import type { PageProps } from './$types';
|
|
import { enhance } from '$app/forms';
|
|
import AdminTable from '$lib/components/admin/AdminTable.svelte';
|
|
import AdminModal from '$lib/components/admin/AdminModal.svelte';
|
|
import Toast from '$lib/components/admin/Toast.svelte';
|
|
import { getModelConfig } from '$lib/svelte-admin/registry';
|
|
|
|
let { data, form }: PageProps = $props();
|
|
|
|
const config = getModelConfig('user-roles')!;
|
|
const canDelete = $derived((data.adminUser?.roleId ?? 4) <= 2);
|
|
|
|
let modalOpen = $state(false);
|
|
let editingRecord = $state<Record<string, unknown> | null>(null);
|
|
let toastMessage = $state('');
|
|
let fieldErrors = $state<Record<string, string>>({});
|
|
let formError = $state('');
|
|
|
|
let name = $state('');
|
|
let dirty = $state(false);
|
|
|
|
$effect(() => {
|
|
if (form && 'success' in (form as any)) {
|
|
const result = form as any;
|
|
if (result.success) {
|
|
toastMessage = 'Saved successfully';
|
|
closeModal();
|
|
} else {
|
|
formError = result.error ?? '';
|
|
fieldErrors = result.fieldErrors ?? {};
|
|
}
|
|
}
|
|
});
|
|
|
|
$effect(() => {
|
|
name = String((editingRecord ?? {})['name'] ?? '');
|
|
dirty = false;
|
|
});
|
|
|
|
function openCreate() {
|
|
editingRecord = null;
|
|
formError = '';
|
|
fieldErrors = {};
|
|
modalOpen = true;
|
|
}
|
|
|
|
function openEdit(record: Record<string, unknown>) {
|
|
editingRecord = record;
|
|
formError = '';
|
|
fieldErrors = {};
|
|
modalOpen = true;
|
|
}
|
|
|
|
function closeModal() {
|
|
modalOpen = false;
|
|
editingRecord = null;
|
|
formError = '';
|
|
fieldErrors = {};
|
|
}
|
|
|
|
function markDirty() {
|
|
if (editingRecord?.id) dirty = true;
|
|
}
|
|
</script>
|
|
|
|
<div>
|
|
<h1>{config.displayName}</h1>
|
|
<AdminTable
|
|
records={data.records}
|
|
listFields={config.listFields}
|
|
searchFields={config.searchFields}
|
|
fieldTypes={config.fieldTypes}
|
|
singular={config.displayNameSingular}
|
|
onRowClick={openEdit}
|
|
onNew={openCreate}
|
|
/>
|
|
</div>
|
|
|
|
<AdminModal
|
|
title={editingRecord
|
|
? `Edit ${config.displayNameSingular}`
|
|
: `Create ${config.displayNameSingular}`}
|
|
open={modalOpen}
|
|
onClose={closeModal}
|
|
{canDelete}
|
|
recordId={editingRecord?.id as string | undefined}
|
|
>
|
|
<form action="?/upsert" method="POST" use:enhance oninput={markDirty}>
|
|
{#if editingRecord?.id}
|
|
<input type="hidden" name="id" value={String(editingRecord.id)} />
|
|
{/if}
|
|
|
|
{#if formError}
|
|
<div>{formError}</div>
|
|
{/if}
|
|
|
|
<div>
|
|
<div>
|
|
<label for="field-name">Name</label>
|
|
<input type="text" id="field-name" name="name" bind:value={name} />
|
|
{#if fieldErrors.name}
|
|
<p>{fieldErrors.name}</p>
|
|
{/if}
|
|
</div>
|
|
|
|
<div>
|
|
<button type="submit" disabled={!!editingRecord?.id && !dirty}>
|
|
{editingRecord ? 'Update' : 'Create'}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</AdminModal>
|
|
|
|
{#if toastMessage}
|
|
<Toast message={toastMessage} />
|
|
{/if}
|