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.
141 lines
4.2 KiB
TypeScript
141 lines
4.2 KiB
TypeScript
// Canonical solfeggio id <- Hz, mirroring the kiosk's `frequencyIdForHz()`
|
|
// map (radiantwave-rebuild src/audio/audio_downloader.cpp). v2 must emit these
|
|
// canonical ids (1-9), never the portal DB autoincrement ids, because
|
|
// proclamations and color palettes on the kiosk are keyed by them.
|
|
export const CANONICAL_ID_FOR_HZ: Record<number, number> = {
|
|
174: 1,
|
|
285: 1,
|
|
396: 2,
|
|
417: 3,
|
|
432: 4,
|
|
528: 5,
|
|
639: 6,
|
|
741: 7,
|
|
852: 8,
|
|
963: 9
|
|
};
|
|
|
|
// The 9 canonical frequencies exactly as the kiosk expects from
|
|
// GET /frequencies (id 1-9, non-zero, `value` recognized by frequencyIdForHz).
|
|
export const CANONICAL_FREQUENCIES: { id: number; name: string; value: number }[] = [
|
|
{ id: 1, name: 'Grounding / Relief', value: 174 },
|
|
{ id: 2, name: 'Root', value: 396 },
|
|
{ id: 3, name: 'Sacral', value: 417 },
|
|
{ id: 4, name: 'Clarity / Wellbeing', value: 432 },
|
|
{ id: 5, name: 'Solar Plexus', value: 528 },
|
|
{ id: 6, name: 'Heart', value: 639 },
|
|
{ id: 7, name: 'Throat', value: 741 },
|
|
{ id: 8, name: '3rd Eye', value: 852 },
|
|
{ id: 9, name: 'Crown', value: 963 }
|
|
];
|
|
|
|
// Seed palettes per canonical id, matching the kiosk's
|
|
// DatabaseManager::seedColorPalettes(). Used to backfill /color-palettes
|
|
// when the DB has not been seeded for an id yet.
|
|
export const CANONICAL_PALETTES: Record<number, string[]> = {
|
|
1: ['#8B6914', '#B8860B', '#DAA520', '#E8B64C', '#FFD700', '#FFE9A8', '#FFF4CC'],
|
|
2: ['#8B0000', '#B22222', '#DC143C', '#FF6347', '#FF8C69', '#FFA500', '#FFDAB9'],
|
|
3: ['#FF4500', '#FF7F50', '#FF8C00', '#FFA500', '#FFC87F', '#FFDFBA', '#FFEBCD'],
|
|
4: ['#8B2252', '#CD3278', '#DB7093', '#FF69B4', '#FFB6C1', '#FFC0CB', '#FFE4E1'],
|
|
5: ['#32CD32', '#3CB371', '#90EE90', '#98FB98', '#ADFFA9', '#C8FFD2', '#F0FFF0'],
|
|
6: ['#00FF7F', '#00CED1', '#40E0D0', '#5F9EA0', '#7FFFD4', '#AFEEEE', '#E0FFFF'],
|
|
7: ['#00BFFF', '#1E90FF', '#4169E1', '#6495ED', '#87CEEB', '#ADD8E6', '#F0F8FF'],
|
|
8: ['#4B0082', '#9370DB', '#8A2BE2', '#7B68EE', '#DA70D6', '#D8BFD8', '#E6E6FA'],
|
|
9: ['#9400D3', '#BA55D3', '#C71585', '#DDA0DD', '#EE82EE', '#FFF0F5', '#FAF0FA']
|
|
};
|
|
|
|
/** Map a SolfeggioFrequency row to its canonical id, or null if it has no
|
|
* canonical id (e.g. 936 Hz). */
|
|
export function canonicalIdForFrequency(freq: { value: number }): number | null {
|
|
return CANONICAL_ID_FOR_HZ[freq.value] ?? null;
|
|
}
|
|
|
|
export type V2Proclamation = {
|
|
id: string;
|
|
name: string;
|
|
content: string;
|
|
categoryId: number;
|
|
groupId: number;
|
|
solfeggioFrequencyId: number;
|
|
colorPalette?: string[];
|
|
};
|
|
|
|
export type ProclamationWithFrequency = {
|
|
id: string;
|
|
name: string;
|
|
content: string;
|
|
categoryId: number;
|
|
groupId: number;
|
|
solfeggioFrequency: { value: number };
|
|
};
|
|
|
|
/** Serialize a DB proclamation row to the v2 wire format. Returns null when
|
|
* the proclamation's frequency has no canonical id (dropped from v2). */
|
|
export function serializeProclamation(
|
|
p: ProclamationWithFrequency,
|
|
palette?: string[]
|
|
): V2Proclamation | null {
|
|
const solfeggioFrequencyId = CANONICAL_ID_FOR_HZ[p.solfeggioFrequency.value];
|
|
if (!solfeggioFrequencyId) return null;
|
|
|
|
const record: V2Proclamation = {
|
|
id: p.id,
|
|
name: p.name,
|
|
content: p.content,
|
|
categoryId: p.categoryId,
|
|
groupId: p.groupId,
|
|
solfeggioFrequencyId
|
|
};
|
|
|
|
if (palette && palette.length > 0) {
|
|
record.colorPalette = palette;
|
|
}
|
|
|
|
return record;
|
|
}
|
|
|
|
export type V2Category = {
|
|
id: number;
|
|
name: string;
|
|
sortOrder: number;
|
|
};
|
|
|
|
export function serializeCategory(c: { id: number; name: string; sortOrder: number }): V2Category {
|
|
return { id: c.id, name: c.name, sortOrder: c.sortOrder };
|
|
}
|
|
|
|
export type V2Group = {
|
|
id: number;
|
|
name: string;
|
|
sortOrder: number;
|
|
};
|
|
|
|
export function serializeGroup(g: { id: number; name: string; sortOrder: number }): V2Group {
|
|
return { id: g.id, name: g.name, sortOrder: g.sortOrder };
|
|
}
|
|
|
|
export type V2License = {
|
|
key: string;
|
|
email_address: string;
|
|
};
|
|
|
|
/** Build the POST /license response. */
|
|
export function serializeLicense(opts: { key: string; emailAddress: string }): V2License {
|
|
return {
|
|
key: opts.key,
|
|
email_address: opts.emailAddress
|
|
};
|
|
}
|
|
|
|
export type V2ColorPalette = {
|
|
solfeggioFrequencyId: number;
|
|
colors: string[];
|
|
};
|
|
|
|
export function serializeColorPalette(palette: {
|
|
solfeggioFrequencyId: number;
|
|
colors: string[];
|
|
}): V2ColorPalette {
|
|
return { solfeggioFrequencyId: palette.solfeggioFrequencyId, colors: palette.colors };
|
|
}
|