- Add ApiKey Prisma model (keyHash, expiresAt, revokedAt)
- Protect /api/v1/* routes with Authorization: Bearer header check in hooks
- create rotateApiKey() utility: normal (48h grace) and emergency (immediate)
- Add Super-only /admin/api-keys page for key rotation with one-time reveal
- Fix roleId check by using parent layout's DB-fetched adminUser
- Add API Keys link to admin sidebar
- Add sortOrder column to ProclamationCategory with migration
- Implement drag-and-drop reorder in AdminTable with persist-to-DB
- Refactor AdminTable to use $derived + dragOverride (no $effect sync)
- Replace window.location.reload with invalidateAll for reorder
- Convert license key create to modal with subscription level dropdown
- Add LicenseKeyForm.svelte following the CategoryForm pattern
- Add replace action (delete + generate new key with same level)
- Remove deactivateOld checkbox; each key has its own level
- Use use:enhance with update() to refresh list after create/replace
- Registry: Remove 'parent.name' from listFields (all shown records
are top-level with no parent), remove 'parent: true' from include
since unused
- +page.server.ts: Custom load filters to parentId=null (top-level
only); custom upsert syncs children (create/update/delete) and
handles P2002 duplicate-name errors; custom delete cascades to
children first to avoid FK constraint violations
- CategoryForm.svelte: Replace parentId select dropdown with dynamic
list of sub-category inputs. Each row has a name field and Remove
button. Pre-populates from record.children when editing existing
categories. Add Sub-category button appends new rows
- +page.svelte: Remove unused relationOptionsData prop from
CategoryForm; rename submit button label from 'Update' to 'Save'
- prisma: Add ProclamationCategorySubscriptionLevel join table
migration and regenerate client
- General: Update TipTap mock, Toast, license-keys page, proclamations
server/route tests, README, AGENTS.md, and other admin forms
Server tests cover load content stripping per role and upsert/delete
role enforcement. Client tests verify field rendering, pre-fill on
edit, content visibility per role, and submit button dirty-state.
Switched selects from bind:value/value to selected attribute pattern
to work around Svelte 5 select value reactivity in test env.
Switch from initialized from prop to -synced
values so form fields always populate when the record changes.
Key the TipTap editor on record.id to force re-render.
- Only roleId <= 1 (SUPER) can create/edit/delete proclamations
- Content field is stripped from records for non-Super users
- Content field is hidden from the form for non-Super users
- Server actions reject upsert/delete from non-Super users
- Add per-route form components: UserRoleForm, UserForm,
ProclamationForm (with category cascade), CategoryForm,
FreqForm, LevelForm
- Update all admin +page.svelte files to use AdminTable + AdminModal
+ their specific form component instead of AdminPage
- Update profiles page to inline fields instead of using AdminForm
- Add custom Proclamation server action with category fallback logic
(categoryId falls back to parentCategoryId when no sub-category)
- Remove cascade/were/fieldLabels logic from generic crud.ts
- Remove outdated cascade/where tests from crud.spec.ts
- Remove fieldLabels from AdminModelConfig type
- Import superValidate from sveltekit-superforms/server instead of default
path to avoid pulling SuperDebug.svelte into SSR
- Upgrade @prisma/client to match prisma CLI version (7.2.0 -> 7.8.0)
so generated client's query_compiler_fast_bg files exist at runtime
- Add regression test verifying superValidate works in server context
- Add regression test verifying generated Prisma client compiler imports
reference real files
- Add Superforms + Zod 4 for all admin form validation
- Replace address isDefault with three separate boolean defaults (Mailing, Billing, Shipping)
- Update profiles page with three-checkbox address form and toast improvements
- Convert all admin page server actions to pass request directly to crud
- Export superFormErrorsToFieldErrors helper for profiles server actions
- Regenerate Prisma client for new schema fields
- Add type (Mailing/Billing/Shipping) and isDefault fields to Address
schema with migration. Unique default enforced server-side.
- Rewrite profiles page with inline address creation form and
editable user name/email within the profile modal.
- Add upsertAddress and updateUser form actions.
- Fix form redirect by adding use:enhance to AdminForm.
- Disable Update button in AdminForm until a field is changed
(prevents accidental submissions).
- Remove redundant Admin Panel link from top navigation bar. The
sidebar already has the full admin model tree and the Dashboard
shows a Proclamations card for Staff+ users.
- Add listOnly: true to users admin config. Users are created via
registration, not the admin panel. RUD operations still work.
- Inline TopBar into root layout (+layout.svelte) — only used there
- Inline Center into home page (+page.svelte) — only used there
- Remove ComboBox.svelte — dead code, no imports
- Remove TipTap.svelte — dead code, no imports
- Remove empty container/ui/admin/tiptap directories
- Keep Window.svelte in components/containers/ — used by 4 pages
The :root.dark CSS variable inversion caused contrast problems -
text-gray-900 and bg-gray-950 both became light in dark mode, making
text invisible on backgrounds.
Fix:
- Remove :root.dark { --color-gray-*: ... } block from layout.css
- Change :root.dark selector to .dark for form element styles
- Add standard Tailwind dark: classes throughout every .svelte page
- dark:bg-gray-950 (page backgrounds)
- dark:bg-gray-900 (card/surface backgrounds)
- dark:bg-gray-800 (table headers, elevated surfaces, active nav links)
- dark:bg-gray-700 (primary buttons)
- dark:text-gray-100 (primary text)
- dark:text-gray-300 (secondary text, labels)
- dark:text-gray-400 (muted text, inactive nav links, links)
- dark:border-gray-700 (borders)
- dark:border-gray-600 (input borders)
- dark:hover:bg-gray-800 (sidebar hover)
- dark:hover:bg-gray-600 (button hover)
- dark:prose-invert (rich text content)
- TopBar: full dark mode support (bg, links, toggle button)
- Sidebar: all nav links, active states, admin submenu get dark mode
- Auth pages (login, register, forgot, reset): full dark mode
- Protected pages (dashboard, account, setup, license-keys,
proclamations, categories): full dark mode
- Home page, error page, admin page: dark:text-gray-100
- Add blocking inline script in app.html that reads localStorage
'theme' and applies .dark class before render (no flash)
- Invert gray scale in .dark via CSS custom properties so all
grayscale colors reverse automatically (light becomes dark)
- Add @custom-variant dark to enable dark: utility classes
- Add border-button toggle ("Dark"/"Light") in TopBar far right
- Persist preference in localStorage
- Remap all non-gray colors (blue, green, red, purple, slate) to gray
equivalents via Tailwind v4 @theme overrides in layout.css
- Zero out all border-radius values (square everything)
- Zero out all shadow values (no depth)
- Disable all transitions and animations globally
- Change theme-color meta tag from purple (#7B2D8E) to gray (#6b7280)
- Clean up undefined 'underlined' class and typo bg-grey-200
- Skip profile redirect when already on /account/setup so users
who just signed up/logged in land on the page without looping
- Add test for the skip-redirect case
- Append ?verified=true to callbackURL in verification link
- Login page reads query param and shows green success banner
when user lands after email verification
- Register action now returns success instead of redirecting to /account/setup
so users without a session (duplicate email with requireEmailVerification)
see a 'check your email' message instead of crashing
- Protected layout guards against null/undefined locals.user before querying
Prisma (was passing id: undefined)
- Updated register page to show success banner with verification instructions
- Updated test to match new action response
- Build the verification URL as /api/auth/verify-email?token=xxx&callbackURL=/login
- Pass token from Better Auth callback instead of its built-in url
- Add callbackURL=/login so user lands on login after verifying
- Create src/lib/server/email-verification.ts with exported utility function
- Use it in auth.ts sendVerificationEmail callback
- Enable sendOnSignUp so verification email is sent on registration
- Fix email verification link using Better Auth's url parameter (was hardcoded to google.com)
- Make App.Locals session/user types nullable to match reality
- Add explicit rate limiting config (60s window, 100 max)
- Add trusted origins for localhost:5173 and production domain
- Add Forgot Password? and Register links to login page
- Remove unused dead code from auth-client.ts
- Remove outdated known-bug note from AGENTS.md (register redirect is fixed)
- Fix register redirect bug where successful signup returned 400 instead
of redirecting to /account/setup (use isRedirect() to re-throw redirects)
- Add error display to register page and fix Svelte 5 syntax (on:click)
- Add await to Google sign-in on login page and register page
- Fix logout to await signOut and redirect to /
- Implement full password reset flow:
- Add sendResetPassword callback to auth.ts config
- Create forgot-password page with email submission
- Create reset-password page with one-time token validation
- Guard reset page: redirect to / if no valid token in URL
- Add ghlContactId field to Profile model for future Go High Level integration
- Add Prisma migration for ghl_contact_id column
- Update register test to expect redirect instead of broken 400
19 new tests across 5 test files covering the protected layout redirect,
account page load, registration action (documenting the redirect bug),
and login/register component rendering. Includes shared test utilities.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add self-referential parent/child relation on AffirmationCategory for
one-level hierarchy. Update AffirmationForm with two-level category and
sub-category dropdowns, server actions with find-or-create hierarchy
logic, AffirmationList with Parent
This page is not behind (protected) because it must not use the (protected)/layout.
This means that this page also needs to handle it's own authentication, because it should not be open to the public either.
Instead of managing a .env file on the server, I have begun the migration to Github secrets.
This is a tes tto see if I can pass in the Google OAuth client secret.
If so, then I can migrate the rest of the environment variables and manage them in one easy place..
I should still keep a record of these somewhere, encrypted, hidden, and safe.
This is a pretty far-developed starting point. Authentication has been intialized with Better-Auth, a database has been setup with Prisma, and Google + email logins have been enabled, as well as Google + Email registration.