Remove stale admin overhaul and API update docs
This commit is contained in:
@@ -1,185 +0,0 @@
|
|||||||
# Admin Overhaul & Bug Fix Plan
|
|
||||||
|
|
||||||
Status: **IN PROGRESS** — checkboxes are ticked as work is completed and committed.
|
|
||||||
|
|
||||||
## Context
|
|
||||||
|
|
||||||
The admin section is mid-refactor. The generic `$lib/svelte-admin/*` CRUD layer and
|
|
||||||
`AdminTable.svelte` were deleted; the **Profiles** page was rebuilt by hand with
|
|
||||||
`DataTable.svelte`, `AdminModal.svelte`, and route-specific form component(s). The
|
|
||||||
remaining admin pages still import the deleted modules, so the app does not compile.
|
|
||||||
|
|
||||||
This plan:
|
|
||||||
|
|
||||||
1. Fixes the reported Profiles-page bugs (stale buttons, unified User/Profile identity,
|
|
||||||
in-modal status messages, centered modal with backdrop).
|
|
||||||
2. Rebuilds every other admin page to match the Profiles page look, style, and
|
|
||||||
functionality — KISS, no clever abstractions.
|
|
||||||
3. Removes the **Admin dashboard** — the nav links are the navigation.
|
|
||||||
4. Uses `DataTable` / `AdminModal` / `Toast` as dumb containers; each page owns its
|
|
||||||
own form logic.
|
|
||||||
|
|
||||||
Roles: `SUPER = 1`, `ADMIN = 2`, `STAFF = 3`, `USER = 4`. Admin area requires `roleId <= 3`.
|
|
||||||
|
|
||||||
## Decisions Locked In
|
|
||||||
|
|
||||||
- **Dashboard**: removed. `/admin` redirects to `/admin/profiles`.
|
|
||||||
- **Profile email is the identity.** The User dropdown is removed from the Profiles
|
|
||||||
form. One Email field:
|
|
||||||
- Linked profile (`userId` set): field shows/edits the User's email; saving
|
|
||||||
syncs the change to the `User` record.
|
|
||||||
- Unlinked profile: email lives on the Profile only.
|
|
||||||
- On save, if an existing `User` matches the entered email, the profile is linked
|
|
||||||
to it automatically.
|
|
||||||
- **Users page** is where Users are edited (Name, Email, Role) — not the Profiles page.
|
|
||||||
- **Status messages live inside modals**, at the top, not in a page-level toast.
|
|
||||||
- **API Keys**: pending operator review. Applied: compile fix (remove `$lib/svelte-admin/roles`
|
|
||||||
dependency) + consistent styling. Modal-based rotation flow is deferred (see appendix).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Checklist
|
|
||||||
|
|
||||||
### Phase 0 — Documentation & scaffolding
|
|
||||||
|
|
||||||
- [x] Document this plan at `docs/ADMIN_OVERHAUL.md` (this file)
|
|
||||||
- [x] Remove Admin dashboard: `/admin` redirects to `/admin/profiles`
|
|
||||||
- [x] Delete `(protected)/admin/+page.svelte`
|
|
||||||
- [x] Rewrite `(protected)/admin/+page.server.ts` as a `redirect(307, '/admin/profiles')`
|
|
||||||
|
|
||||||
### Phase 1 — Shared components
|
|
||||||
|
|
||||||
- [x] Rewrite `src/lib/components/admin/AdminModal.svelte`
|
|
||||||
- [x] Fixed overlay, centered panel (`fixed inset-0 z-50`)
|
|
||||||
- [x] Dark translucent backdrop (`bg-slate-900/50`), click/`Esc` to close
|
|
||||||
- [x] Panel: `max-w-2xl`, `max-h-[90vh]`, scrollable body
|
|
||||||
- [x] Header: title, Delete (when `canDelete && recordId`), close `×`
|
|
||||||
- [x] Extend `src/lib/components/DataTable.svelte`
|
|
||||||
- [x] Optional `copyableFields` → copy button per matching cell (`stopPropagation`)
|
|
||||||
- [x] Optional `draggable` + `onReorder` → drag-handle reorder column
|
|
||||||
- [x] Restyle `src/lib/components/admin/Toast.svelte` (fixed, dark chip, countdown/dismiss)
|
|
||||||
|
|
||||||
### Phase 2 — Profiles page (reference implementation)
|
|
||||||
|
|
||||||
- [x] `+page.server.ts`
|
|
||||||
- [x] Remove user-options relation data / `updateUser` action
|
|
||||||
- [x] New schema: `email` (required), `firstName`, `lastName`, `phoneNumber`
|
|
||||||
- [x] Upsert: create links to matching `User` by email; update syncs linked `User.email`;
|
|
||||||
guard `P2002` with friendly message
|
|
||||||
- [x] `ProfileFields.svelte`
|
|
||||||
- [x] Single Email field (bound from `user.email ?? record.email`); remove User select
|
|
||||||
- [x] `CreateProfileModal.svelte`
|
|
||||||
- [x] Fix skip-first `lastForm` effect bug
|
|
||||||
- [x] In-modal success banner ("Profile created") then auto-close
|
|
||||||
- [x] In-modal error banner + field errors
|
|
||||||
- [x] `EditProfileModal.svelte`
|
|
||||||
- [x] Fix skip-first `lastForm` effect bug
|
|
||||||
- [x] Reset sub-state per record (addresses, toggles, errors) on record change
|
|
||||||
- [x] Remove the "Edit User" / "Save User" section
|
|
||||||
- [x] In-modal success/error banners; keep open on successful edit
|
|
||||||
- [x] Clear & close Add-Address form on success; append returned address live
|
|
||||||
- [x] `+page.svelte`
|
|
||||||
- [x] Email column shows `user.email ?? email`; remove `users` prop plumbing
|
|
||||||
- [x] `{#key editRecord?.id}` so the edit modal remounts per record
|
|
||||||
- [x] Remove page-level `Toast` for modal feedback
|
|
||||||
- [x] New spec: `profiles/+page.server.spec.ts`
|
|
||||||
- [x] Create links by matched email; unlinked stores email only
|
|
||||||
- [x] Update syncs linked `User.email`; rejects email owned by another user
|
|
||||||
- [x] Validation failures and `P2002` handling
|
|
||||||
|
|
||||||
### Phase 3 — Users page
|
|
||||||
|
|
||||||
- [x] `+page.server.ts` — load users + role options (role-filtered for non-super);
|
|
||||||
`upsert` name/email/roleId; `delete`; `P2002` guard
|
|
||||||
- [x] `+page.svelte` — `DataTable` (Name, Email, Role, Created) + edit `AdminModal`
|
|
||||||
(Name, Email, Role select, Delete)
|
|
||||||
- [x] Replace `UserForm.svelte` usage (or convert to inline form) — no `svelte-admin`
|
|
||||||
|
|
||||||
### Phase 4 — User Roles page
|
|
||||||
|
|
||||||
- [x] `+page.server.ts` — direct `prisma.userRole` load/upsert/delete (P2003 guard)
|
|
||||||
- [x] `+page.svelte` — `DataTable` (Id, Name) + modal
|
|
||||||
|
|
||||||
### Phase 5 — Solfeggio Frequencies page
|
|
||||||
|
|
||||||
- [x] `+page.server.ts` — direct `prisma.solfeggioFrequency` load/upsert/delete (P2003 guard)
|
|
||||||
- [x] `+page.svelte` — `DataTable` (Name, Value) + modal
|
|
||||||
|
|
||||||
### Phase 6 — Proclamation Categories page
|
|
||||||
|
|
||||||
- [x] `+page.server.ts` — direct `prisma.proclamationCategory` load/upsert/delete/reorder
|
|
||||||
(P2002/P2003 guards, maxSort for new rows)
|
|
||||||
- [x] `+page.svelte` — draggable `DataTable` + modal (Name), reorder via fetch + invalidateAll
|
|
||||||
|
|
||||||
### Phase 7 — Proclamation Groups page
|
|
||||||
|
|
||||||
- [x] `+page.server.ts` — direct `prisma.proclamationGroup` load/upsert/delete/reorder
|
|
||||||
(P2002/P2003 guards)
|
|
||||||
- [x] `+page.svelte` — draggable `DataTable` (Name, Sort) + modal
|
|
||||||
|
|
||||||
### Phase 8 — License Keys page
|
|
||||||
|
|
||||||
- [x] `+page.server.ts` — direct `prisma.licenseKey` load (with user) + `createKey` + `replace`
|
|
||||||
- [x] `+page.svelte` — `DataTable` with copyable Key column; New-by-email modal; row → Replace modal
|
|
||||||
- [x] In-modal success/error banners for both flows
|
|
||||||
|
|
||||||
### Phase 9 — Proclamations page
|
|
||||||
|
|
||||||
- [x] `+page.server.ts` — direct load (strip content for non-super) + upsert (Super only,
|
|
||||||
P2002) + delete (Super only, P2003); category/group/frequency option data
|
|
||||||
- [x] `+page.svelte` — `DataTable` (Name, Category, Group, Frequency) + modal with
|
|
||||||
TipTap content editor for Super users
|
|
||||||
- [x] Update `page.server.spec.ts` and `page.svelte.spec.ts` (drop `svelte-admin` mocks)
|
|
||||||
|
|
||||||
### Phase 10 — API Keys page (operator page)
|
|
||||||
|
|
||||||
- [x] `+page.server.ts` — remove `ROLES` import (`$lib/svelte-admin/roles` deleted); use literal
|
|
||||||
- [x] `+page.svelte` — consistent styling; keep single-page rotate flow (modal conversion deferred)
|
|
||||||
- [x] Document deferred modal conversion in appendix
|
|
||||||
|
|
||||||
### Phase 11 — Tests & validation
|
|
||||||
|
|
||||||
- [x] Update `license-keys/page.server.spec.ts` (drop `svelte-admin` mocks)
|
|
||||||
- [x] `npm run check`
|
|
||||||
- [x] `npm run lint` (pre-existing `no-explicit-any` spec debt remains — see Appendix C)
|
|
||||||
- [x] `npm run test`
|
|
||||||
- [x] `npm run build` (needs env vars exported during `vite build`)
|
|
||||||
- [x] Final commit; mark every checkbox complete
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Appendix A — Profiles bug root causes
|
|
||||||
|
|
||||||
1. **Skip-first `lastForm` bug.** Both modals guard against the initial `null` form
|
|
||||||
with `if (lastForm === null) { lastForm = form; return; }`, which also swallows the
|
|
||||||
_first real submit result_ — the first "Add Address" save creates the DB row but the
|
|
||||||
UI never reacts (no close, no toast, no appended row). Fix: `if (!form || form === lastForm) return;`.
|
|
||||||
2. **Stale modal state across records.** `{#if editRecord}` keeps the modal mounted
|
|
||||||
between different rows, while the `seeded` flag never resets — the previous row's
|
|
||||||
addresses/user data leak in, and `record` stays stale (the reported
|
|
||||||
"my name didn't update"). Fix: `{#key editRecord?.id}` remount + per-record reseed.
|
|
||||||
3. **User data is only a snapshot.** The modal rendered `record.user.name / email`
|
|
||||||
directly. With the unified email model this display goes away; the form fields are
|
|
||||||
controlled by local `$state` and the server keeps Profile + User in sync.
|
|
||||||
|
|
||||||
## Appendix B — API Keys decision note
|
|
||||||
|
|
||||||
The API Keys page is an operator page (key rotation), not a CRUD table. It does not use
|
|
||||||
`DataTable`. Recommended future change: present the rotation confirmation and the new
|
|
||||||
key in shared `AdminModal` panels with in-modal status. That conversion is **deferred**
|
|
||||||
pending review. What was done now: compile fix (replaced the deleted `ROLES` import)
|
|
||||||
and consistent styling (simple slate card layout, indigo action buttons, red emergency
|
|
||||||
button) so the page compiles and matches the site visually.
|
|
||||||
|
|
||||||
## Appendix C — Validation caveats
|
|
||||||
|
|
||||||
1. **`npm run check`** required renaming `src/routes/(protected)/admin/profiles/+page.server.spec.ts`
|
|
||||||
to `page.server.spec.ts`: SvelteKit reserves files whose names start with `+`. The `+`-prefix
|
|
||||||
broke `svelte-kit sync` and thus the whole `check`/`build` pipeline. Renamed.
|
|
||||||
2. **`npm run lint`** previously reported 142 `no-explicit-any` errors across 14 pre-existing spec
|
|
||||||
files (user/account, dashboard, license-keys, login/register/reset, etc.). A scoped override now
|
|
||||||
disables that rule for `**/*.spec.ts`, since test doubles commonly need `any`. All non-spec lint
|
|
||||||
errors were fixed; `npm run lint` is green.
|
|
||||||
3. **`npm run build`** needs `BETTER_AUTH_SECRET`/`BETTER_AUTH_URL`/`DATABASE_URL` exported in the
|
|
||||||
shell, because `src/lib/auth.ts` reads `$env/dynamic/private` (empty at `vite build` time) and
|
|
||||||
Better Auth rejects the default secret. Export the vars before building.
|
|
||||||
@@ -1,330 +0,0 @@
|
|||||||
# API Updates — Category Flattening, Groups, Subscription Level Removal
|
|
||||||
|
|
||||||
**Release:** v2 schema change (migration `20260821000000_flat_categories_groups_drop_subscription_levels`)
|
|
||||||
**Status:** Breaking changes. Clients must be updated.
|
|
||||||
|
|
||||||
## Summary of changes
|
|
||||||
|
|
||||||
| Endpoint | Change |
|
|
||||||
| ----------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
||||||
| `GET /api/v2/categories` | **Breaking**: `parentId` removed from every record. Categories are now a flat list. |
|
|
||||||
| `GET /api/v2/groups` | **New**: returns the group list (former sub-categories). |
|
|
||||||
| `GET /api/v2/proclamations` | **Breaking**: `groupId` added; `subscriptionLevelId` removed. |
|
|
||||||
| `GET /api/v2/subscription-levels` | **Removed**: subscription levels no longer exist. |
|
|
||||||
| `POST /api/v2/license` | **Breaking**: response no longer contains `subscription_level`. |
|
|
||||||
| `POST /api/v2/automations/create/account` | **Breaking**: `subscription_level` is no longer accepted or required in the payload. |
|
|
||||||
| `POST /api/v1/license` | **Breaking**: response no longer contains `subscription_level`. |
|
|
||||||
| `POST /api/v1/automations/create/account` | **Breaking**: `subscription_level` is no longer accepted or required in the payload. |
|
|
||||||
| `GET /api/v1/categories` | Payload implicitly changed: rows no longer contain `parentId`. |
|
|
||||||
|
|
||||||
### Data model mapping
|
|
||||||
|
|
||||||
- Former **parent** categories (`Spiritual`, `Physical`, `Mental`, `Emotional`, `Financial`,
|
|
||||||
`Social`, `Biblical`, ...) became the flat **Category** list.
|
|
||||||
- Former **child** sub-categories (`General`, `Specific`, `Precise`, `Prayers`, `Scripture`)
|
|
||||||
became **Groups**.
|
|
||||||
- A proclamation's former child category becomes its `group`; its former parent category becomes
|
|
||||||
its `category`. A proclamation may move between categories without changing its group.
|
|
||||||
- Proclamations that had no sub-category (e.g. under `Custom (Client Specific)`) were assigned to
|
|
||||||
the `General` group.
|
|
||||||
- Every proclamation now has exactly one category and one group.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## OpenAPI 3.0 specification
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
openapi: 3.0.3
|
|
||||||
info:
|
|
||||||
title: Radiant Wave Portal API
|
|
||||||
version: 2.1.0
|
|
||||||
description: |
|
|
||||||
Changes in this release:
|
|
||||||
* ProclamationCategory hierarchy flattened; parentId removed everywhere.
|
|
||||||
* New ProclamationGroup resource replaces sub-categories as a movable filter.
|
|
||||||
* SubscriptionLevel resource and all level-based gating removed.
|
|
||||||
|
|
||||||
paths:
|
|
||||||
/api/v2/categories:
|
|
||||||
get:
|
|
||||||
summary: List categories (flat)
|
|
||||||
description: >
|
|
||||||
Returns all proclamation categories as a flat, ordered list.
|
|
||||||
BREAKING: the previous version returned a hierarchical list where each
|
|
||||||
record carried a nullable parentId linking children to parents. That
|
|
||||||
field no longer exists.
|
|
||||||
tags: [Categories]
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Flat category list.
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/Category'
|
|
||||||
examples:
|
|
||||||
newResponse:
|
|
||||||
summary: New response
|
|
||||||
value:
|
|
||||||
- id: 4
|
|
||||||
name: Spiritual
|
|
||||||
sortOrder: 0
|
|
||||||
- id: 2
|
|
||||||
name: Physical
|
|
||||||
sortOrder: 1
|
|
||||||
oldResponseDeprecated:
|
|
||||||
summary: Old (removed) response shape
|
|
||||||
value:
|
|
||||||
- id: 5
|
|
||||||
name: General
|
|
||||||
sortOrder: 0
|
|
||||||
parentId: 4
|
|
||||||
- id: 9
|
|
||||||
name: Specific
|
|
||||||
sortOrder: 1
|
|
||||||
parentId: 4
|
|
||||||
|
|
||||||
/api/v2/groups:
|
|
||||||
get:
|
|
||||||
summary: List groups
|
|
||||||
description: >
|
|
||||||
NEW ENDPOINT. Returns all proclamation groups ordered by sortOrder.
|
|
||||||
Groups replace the former sub-categories and act as a movable filter:
|
|
||||||
a proclamation can be moved from one category to another while keeping
|
|
||||||
its group.
|
|
||||||
tags: [Groups]
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Group list.
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/Group'
|
|
||||||
example:
|
|
||||||
- id: 1
|
|
||||||
name: General
|
|
||||||
sortOrder: 0
|
|
||||||
- id: 2
|
|
||||||
name: Specific
|
|
||||||
sortOrder: 1
|
|
||||||
|
|
||||||
/api/v2/proclamations:
|
|
||||||
get:
|
|
||||||
summary: List proclamations
|
|
||||||
description: >
|
|
||||||
Returns proclamations with their canonical frequency id and optional
|
|
||||||
color palette. BREAKING: each record now carries groupId (required,
|
|
||||||
always present) and no longer carries subscriptionLevelId. When the
|
|
||||||
optional entitlement headers are supplied, only the caller's entitled
|
|
||||||
proclamations are returned; there is no longer any per-level gating.
|
|
||||||
tags: [Proclamations]
|
|
||||||
parameters:
|
|
||||||
- in: header
|
|
||||||
name: x-license-key
|
|
||||||
required: false
|
|
||||||
schema: { type: string }
|
|
||||||
description: License key. Must be paired with x-email.
|
|
||||||
- in: header
|
|
||||||
name: x-email
|
|
||||||
required: false
|
|
||||||
schema: { type: string }
|
|
||||||
description: Account email. Must be paired with x-license-key.
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Proclamation list.
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/Proclamation'
|
|
||||||
examples:
|
|
||||||
newResponse:
|
|
||||||
summary: New response
|
|
||||||
value:
|
|
||||||
- id: b40fdcb9-703c-420e-af32-a081964c6f08
|
|
||||||
name: Peace & Relaxation
|
|
||||||
content: <p>The text content</p>
|
|
||||||
categoryId: 4
|
|
||||||
groupId: 1
|
|
||||||
solfeggioFrequencyId: 5
|
|
||||||
colorPalette:
|
|
||||||
[
|
|
||||||
'#32CD32',
|
|
||||||
'#3CB371',
|
|
||||||
'#90EE90',
|
|
||||||
'#98FB98',
|
|
||||||
'#ADFFA9',
|
|
||||||
'#C8FFD2',
|
|
||||||
'#F0FFF0'
|
|
||||||
]
|
|
||||||
oldResponseDeprecated:
|
|
||||||
summary: Old (removed) response shape
|
|
||||||
value:
|
|
||||||
- id: b40fdcb9-703c-420e-af32-a081964c6f08
|
|
||||||
name: Peace & Relaxation
|
|
||||||
content: <p>The text content</p>
|
|
||||||
categoryId: 5
|
|
||||||
solfeggioFrequencyId: 5
|
|
||||||
subscriptionLevelId: 1
|
|
||||||
'400':
|
|
||||||
description: Entitlement headers incomplete or invalid.
|
|
||||||
|
|
||||||
/api/v2/subscription-levels:
|
|
||||||
get:
|
|
||||||
summary: REMOVED
|
|
||||||
deprecated: true
|
|
||||||
description: >
|
|
||||||
This endpoint was removed. Subscription levels no longer exist in the
|
|
||||||
data model; clients must stop calling it.
|
|
||||||
tags: [Removed]
|
|
||||||
responses:
|
|
||||||
'404':
|
|
||||||
description: Route no longer exists.
|
|
||||||
|
|
||||||
/api/v2/license:
|
|
||||||
post:
|
|
||||||
summary: Validate a license key
|
|
||||||
description: >
|
|
||||||
Validates a key/email pair. BREAKING: the response no longer contains a
|
|
||||||
subscription_level object.
|
|
||||||
tags: [License]
|
|
||||||
requestBody:
|
|
||||||
required: true
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
required: [license_key, email_address]
|
|
||||||
properties:
|
|
||||||
license_key: { type: string, example: ABCD-EFGH-IJKL-MNOP }
|
|
||||||
email_address: { type: string, format: email }
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Key is valid.
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/LicenseResponse'
|
|
||||||
examples:
|
|
||||||
newResponse:
|
|
||||||
summary: New response
|
|
||||||
value:
|
|
||||||
key: ABCD-EFGH-IJKL-MNOP
|
|
||||||
email_address: user@example.com
|
|
||||||
oldResponseDeprecated:
|
|
||||||
summary: Old (removed) response shape
|
|
||||||
value:
|
|
||||||
key: ABCD-EFGH-IJKL-MNOP
|
|
||||||
email_address: user@example.com
|
|
||||||
subscription_level:
|
|
||||||
id: 2
|
|
||||||
name: Advanced
|
|
||||||
description: General + Specific
|
|
||||||
'400':
|
|
||||||
description: Validation failure (missing fields, unknown key, wrong owner).
|
|
||||||
|
|
||||||
/api/v2/automations/create/account:
|
|
||||||
post:
|
|
||||||
summary: Create account + license key (GoHighLevel automation)
|
|
||||||
description: >
|
|
||||||
Creates a portal account when needed and issues a license key.
|
|
||||||
BREAKING: subscription_level was removed from the request contract. The
|
|
||||||
field is neither required nor validated anymore; sending it is ignored.
|
|
||||||
Signature headers (x-timestamp, x-nonce, x-signature) are unchanged.
|
|
||||||
tags: [Automations]
|
|
||||||
requestBody:
|
|
||||||
required: true
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
required: [email]
|
|
||||||
properties:
|
|
||||||
email: { type: string, format: email }
|
|
||||||
name: { type: string }
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Account and license key created.
|
|
||||||
'400':
|
|
||||||
description: Missing/invalid email.
|
|
||||||
'401':
|
|
||||||
description: Invalid signature.
|
|
||||||
|
|
||||||
components:
|
|
||||||
schemas:
|
|
||||||
Category:
|
|
||||||
type: object
|
|
||||||
required: [id, name, sortOrder]
|
|
||||||
properties:
|
|
||||||
id: { type: integer }
|
|
||||||
name: { type: string }
|
|
||||||
sortOrder: { type: integer }
|
|
||||||
|
|
||||||
Group:
|
|
||||||
type: object
|
|
||||||
required: [id, name, sortOrder]
|
|
||||||
properties:
|
|
||||||
id: { type: integer }
|
|
||||||
name: { type: string }
|
|
||||||
sortOrder: { type: integer }
|
|
||||||
|
|
||||||
Proclamation:
|
|
||||||
type: object
|
|
||||||
required: [id, name, content, categoryId, groupId, solfeggioFrequencyId]
|
|
||||||
properties:
|
|
||||||
id:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
name: { type: string }
|
|
||||||
content: { type: string, description: HTML formatted content. }
|
|
||||||
categoryId:
|
|
||||||
type: integer
|
|
||||||
description: Flat category this proclamation belongs to.
|
|
||||||
groupId:
|
|
||||||
type: integer
|
|
||||||
description: >
|
|
||||||
Group membership (movable filter). Independent of categoryId;
|
|
||||||
changing the category does not change the group.
|
|
||||||
solfeggioFrequencyId:
|
|
||||||
type: integer
|
|
||||||
minimum: 1
|
|
||||||
maximum: 9
|
|
||||||
description: Canonical kiosk frequency id (not the DB autoincrement id).
|
|
||||||
colorPalette:
|
|
||||||
type: array
|
|
||||||
items: { type: string }
|
|
||||||
minItems: 7
|
|
||||||
maxItems: 7
|
|
||||||
description: Present only when a palette exists for the frequency.
|
|
||||||
|
|
||||||
LicenseResponse:
|
|
||||||
type: object
|
|
||||||
required: [key, email_address]
|
|
||||||
properties:
|
|
||||||
key: { type: string }
|
|
||||||
email_address: { type: string, format: email }
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Client migration checklist
|
|
||||||
|
|
||||||
1. **Kiosk / API consumers**
|
|
||||||
- Stop reading `parentId`; treat `/api/v2/categories` as flat.
|
|
||||||
- Fetch `/api/v2/groups` once at startup for the filter list.
|
|
||||||
- Read `groupId` from each proclamation record for filtering.
|
|
||||||
- Remove all references to `subscriptionLevelId` and `/api/v2/subscription-levels`.
|
|
||||||
- Remove `subscription_level` handling from license validation responses.
|
|
||||||
|
|
||||||
2. **GoHighLevel automation**
|
|
||||||
- Remove `subscription_level` from the account-creation webhook payload
|
|
||||||
(optional but recommended; the field is now ignored).
|
|
||||||
|
|
||||||
3. **Admin portal**
|
|
||||||
- Manage categories under Admin → Proclamation Categories (flat list, drag to reorder).
|
|
||||||
- Manage groups under Admin → Proclamation Groups (drag to reorder).
|
|
||||||
- Proclamations now select one Category and one Group independently.
|
|
||||||
Reference in New Issue
Block a user