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:
NeilClack
2026-09-10 09:38:01 -05:00
parent f4e736c92d
commit 91d162c746
127 changed files with 7182 additions and 9785 deletions
@@ -1,27 +1,19 @@
import type { PageServerLoad } from './$types';
import { getModelConfig } from '$lib/svelte-admin/registry';
import { loadRecords } from '$lib/svelte-admin/crud';
import { prisma } from '$lib/db';
import { sendMail } from '$lib/server/resend';
import { error } from '@sveltejs/kit';
const config = getModelConfig('license-keys')!;
function generateLicenseKey(): string {
const hex = crypto.randomUUID().replace(/-/g, '').slice(0, 16).toUpperCase();
return `${hex.slice(0, 4)}-${hex.slice(4, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}`;
}
export const load: PageServerLoad = async ({ locals }) => {
const records = await loadRecords(locals, config);
const subscriptionLevels = await prisma.subscriptionLevel.findMany({
select: { id: true, name: true },
orderBy: { name: 'asc' }
export const load: PageServerLoad = async () => {
const records = await prisma.licenseKey.findMany({
include: { user: true },
orderBy: { key: 'asc' }
});
return {
...records,
subscriptionLevels: subscriptionLevels.map((s: { id: number; name: string }) => ({
label: s.name,
value: s.name
}))
};
return { records: records as unknown as Record<string, unknown>[] };
};
export const actions = {
@@ -29,41 +21,35 @@ export const actions = {
if (!locals.user) throw error(401, 'Unauthorized');
const formData = await request.formData();
const email = formData.get('email')?.toString();
const level = formData.get('level')?.toString();
const email = formData.get('email')?.toString()?.trim().toLowerCase();
if (!email) {
return { success: false, error: 'Email is required' };
}
if (!level) {
return { success: false, error: 'Subscription level is required' };
return { success: false, action: 'createKey', error: 'Email is required' };
}
const user = await prisma.user.findUnique({ where: { email } });
if (!user) {
return { success: false, error: 'No user found with that email' };
}
const hex = crypto.randomUUID().replace(/-/g, '').slice(0, 16).toUpperCase();
const key = `${hex.slice(0, 4)}-${hex.slice(4, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}`;
const key = generateLicenseKey();
await prisma.licenseKey.create({
data: {
key,
active: true,
level,
userId: user.id
userId: user?.id,
email: user ? undefined : email
}
});
try {
const profile = await prisma.profile.findUnique({ where: { userId: user.id } });
const profile = await prisma.profile.findFirst({
where: user ? { userId: user.id } : { email }
});
await sendMail({
to: email,
subject: 'Your Rhemawave License Key',
subject: 'Your Radiant Wave License Key',
templateId: 'license-key-delivery',
data: {
firstName: profile?.firstName ?? user.name ?? 'Valued Customer',
firstName: profile?.firstName ?? user?.name ?? 'Valued Customer',
license_key: key
}
});
@@ -71,32 +57,29 @@ export const actions = {
console.error('Failed to send license key email:', e);
}
return { success: true, error: undefined };
return { success: true, action: 'createKey' };
},
replace: async ({ request, locals }) => {
if (!locals.user) throw error(401, 'Unauthorized');
const formData = await request.formData();
const keyId = formData.get('id')?.toString();
if (!keyId) return { success: false, error: 'Missing key id' };
if (!keyId) return { success: false, action: 'replace', error: 'Missing key id' };
const existing = await prisma.licenseKey.findUnique({ where: { id: keyId } });
if (!existing) return { success: false, error: 'License key not found' };
if (!existing) return { success: false, action: 'replace', error: 'License key not found' };
await prisma.licenseKey.delete({ where: { id: keyId } });
const hex = crypto.randomUUID().replace(/-/g, '').slice(0, 16).toUpperCase();
const key = `${hex.slice(0, 4)}-${hex.slice(4, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}`;
await prisma.licenseKey.create({
data: {
key,
key: generateLicenseKey(),
active: true,
level: existing.level,
userId: existing.userId
userId: existing.userId,
email: existing.email
}
});
return { success: true, error: undefined };
return { success: true, action: 'replace' };
}
};