New scheduling UI!

This commit is contained in:
George Powell
2026-07-07 17:02:24 -04:00
parent be0d7ad297
commit 21efdd4eef
19 changed files with 2127 additions and 39 deletions
+71 -12
View File
@@ -1,6 +1,7 @@
<script lang="ts">
import { browser } from '$app/environment';
import { onMount } from 'svelte';
import { invalidateAll } from '$app/navigation';
import AuthModal from '$lib/components/AuthModal.svelte';
import Container from '$lib/components/Container.svelte';
import { bibleBooks } from '$lib/types/bible';
@@ -35,6 +36,41 @@
let filter = $state<'all' | 'upcoming' | 'past'>('all');
const filters = ['all', 'upcoming', 'past'] as const;
// Dev-only seed button (calls POST /api/dev/seed-submission, which is
// host-gated to localhost:5173 / test.bibdle.com). Hidden in prod.
const isDevHost = $derived(
browser &&
['localhost:5173', 'test.bibdle.com'].includes(window.location.host)
);
let seeding = $state(false);
let seedMessage = $state<{ ok: boolean; text: string } | null>(null);
async function seedSubmission() {
seeding = true;
seedMessage = null;
try {
const res = await fetch('/api/dev/seed-submission', { method: 'POST' });
const body = await res.json().catch(() => ({}));
if (!res.ok) {
seedMessage = {
ok: false,
text: body?.error ?? `Failed (${res.status})`
};
} else {
seedMessage = {
ok: true,
text: `Seeded ${body?.scheduledDate ?? ''}${body?.reference ?? ''}`
};
// Reload server load data so the new row appears in the table.
await invalidateAll();
}
} catch (err) {
seedMessage = { ok: false, text: String(err) };
} finally {
seeding = false;
}
}
function getOrCreateAnonymousId(): string {
if (!browser) return '';
const key = 'bibdle-anonymous-id';
@@ -148,22 +184,45 @@
{:else}
<!-- Filter toggle -->
<div class="flex items-center justify-between gap-2 mb-4 flex-wrap">
<div class="flex gap-1 bg-white/5 rounded-lg p-1 border border-white/10">
{#each filters as f}
<div class="flex items-center gap-2 flex-wrap">
<div class="flex gap-1 bg-white/5 rounded-lg p-1 border border-white/10">
{#each filters as f}
<button
onclick={() => (filter = f)}
class="px-3 py-1.5 rounded-md text-xs font-medium transition-colors {filter ===
f
? 'bg-blue-600 text-white'
: 'text-gray-300 hover:bg-white/5'}"
>
{f.charAt(0).toUpperCase() + f.slice(1)}
</button>
{/each}
</div>
{#if isDevHost}
<button
onclick={() => (filter = f)}
class="px-3 py-1.5 rounded-md text-xs font-medium transition-colors {filter ===
f
? 'bg-blue-600 text-white'
: 'text-gray-300 hover:bg-white/5'}"
onclick={seedSubmission}
disabled={seeding}
class="px-3 py-1.5 rounded-md text-xs font-medium border border-white/10 bg-white/5 text-gray-200 hover:bg-white/10 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
{f.charAt(0).toUpperCase() + f.slice(1)}
{seeding ? 'Seeding…' : '+ Seed test submission'}
</button>
{/each}
{/if}
</div>
<div class="flex items-center gap-3">
{#if seedMessage}
<span
class="text-xs {seedMessage.ok
? 'text-emerald-400'
: 'text-red-400'}"
>
{seedMessage.text}
</span>
{/if}
<span class="text-xs text-gray-500">
{filteredRows.length} {filteredRows.length === 1 ? 'row' : 'rows'}
</span>
</div>
<span class="text-xs text-gray-500">
{filteredRows.length} {filteredRows.length === 1 ? 'row' : 'rows'}
</span>
</div>
{#if filteredRows.length === 0}