diff --git a/spec.md b/spec.md index 3745953..5ed71ba 100644 --- a/spec.md +++ b/spec.md @@ -5,10 +5,10 @@ - [x] **Step 1 — Schema & migration:** add `verse_submissions` table to `src/lib/server/db/schema.ts` (nullable `user_id` FK → `user.id` ON DELETE SET NULL, unique `scheduled_date`, index on `user_id`), pushed to dev.db. - [x] **Step 2 — Bible structure + windowing helpers:** export `getChapterCount`/`getVerseCount` from `xml-bible.ts`; add `composeVerseWindow()` (fall-forward 3-verse window, never crosses book, crosses chapter within book) and `formatWindowReference()` (hyphen same-chapter, en-dash cross-chapter). Tests in `tests/verse-window.test.ts` (17 pass). - [x] **Step 3 — Public APIs:** `GET /api/bible/structure` (66-book verse counts for cascading dropdowns) and `GET /api/verse-window` (live 3-verse preview). -- [ ] **Step 4 — Admin module + `/scheduled-verses` page:** `src/lib/server/admin.ts` (`ADMIN_EMAIL`), auth-walled admin view joining `verse_submissions` → `daily_verses` → `user`. -- [ ] **Step 5 — Submit backend + lazy gap-fill:** `POST /api/submit-verse` (solved-today gate, 7-day cooldown, structural validation, scheduling scan with empty/no-back-to-back/60-day-repeat rules, transaction + retry), `GET /api/submit-verse/status`, modified `getVerseForDate` neighbor avoidance. -- [ ] **Step 6 — Frontend `SubmitVerse.svelte`:** three states (logged-out sign-in dropdown, cooldown + countdown, cascading selects + preview + submit), wired into `WinScreen.svelte`. -- [ ] **Step 7 — Tests:** scheduling validity, cooldown arithmetic, concurrency. +- [x] **Step 4 — Admin module + `/scheduled-verses` page:** `src/lib/server/admin.ts` (`ADMIN_EMAIL`), auth-walled admin view joining `verse_submissions` → `daily_verses` → `user`. +- [x] **Step 5 — Submit backend + lazy gap-fill:** `POST /api/submit-verse` (solved-today gate, 7-day cooldown, structural validation, scheduling scan with empty/no-back-to-back/60-day-repeat rules, transaction + retry), `GET /api/submit-verse/status`, modified `getVerseForDate` neighbor avoidance. +- [x] **Step 6 — Frontend `SubmitVerse.svelte`:** three states (logged-out sign-in dropdown, cooldown + countdown, cascading selects + preview + submit), wired into `WinScreen.svelte`. +- [x] **Step 7 — Tests:** scheduling validity, cooldown arithmetic, concurrency. - [ ] **Step 8 — Docs:** update README schema + routes/API tables. --- diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 5209e6e..66a24c7 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -17,9 +17,9 @@ const handleAuth: Handle = async ({ event, resolve }) => { const { session, user } = await auth.validateSessionToken(sessionToken); if (session) { - auth.setSessionTokenCookie(event, sessionToken, session.expiresAt); + auth.setSessionTokenCookie({ cookies: event.cookies }, sessionToken, session.expiresAt); } else { - auth.deleteSessionTokenCookie(event); + auth.deleteSessionTokenCookie({ cookies: event.cookies }); } event.locals.user = user; diff --git a/src/lib/components/Button.svelte b/src/lib/components/Button.svelte index 9bb58aa..1e32aeb 100644 --- a/src/lib/components/Button.svelte +++ b/src/lib/components/Button.svelte @@ -7,6 +7,7 @@ onclick?: () => void; class?: string; type?: "button" | "submit" | "reset"; + disabled?: boolean; } let { @@ -15,6 +16,7 @@ onclick, class: className = "", type = "button", + disabled = false, }: Props = $props(); const variantClasses = { @@ -31,6 +33,7 @@ + + {:else if !isLoggedIn} + + + {#if expanded} +

+ Sign in to submit a verse for a future day +

+
+ + +
+
+ + +
+ {/if} + {:else if onCooldown} + + + {#if countdownText} +

Next submission in {countdownText}

+ {/if} + {:else} + + + {#if expanded} +
+ {#if statusError} +

{statusError}

+ {/if} + + {#if !structure} +

Loading Bible structure…

+ {:else} +
+ + + + + +
+ + +
+ {#if previewLoading} +

Loading preview…

+ {:else if preview} +

{preview.reference}

+
+ {#each preview.windowVerses as verseText, i (i)} +

{verseText}

+ {/each} +
+ {:else} +

Select a verse to preview.

+ {/if} +
+ + {#if submitError} +

{submitError}

+ {/if} + + + {#if status?.upcoming && status.upcoming.length > 0} +

+ Your upcoming verse{status.upcoming.length > 1 + ? "s" + : ""}: + {#each status.upcoming as u, i (i)} + {#if i > 0}·{/if} + {u.reference} ({fmtDateVague(u.scheduledDate)}) + {/each} +

+ {/if} + {/if} +
+ {/if} + {/if} + + + diff --git a/src/lib/components/VerseDisplay.svelte b/src/lib/components/VerseDisplay.svelte index bc81a69..a954f98 100644 --- a/src/lib/components/VerseDisplay.svelte +++ b/src/lib/components/VerseDisplay.svelte @@ -1,14 +1,21 @@