docs: anleitung, changelog 0.3.0 und roadmap zu phase 3

Vier neue Kapitel in der Anleitung (mehrere Sammler, Preise,
Erinnerungen, Dubletten) mit vier automatisch erzeugten Bildern,
Changelog 0.3.0 samt der bewusst weggelassenen BrickLink-Anbindung,
Roadmap abgehakt und um einen offenen Ausblick ergaenzt, Devlog mit
Zeiten und Stolpersteinen.

Dazu zwei Korrekturen aus dem Testlauf:

Der Zeitplan fuer Erinnerungen liess sich nicht aus Next'
instrumentation.ts starten - die Datei wird auch fuer die Edge-Runtime
uebersetzt, und das native SQLite-Modul bricht dort den Build. Er
startet jetzt aus dem Healthcheck heraus, den der Container ohnehin alle
30 Sekunden aufruft, und zusaetzlich aus dem App-Layout; der Aufruf ist
idempotent.

Der Screenshot-Lauf waehlte ploetzlich die falschen Kacheln aus: Der
neue Sammler-Umschalter benutzt ebenfalls aria-pressed. Die
Auswahlkacheln tragen jetzt ein eigenes data-set-card.

Ausserdem raeumen die Testskripte einen Container weg, den ein
abgebrochener Lauf zurueckgelassen hat - sonst blockiert er den Port.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
unknown
2026-09-07 10:11:40 +02:00
co-authored by Claude Opus 5
parent 0d08df0bff
commit e52557b4bf
33 changed files with 312 additions and 28 deletions
+5
View File
@@ -1,8 +1,13 @@
import { AppHeader } from '@/components/layout/app-header';
import { BottomNav } from '@/components/layout/bottom-nav';
import { ensureReminderScheduler } from '@/lib/reminder-job';
import { requireUser } from '@/lib/session';
export default async function AppLayout({ children }: { children: React.ReactNode }) {
// Second entry point besides the healthcheck, in case someone turns that
// off. The call is idempotent.
ensureReminderScheduler();
const user = await requireUser();
return (
+5
View File
@@ -1,6 +1,7 @@
import { sql } from 'drizzle-orm';
import { NextResponse } from 'next/server';
import { db } from '@/db';
import { ensureReminderScheduler } from '@/lib/reminder-job';
export const dynamic = 'force-dynamic';
@@ -9,6 +10,10 @@ export const dynamic = 'force-dynamic';
* file is readable - a healthy Node process on a broken volume is not healthy.
*/
export function GET() {
// The container polls this every 30s, which makes it the earliest reliable
// moment to have the reminder scheduler running. Repeat calls are free.
ensureReminderScheduler();
try {
db.get(sql`select 1`);
return NextResponse.json({ status: 'ok', time: new Date().toISOString() });
+1
View File
@@ -208,6 +208,7 @@ export function CatalogGrid({
<button
key={item.id}
type="button"
data-set-card
disabled={owned}
aria-pressed={isSelected}
onClick={() => toggle(item.id)}
-15
View File
@@ -1,15 +0,0 @@
/**
* Runs once when the server starts.
*
* Next calls this before handling the first request, which is the one place a
* long-lived background task can be started from.
*/
export async function register() {
// Only in the Node runtime - the edge runtime has no timers that survive,
// and the dev server would start a second scheduler on every reload.
if (process.env.NEXT_RUNTIME !== 'nodejs') return;
if (process.env.NODE_ENV !== 'production') return;
const { startReminderScheduler } = await import('@/lib/reminder-job');
startReminderScheduler();
}
+15
View File
@@ -107,6 +107,21 @@ export async function runReminderJob(now = new Date()): Promise<ReminderRun> {
const HOUR_MS = 60 * 60 * 1000;
/**
* Started lazily from the first server render or healthcheck rather than from
* Next's instrumentation hook: that hook is compiled for the edge runtime too,
* and pulling the native SQLite driver into that bundle breaks the build.
* Cached on globalThis so the dev server's module reloading cannot stack up
* several schedulers.
*/
const globalForScheduler = globalThis as unknown as { bricktrackReminders?: boolean };
export function ensureReminderScheduler(): void {
if (globalForScheduler.bricktrackReminders) return;
globalForScheduler.bricktrackReminders = true;
startReminderScheduler();
}
/**
* Starts the hourly check.
*