From cc51f405911c6c772b39c6d80272d9678aaea30c Mon Sep 17 00:00:00 2001 From: NeilClack Date: Wed, 25 Feb 2026 13:19:26 -0600 Subject: [PATCH] Build user pages --- CLAUDE.md | 9 +- .../migration.sql | 60 + prisma/schema.prisma | 36 +- .../forms/affirmation/AffirmationForm.svelte | 260 --- .../forms/frequency/FrequencyForm.svelte | 62 - .../ui/AffirmationList/AffirmationList.svelte | 242 --- .../ui/FrequencyList/FrequencyList.svelte | 176 -- src/lib/components/ui/topBar/TopBar.svelte | 9 +- src/lib/db.ts | 2 +- src/lib/generated/prisma/browser.ts | 8 +- src/lib/generated/prisma/client.ts | 8 +- src/lib/generated/prisma/internal/class.ts | 20 +- .../prisma/internal/prismaNamespace.ts | 154 +- .../prisma/internal/prismaNamespaceBrowser.ts | 12 +- src/lib/generated/prisma/models.ts | 4 +- .../generated/prisma/models/Affirmation.ts | 1565 ---------------- .../prisma/models/AffirmationCategory.ts | 1576 ----------------- .../generated/prisma/models/Proclamation.ts | 1565 ++++++++++++++++ .../prisma/models/ProclamationCategory.ts | 1576 +++++++++++++++++ .../prisma/models/SolfeggioFrequency.ts | 90 +- src/lib/svelte-admin/README.md | 460 +++-- src/lib/svelte-admin/registry.ts | 38 +- src/lib/svelte-admin/roles.ts | 2 +- src/lib/test-utils.ts | 82 + src/routes/(protected)/+layout.svelte | 56 +- .../(protected)/account/+page.server.ts | 88 +- src/routes/(protected)/account/+page.svelte | 223 ++- .../(protected)/account/page.server.spec.ts | 257 ++- .../(protected)/admin/layout.server.spec.ts | 104 ++ .../(protected)/affirmations/+page.server.ts | 97 - .../(protected)/affirmations/+page.svelte | 72 - .../affirmations/[id]/+page.server.ts | 101 -- .../affirmations/[id]/+page.svelte | 49 - .../(protected)/dashboard/+page.server.ts | 33 + src/routes/(protected)/dashboard/+page.svelte | 39 +- .../(protected)/dashboard/page.server.spec.ts | 88 + .../(protected)/frequencies/+page.server.ts | 47 - .../(protected)/frequencies/+page.svelte | 66 - .../frequencies/[id]/+page.server.ts | 50 - .../(protected)/frequencies/[id]/+page.svelte | 47 - .../(protected)/license-keys/+page.server.ts | 16 + .../(protected)/license-keys/+page.svelte | 54 + .../license-keys/page.server.spec.ts | 59 + .../(protected)/proclamations/+page.server.ts | 38 + .../(protected)/proclamations/+page.svelte | 108 ++ .../proclamations/[id]/+page.server.ts | 21 + .../proclamations/[id]/+page.svelte | 45 + .../proclamations/[id]/page.server.spec.ts | 66 + .../proclamations/page.server.spec.ts | 113 ++ src/routes/+layout.server.ts | 15 + src/routes/+layout.svelte | 8 +- src/routes/api/v1/affirmations/+server.ts | 15 - src/routes/api/v1/proclamations/+server.ts | 30 + 53 files changed, 5204 insertions(+), 4817 deletions(-) create mode 100644 prisma/migrations/20260225150600_rename_affirmation_to_proclamation/migration.sql delete mode 100644 src/lib/components/forms/affirmation/AffirmationForm.svelte delete mode 100644 src/lib/components/forms/frequency/FrequencyForm.svelte delete mode 100644 src/lib/components/ui/AffirmationList/AffirmationList.svelte delete mode 100644 src/lib/components/ui/FrequencyList/FrequencyList.svelte delete mode 100644 src/lib/generated/prisma/models/Affirmation.ts delete mode 100644 src/lib/generated/prisma/models/AffirmationCategory.ts create mode 100644 src/lib/generated/prisma/models/Proclamation.ts create mode 100644 src/lib/generated/prisma/models/ProclamationCategory.ts create mode 100644 src/routes/(protected)/admin/layout.server.spec.ts delete mode 100644 src/routes/(protected)/affirmations/+page.server.ts delete mode 100644 src/routes/(protected)/affirmations/+page.svelte delete mode 100644 src/routes/(protected)/affirmations/[id]/+page.server.ts delete mode 100644 src/routes/(protected)/affirmations/[id]/+page.svelte create mode 100644 src/routes/(protected)/dashboard/+page.server.ts create mode 100644 src/routes/(protected)/dashboard/page.server.spec.ts delete mode 100644 src/routes/(protected)/frequencies/+page.server.ts delete mode 100644 src/routes/(protected)/frequencies/+page.svelte delete mode 100644 src/routes/(protected)/frequencies/[id]/+page.server.ts delete mode 100644 src/routes/(protected)/frequencies/[id]/+page.svelte create mode 100644 src/routes/(protected)/license-keys/+page.server.ts create mode 100644 src/routes/(protected)/license-keys/+page.svelte create mode 100644 src/routes/(protected)/license-keys/page.server.spec.ts create mode 100644 src/routes/(protected)/proclamations/+page.server.ts create mode 100644 src/routes/(protected)/proclamations/+page.svelte create mode 100644 src/routes/(protected)/proclamations/[id]/+page.server.ts create mode 100644 src/routes/(protected)/proclamations/[id]/+page.svelte create mode 100644 src/routes/(protected)/proclamations/[id]/page.server.spec.ts create mode 100644 src/routes/(protected)/proclamations/page.server.spec.ts create mode 100644 src/routes/+layout.server.ts delete mode 100644 src/routes/api/v1/affirmations/+server.ts create mode 100644 src/routes/api/v1/proclamations/+server.ts diff --git a/CLAUDE.md b/CLAUDE.md index bfe5207..d1b33a9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -Rhema Wave Technologies User Portal — a SvelteKit web app managing affirmations (audio content tied to solfeggio frequencies), user accounts, license keys, and authentication. +Rhema Wave Technologies User Portal — a SvelteKit web app managing proclamations (audio content tied to solfeggio frequencies), user accounts, license keys, and authentication. ## Tech Stack @@ -48,13 +48,12 @@ npm run deploy # Apply pending Prisma migrations ### Key Patterns - **Svelte 5 runes throughout** — use `$props()`, `$state()`, `$derived()`, `$effect()` (not Svelte 4 reactivity). -- **SvelteKit form actions** for mutations (create/edit affirmations, registration, account setup). -- **Reusable forms** — `AffirmationForm.svelte` is shared between create and edit via an `action` prop. -- **API routes** at `src/routes/api/v1/` (affirmations, license keys). +- **SvelteKit form actions** for mutations (create/edit proclamations, registration, account setup). +- **API routes** at `src/routes/api/v1/` (proclamations, license keys). ### Database Schema -Core models: `User`, `Profile` (1:1 with User), `Address` (belongs to Profile), `Session`, `Account` (OAuth), `UserRole`, `Affirmation`, `SolfeggioFrequency`, `AffirmationCategory`, `LicenseKey`. Schema lives in `prisma/schema.prisma`. +Core models: `User`, `Profile` (1:1 with User), `Address` (belongs to Profile), `Session`, `Account` (OAuth), `UserRole`, `Proclamation`, `SolfeggioFrequency`, `ProclamationCategory`, `LicenseKey`. Schema lives in `prisma/schema.prisma`. ## Testing diff --git a/prisma/migrations/20260225150600_rename_affirmation_to_proclamation/migration.sql b/prisma/migrations/20260225150600_rename_affirmation_to_proclamation/migration.sql new file mode 100644 index 0000000..034b8d3 --- /dev/null +++ b/prisma/migrations/20260225150600_rename_affirmation_to_proclamation/migration.sql @@ -0,0 +1,60 @@ +/* + Warnings: + + - You are about to drop the `Affirmation` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `AffirmationCategory` table. If the table is not empty, all the data it contains will be lost. + +*/ +-- DropForeignKey +ALTER TABLE "Affirmation" DROP CONSTRAINT "Affirmation_categoryId_fkey"; + +-- DropForeignKey +ALTER TABLE "Affirmation" DROP CONSTRAINT "Affirmation_solfeggioFrequencyId_fkey"; + +-- DropForeignKey +ALTER TABLE "AffirmationCategory" DROP CONSTRAINT "AffirmationCategory_parentId_fkey"; + +-- DropTable +DROP TABLE "Affirmation"; + +-- DropTable +DROP TABLE "AffirmationCategory"; + +-- CreateTable +CREATE TABLE "Proclamation" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "content" TEXT NOT NULL, + "solfeggioFrequencyId" INTEGER NOT NULL, + "categoryId" INTEGER NOT NULL, + "price" DOUBLE PRECISION NOT NULL DEFAULT 0.99, + + CONSTRAINT "Proclamation_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "ProclamationCategory" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "parentId" INTEGER, + + CONSTRAINT "ProclamationCategory_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "Proclamation_name_key" ON "Proclamation"("name"); + +-- CreateIndex +CREATE UNIQUE INDEX "Proclamation_content_key" ON "Proclamation"("content"); + +-- CreateIndex +CREATE UNIQUE INDEX "ProclamationCategory_name_key" ON "ProclamationCategory"("name"); + +-- AddForeignKey +ALTER TABLE "Proclamation" ADD CONSTRAINT "Proclamation_solfeggioFrequencyId_fkey" FOREIGN KEY ("solfeggioFrequencyId") REFERENCES "SolfeggioFrequency"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Proclamation" ADD CONSTRAINT "Proclamation_categoryId_fkey" FOREIGN KEY ("categoryId") REFERENCES "ProclamationCategory"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "ProclamationCategory" ADD CONSTRAINT "ProclamationCategory_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "ProclamationCategory"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index afb4ae2..8a22774 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -109,31 +109,31 @@ model Verification { @@map("verification") } -model Affirmation { - id String @id @default(uuid()) - name String @unique - content String @unique +model Proclamation { + id String @id @default(uuid()) + name String @unique + content String @unique solfeggioFrequencyId Int - solfeggioFrequency SolfeggioFrequency @relation(fields: [solfeggioFrequencyId], references: [id]) + solfeggioFrequency SolfeggioFrequency @relation(fields: [solfeggioFrequencyId], references: [id]) categoryId Int - category AffirmationCategory @relation(fields: [categoryId], references: [id]) - price Float @default(0.99) + category ProclamationCategory @relation(fields: [categoryId], references: [id]) + price Float @default(0.99) } model SolfeggioFrequency { - id Int @id @default(autoincrement()) - name String @unique - value Float @unique - affirmations Affirmation[] + id Int @id @default(autoincrement()) + name String @unique + value Float @unique + proclamations Proclamation[] } -model AffirmationCategory { - id Int @id @default(autoincrement()) - name String @unique - parentId Int? - parent AffirmationCategory? @relation("CategoryHierarchy", fields: [parentId], references: [id]) - children AffirmationCategory[] @relation("CategoryHierarchy") - affirmations Affirmation[] +model ProclamationCategory { + id Int @id @default(autoincrement()) + name String @unique + parentId Int? + parent ProclamationCategory? @relation("CategoryHierarchy", fields: [parentId], references: [id]) + children ProclamationCategory[] @relation("CategoryHierarchy") + proclamations Proclamation[] } model LicenseKey { diff --git a/src/lib/components/forms/affirmation/AffirmationForm.svelte b/src/lib/components/forms/affirmation/AffirmationForm.svelte deleted file mode 100644 index 9a53799..0000000 --- a/src/lib/components/forms/affirmation/AffirmationForm.svelte +++ /dev/null @@ -1,260 +0,0 @@ - - -
- {#if affirmation.id || form?.id} - - {/if} - - - - -
- - -
- -
- - -
- -
- - -
- -
- - - - {#if showNewCategory} - - {/if} -
- - {#if categoryInput || showNewCategory} -
- - - - {#if showNewSubCategory} - - {/if} -
- {/if} - -
- -
- $ - -
-
- - -
diff --git a/src/lib/components/forms/frequency/FrequencyForm.svelte b/src/lib/components/forms/frequency/FrequencyForm.svelte deleted file mode 100644 index 5ad0eb3..0000000 --- a/src/lib/components/forms/frequency/FrequencyForm.svelte +++ /dev/null @@ -1,62 +0,0 @@ - - -
- {#if frequency.id || form?.id} - - {/if} - -
- - -
- -
- - -
- - -
diff --git a/src/lib/components/ui/AffirmationList/AffirmationList.svelte b/src/lib/components/ui/AffirmationList/AffirmationList.svelte deleted file mode 100644 index 7a2a80f..0000000 --- a/src/lib/components/ui/AffirmationList/AffirmationList.svelte +++ /dev/null @@ -1,242 +0,0 @@ - - -
-
- - - - - - - {#if hasActiveFilters} - - {/if} - - - {filteredAndSorted().length} of {affirmations.length} - -
- -
- - - - - - - - - - - - {#each filteredAndSorted() as affirmation} - - - - - - - - {:else} - - - - {/each} - -
- - - - - - - -
{affirmation.name}{categoryDisplayName(affirmation.category) || '—'}{affirmation.solfeggioFrequency?.name ?? '—'}${affirmation.price.toFixed(2)} - - Edit - -
- {#if hasActiveFilters} - No affirmations match your filters - {:else} - No affirmations yet - {/if} -
-
-
\ No newline at end of file diff --git a/src/lib/components/ui/FrequencyList/FrequencyList.svelte b/src/lib/components/ui/FrequencyList/FrequencyList.svelte deleted file mode 100644 index 2aaac69..0000000 --- a/src/lib/components/ui/FrequencyList/FrequencyList.svelte +++ /dev/null @@ -1,176 +0,0 @@ - - -
-
- - - {#if hasActiveFilters} - - {/if} - - - {filteredAndSorted().length} of {frequencies.length} - -
- -
- - - - - - - - - - - {#each filteredAndSorted() as frequency} - - - - - - - {:else} - - - - {/each} - -
- - - - - -
{frequency.name}{frequency.value} Hz - {frequency._count?.affirmations ?? 0} - - - Edit - -
- {#if hasActiveFilters} - No frequencies match your filters - {:else} - No frequencies yet - {/if} -
-
-
diff --git a/src/lib/components/ui/topBar/TopBar.svelte b/src/lib/components/ui/topBar/TopBar.svelte index 87010c3..5f04ed9 100644 --- a/src/lib/components/ui/topBar/TopBar.svelte +++ b/src/lib/components/ui/topBar/TopBar.svelte @@ -1,15 +1,22 @@