Commit 1: Create individual form components per route, remove generic AdminForm dependency

- Add per-route form components: UserRoleForm, UserForm,
  ProclamationForm (with category cascade), CategoryForm,
  FreqForm, LevelForm
- Update all admin +page.svelte files to use AdminTable + AdminModal
  + their specific form component instead of AdminPage
- Update profiles page to inline fields instead of using AdminForm
- Add custom Proclamation server action with category fallback logic
  (categoryId falls back to parentCategoryId when no sub-category)
- Remove cascade/were/fieldLabels logic from generic crud.ts
- Remove outdated cascade/where tests from crud.spec.ts
- Remove fieldLabels from AdminModelConfig type
This commit is contained in:
NeilClack
2026-07-07 21:01:02 -05:00
parent 2073248576
commit 22b6adee3f
17 changed files with 1440 additions and 90 deletions
+18 -30
View File
@@ -52,8 +52,6 @@ export async function loadRecords(locals: App.Locals, config: AdminModelConfig)
});
const relationOptionsData: Record<string, { label: string; value: string | number }[]> = {};
// For cascade child fields: maps field -> { childValue -> parentValue }
const cascadeOptionParentMap: Record<string, Record<string | number, string | number>> = {};
for (const [field, relConfig] of Object.entries(config.relationOptions ?? {})) {
const relDelegate = getDelegate({ ...config, modelName: relConfig.model });
@@ -61,13 +59,6 @@ export async function loadRecords(locals: App.Locals, config: AdminModelConfig)
[relConfig.valueField]: true,
[relConfig.labelField]: true
};
// Check if this field is a cascade child (needs parentId)
const cascadeEntry = Object.entries(config.cascadeFields ?? {}).find(
([, c]) => c.parentField === field
);
if (cascadeEntry) {
selectFields['parentId'] = true;
}
const options = await relDelegate.findMany({
select: selectFields,
orderBy: { [relConfig.labelField]: 'asc' }
@@ -77,27 +68,11 @@ export async function loadRecords(locals: App.Locals, config: AdminModelConfig)
value: opt[relConfig.valueField] as string | number
}));
relationOptionsData[field] = mapped;
// Build parent map if cascade child
if (cascadeEntry) {
const childField = cascadeEntry[0];
const parentMap: Record<string | number, string | number> = {};
const rows = options as Record<string, unknown>[];
for (const row of rows) {
const childVal = row[relConfig.valueField] as string | number;
const parentVal = row['parentId'] as number | null;
if (parentVal != null) {
parentMap[childVal] = parentVal;
}
}
cascadeOptionParentMap[childField] = parentMap;
}
}
return {
records: records as Record<string, unknown>[],
relationOptionsData,
cascadeOptionParentMap
relationOptionsData
};
}
@@ -106,8 +81,11 @@ export async function upsertRecord(
config: AdminModelConfig,
locals: App.Locals
) {
const formData =
requestOrFormData instanceof FormData ? requestOrFormData : await requestOrFormData.formData();
const schema = modelSchemas[config.modelName];
const form = schema ? await superValidate(requestOrFormData, zod4(schema)) : null;
const form = schema ? await superValidate(formData, zod4(schema)) : null;
if (form && !form.valid) {
const { error, fieldErrors } = superFormErrorsToFieldErrors(
@@ -116,8 +94,6 @@ export async function upsertRecord(
return { ...form, success: false, error, fieldErrors };
}
const formData =
requestOrFormData instanceof FormData ? requestOrFormData : await requestOrFormData.formData();
const rawId = formData.get('id')?.toString();
const data =
form?.data ??
@@ -163,8 +139,20 @@ export async function upsertRecord(
await delegate.create({ data });
} catch (e: unknown) {
if (e && typeof e === 'object' && 'code' in e && (e as { code: string }).code === 'P2002') {
const meta = (e as { meta?: { target?: string[] } }).meta;
const meta = (e as { meta?: { target?: string[]; modelName?: string } }).meta;
const fields = meta?.target ?? [];
console.error(
`[crud] P2002 on ${meta?.modelName ?? 'unknown'} create. data=`,
data,
`meta.target=`,
meta?.target
);
if (fields.length === 0) {
return {
success: false,
error: 'A database constraint violation occurred. The record could not be created.'
};
}
return {
success: false,
error: `A record with this ${fields.join(', ')} already exists.`,