Refactor admin to shared generic CRUD; restructure proclamation categories/groups and APIs

- 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
This commit is contained in:
NeilClack
2026-08-27 12:03:40 -05:00
parent b618d8cb93
commit 1d11de8cec
50 changed files with 1168 additions and 2000 deletions
+27 -19
View File
@@ -17,12 +17,18 @@ export const User = z.object({
roleId: z.coerce.number().int().min(1)
});
export const Profile = z.object({
userId: z.string().min(1, 'Required'),
firstName: name,
lastName: name,
phoneNumber: phone
});
export const Profile = z
.object({
userId: z.string().optional(),
email: z.string().email('Invalid email address').optional(),
firstName: name,
lastName: name,
phoneNumber: phone
})
.refine((p) => p.userId || p.email, {
message: 'Provide either a user or an email',
path: ['email']
});
export const Address = z.object({
profileId: z.string().min(1),
@@ -42,30 +48,32 @@ export const SolfeggioFrequency = z.object({
value: z.coerce.number().int('Must be a whole number')
});
export const SubscriptionLevel = z.object({
name: z.string().min(1, 'Required'),
description: z.string().optional().default('')
export const ProclamationGroup = z.object({
name: z.string().min(1, 'Required')
});
export const ProclamationCategory = z.object({
name: z.string().min(1, 'Required'),
parentId: z.coerce.number().int().optional()
name: z.string().min(1, 'Required')
});
export const Proclamation = z.object({
name: z.string().min(1, 'Required'),
solfeggioFrequencyId: z.coerce.number().int(),
parentCategoryId: z.coerce.number().int(),
categoryId: z.coerce.number().int().optional(),
subscriptionLevelId: z.coerce.number().int(),
categoryId: z.coerce.number().int(),
groupId: z.coerce.number().int(),
content: z.string().optional().default('')
});
export const LicenseKey = z.object({
active: z.coerce.boolean(),
level: z.string().min(1, 'Required'),
userId: z.string().min(1, 'Required')
});
export const LicenseKey = z
.object({
active: z.coerce.boolean(),
userId: z.string().optional(),
email: z.string().email('Invalid email address').optional()
})
.refine((k) => k.userId || k.email, {
message: 'Provide either a user or an email',
path: ['email']
});
export const UserUpdate = z.object({
id: z.string().min(1),