Rename Tracked Events to Tracked Items, add More>> spillover, lock recap cadence

- Tracked items get the same isSpillover flag as categories: a per-item "More"
  checkbox collapses it into the "More »" nav tab instead of its own top-level
  tab, mirroring Category.isSpillover end to end (schema, CRUD, public API,
  nav partitioning, /more page).
- Renamed user-facing "Tracked events" text to "Tracked items" (admin tab,
  toolbar, buttons, placeholders, /event/:id subtitle) — the underlying
  TrackedEvent/events data model and routes are unchanged.
- Locked the per-item recap cadence dropdown (previously a live
  Continuous/Daily/Hourly picker) and moved it to its own labeled section with
  an explanatory hint: reading eventsRecap.ts confirmed Continuous and Hourly
  currently behave identically, so the live choice was more confusing than
  useful. New items are fixed to Continuous; existing items show their real
  cadence read-only.
This commit is contained in:
Claude
2026-07-26 17:59:56 +00:00
parent 20f9267594
commit 1068a4a5f7
13 changed files with 143 additions and 45 deletions
+3 -1
View File
@@ -47,7 +47,9 @@ export async function registerPublicRoutes(app: FastifyInstance) {
app.get('/api/events', async () => {
// Public fields only — sourceIds, cadenceTime etc. stay admin-only.
return eventsDb.listEvents().map((e) => ({ id: e.id, name: e.name, active: e.active, cadence: e.cadence }));
return eventsDb
.listEvents()
.map((e) => ({ id: e.id, name: e.name, active: e.active, cadence: e.cadence, isSpillover: e.isSpillover }));
});
// Drives the site nav — admin-editable (add/remove/reorder) via /api/admin/categories,
+6 -3
View File
@@ -12,6 +12,7 @@ function rowToEvent(row: any): TrackedEvent {
cadence: row.cadence,
cadenceTime: row.cadence_time,
active: !!row.active,
isSpillover: !!row.is_spillover,
retentionOverrideDays: row.retention_override_days,
lastRecapAt: row.last_recap_at,
createdAt: row.created_at
@@ -52,8 +53,8 @@ export function createEvent(input: Partial<TrackedEvent>): TrackedEvent {
const id = `evt-${randomUUID()}`;
const now = new Date().toISOString();
db.prepare(
`INSERT INTO tracked_events (id, name, description, source_ids, keywords, cadence, cadence_time, active, retention_override_days, last_recap_at, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?)`
`INSERT INTO tracked_events (id, name, description, source_ids, keywords, cadence, cadence_time, active, is_spillover, retention_override_days, last_recap_at, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?)`
).run(
id,
input.name ?? 'Untitled event',
@@ -63,6 +64,7 @@ export function createEvent(input: Partial<TrackedEvent>): TrackedEvent {
input.cadence ?? 'continuous',
input.cadenceTime ?? null,
input.active === false ? 0 : 1,
input.isSpillover ? 1 : 0,
input.retentionOverrideDays ?? null,
now
);
@@ -74,7 +76,7 @@ export function updateEvent(id: string, patch: Partial<TrackedEvent>): TrackedEv
if (!existing) return null;
const merged = { ...existing, ...patch };
db.prepare(
`UPDATE tracked_events SET name=?, description=?, source_ids=?, keywords=?, cadence=?, cadence_time=?, active=?, retention_override_days=?, last_recap_at=? WHERE id=?`
`UPDATE tracked_events SET name=?, description=?, source_ids=?, keywords=?, cadence=?, cadence_time=?, active=?, is_spillover=?, retention_override_days=?, last_recap_at=? WHERE id=?`
).run(
merged.name,
merged.description,
@@ -83,6 +85,7 @@ export function updateEvent(id: string, patch: Partial<TrackedEvent>): TrackedEv
merged.cadence,
merged.cadenceTime,
merged.active ? 1 : 0,
merged.isSpillover ? 1 : 0,
merged.retentionOverrideDays,
merged.lastRecapAt,
id
+4
View File
@@ -133,6 +133,7 @@ export function migrate() {
cadence TEXT NOT NULL DEFAULT 'continuous',
cadence_time TEXT,
active INTEGER NOT NULL DEFAULT 1,
is_spillover INTEGER NOT NULL DEFAULT 0,
retention_override_days INTEGER,
last_recap_at TEXT,
created_at TEXT NOT NULL
@@ -328,6 +329,9 @@ export function migrate() {
if (!hasColumn('tracked_events', 'keywords')) {
db.exec("ALTER TABLE tracked_events ADD COLUMN keywords TEXT NOT NULL DEFAULT '[]'");
}
if (!hasColumn('tracked_events', 'is_spillover')) {
db.exec('ALTER TABLE tracked_events ADD COLUMN is_spillover INTEGER NOT NULL DEFAULT 0');
}
if (!hasColumn('merged_articles', 'is_recap')) {
db.exec('ALTER TABLE merged_articles ADD COLUMN is_recap INTEGER NOT NULL DEFAULT 0');
}
+2
View File
@@ -181,6 +181,8 @@ export interface TrackedEvent {
cadence: 'continuous' | 'daily' | 'hourly' | 'custom';
cadenceTime: string | null;
active: boolean;
/** Collapses into the "More »" nav tab instead of getting its own top-level tab — same idea as Category.isSpillover. */
isSpillover: boolean;
retentionOverrideDays: number | null;
lastRecapAt: string | null;
createdAt: string;