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:
@@ -1,78 +1,87 @@
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { getModelConfig } from '$lib/svelte-admin/registry';
|
||||
import { loadRecords, deleteRecord, superFormErrorsToFieldErrors } from '$lib/svelte-admin/crud';
|
||||
import { prisma } from '$lib/db';
|
||||
import { superValidate } from 'sveltekit-superforms/server';
|
||||
import { zod4 } from 'sveltekit-superforms/adapters';
|
||||
import * as Schemas from '$lib/svelte-admin/schemas';
|
||||
import { z } from 'zod';
|
||||
import { superFormErrorsToFieldErrors } from '$lib/superform-errors';
|
||||
|
||||
const config = getModelConfig('proclamations')!;
|
||||
|
||||
function viewerRoleId(locals: App.Locals): number {
|
||||
return (locals.user as { roleId?: number } | null)?.roleId ?? 4;
|
||||
}
|
||||
const ProclamationSchema = z.object({
|
||||
name: z.string().min(1, 'Required'),
|
||||
solfeggioFrequencyId: z.coerce.number().int(),
|
||||
categoryId: z.coerce.number().int(),
|
||||
groupId: z.coerce.number().int(),
|
||||
content: z.string().optional().default('')
|
||||
});
|
||||
|
||||
function isSuper(locals: App.Locals): boolean {
|
||||
return viewerRoleId(locals) <= 1;
|
||||
return ((locals.user as { roleId?: number } | null)?.roleId ?? 4) <= 1;
|
||||
}
|
||||
|
||||
export const load: PageServerLoad = async ({ locals }) => {
|
||||
const base = await loadRecords(locals, config);
|
||||
const [proclamations, frequencies, categories, groups] = await Promise.all([
|
||||
prisma.proclamation.findMany({
|
||||
include: { category: true, group: true, solfeggioFrequency: true },
|
||||
orderBy: { name: 'asc' }
|
||||
}),
|
||||
prisma.solfeggioFrequency.findMany({
|
||||
select: { id: true, name: true, value: true },
|
||||
orderBy: { name: 'asc' }
|
||||
}),
|
||||
prisma.proclamationCategory.findMany({
|
||||
select: { id: true, name: true },
|
||||
orderBy: { name: 'asc' }
|
||||
}),
|
||||
prisma.proclamationGroup.findMany({
|
||||
select: { id: true, name: true },
|
||||
orderBy: { sortOrder: 'asc' }
|
||||
})
|
||||
]);
|
||||
|
||||
const records = proclamations as unknown as Record<string, unknown>[];
|
||||
|
||||
// Strip content from non-Super users
|
||||
if (!isSuper(locals)) {
|
||||
for (const record of base.records) {
|
||||
for (const record of records) {
|
||||
delete record.content;
|
||||
}
|
||||
}
|
||||
|
||||
const categories = await prisma.proclamationCategory.findMany({
|
||||
select: { id: true, name: true, parentId: true },
|
||||
orderBy: { name: 'asc' }
|
||||
});
|
||||
|
||||
return { ...base, categories };
|
||||
return { records, frequencies, categories, groups };
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
upsert: async ({ request, locals }) => {
|
||||
if (!locals.user) {
|
||||
return { success: false, error: 'Unauthorized' };
|
||||
return { success: false, action: 'upsert', error: 'Unauthorized' };
|
||||
}
|
||||
|
||||
if (!isSuper(locals)) {
|
||||
return { success: false, error: 'Only Super admins can edit proclamations' };
|
||||
return {
|
||||
success: false,
|
||||
action: 'upsert',
|
||||
error: 'Only Super admins can edit proclamations'
|
||||
};
|
||||
}
|
||||
|
||||
const formData = await request.formData();
|
||||
const form = await superValidate(formData, zod4(Schemas.Proclamation));
|
||||
const form = await superValidate(formData, zod4(ProclamationSchema));
|
||||
if (!form.valid) {
|
||||
const { error, fieldErrors } = superFormErrorsToFieldErrors(
|
||||
const { error: formError, fieldErrors } = superFormErrorsToFieldErrors(
|
||||
form.errors as Record<string, unknown>
|
||||
);
|
||||
return { success: false, error, fieldErrors };
|
||||
return { success: false, action: 'upsert', error: formError, fieldErrors };
|
||||
}
|
||||
|
||||
const rawId = formData.get('id')?.toString();
|
||||
const { parentCategoryId, ...rest } = form.data;
|
||||
|
||||
const data: Record<string, unknown> = {
|
||||
...rest,
|
||||
categoryId: rest.categoryId ?? parentCategoryId
|
||||
};
|
||||
const data: Record<string, unknown> = { ...form.data };
|
||||
|
||||
try {
|
||||
if (rawId) {
|
||||
const existing = await prisma.proclamation.findUnique({
|
||||
where: { id: rawId }
|
||||
});
|
||||
const existing = await prisma.proclamation.findUnique({ where: { id: rawId } });
|
||||
if (!existing) {
|
||||
return { success: false, error: 'Record not found' };
|
||||
return { success: false, action: 'upsert', error: 'Record not found' };
|
||||
}
|
||||
await prisma.proclamation.update({
|
||||
where: { id: rawId },
|
||||
data: data as any
|
||||
});
|
||||
await prisma.proclamation.update({ where: { id: rawId }, data: data as any });
|
||||
} else {
|
||||
await prisma.proclamation.create({ data: data as any });
|
||||
}
|
||||
@@ -80,18 +89,41 @@ export const actions = {
|
||||
if (e && typeof e === 'object' && 'code' in e && (e as { code: string }).code === 'P2002') {
|
||||
return {
|
||||
success: false,
|
||||
action: 'upsert',
|
||||
error: 'A record with this name already exists.'
|
||||
};
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
return { success: true, action: 'upsert' };
|
||||
},
|
||||
delete: async ({ request, locals }) => {
|
||||
if (!isSuper(locals)) {
|
||||
return { success: false, error: 'Only Super admins can delete proclamations' };
|
||||
return {
|
||||
success: false,
|
||||
action: 'delete',
|
||||
error: 'Only Super admins can delete proclamations'
|
||||
};
|
||||
}
|
||||
return deleteRecord(request, config, locals);
|
||||
|
||||
const formData = await request.formData();
|
||||
const rawId = formData.get('id')?.toString();
|
||||
if (!rawId) return { success: false, action: 'delete', error: 'Missing id' };
|
||||
|
||||
try {
|
||||
await prisma.proclamation.delete({ where: { id: rawId } });
|
||||
} catch (e: unknown) {
|
||||
if (e && typeof e === 'object' && 'code' in e && (e as { code: string }).code === 'P2003') {
|
||||
return {
|
||||
success: false,
|
||||
action: 'delete',
|
||||
error: 'Cannot delete: this proclamation is assigned to users.'
|
||||
};
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
return { success: true, action: 'delete' };
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user