Phase 5: alert subscriptions UI + multiple watchlists + queue fixes
CI / Test & Type-Check (push) Canceled after 0s

UI:
- /alerts page: event history with acknowledge, subscription create/manage with toggle
- /admin/smtp: SMTP config form (host, port, auth, test)
- Watchlist sidebar: dropdown selector for multiple watchlists, create/delete
- Sidebar: alerts count badge, SMTP link under admin
- Mobile tab nav: alerts tab added
- Client trpc.ts: all new API methods + types

Backend:
- watchlists.listByWatchlist procedure + listSymbolsByWatchlist repo fn
- yfinance min-interval 1500->2000ms to reduce Edge 429s
- Fixed e.date.slice error in yfinance-adjustments with typeof guard
- Removed defunct BITF from watchlist+queue
- Cleared 83 failed + 12 backoff queue jobs

Docs:
- FUNCTIONAL_DESIGN.md: alerts + multiple watchlists + SMTP documented
- TECH_DESIGN.md: new modules, tRPC procs, routes updated
This commit is contained in:
Investor Flow Build
2026-07-23 20:51:47 -04:00
parent f71987c119
commit 5b9f770aa4
23 changed files with 1567 additions and 146 deletions
+42 -2
View File
@@ -134,7 +134,12 @@ function runMigrations(db: DatabaseSync): void {
db.exec(`CREATE UNIQUE INDEX IF NOT EXISTS uq_inst_filings ON institution_filings(filer_cik, symbol, reported_quarter, form)`);
} catch { /* ignore */ }
// 4. Data-quality lint table.
// 4. Rename legacy 'Starter' watchlist to 'default'.
try {
db.exec(`UPDATE watchlists SET name='default' WHERE name='Starter'`);
} catch { /* ignore */ }
// 5. Data-quality lint table.
try {
db.exec(`
CREATE TABLE IF NOT EXISTS data_quality (
@@ -152,7 +157,7 @@ function runMigrations(db: DatabaseSync): void {
`);
} catch { /* ignore */ }
// 5. Analyst ratings / upgrades & downgrades (yahoo-finance2).
// 6. Analyst ratings / upgrades & downgrades (yahoo-finance2).
try { db.exec(`ALTER TABLE analyst_ratings ADD COLUMN target_from REAL`); } catch { /* ignore */ }
try { db.exec(`ALTER TABLE analyst_ratings ADD COLUMN target_to REAL`); } catch { /* ignore */ }
try {
@@ -182,6 +187,41 @@ function runMigrations(db: DatabaseSync): void {
strong_sell INTEGER NOT NULL DEFAULT 0,
fetched_at TEXT NOT NULL
)
`);
} catch { /* ignore */ }
// 7. Alert subscriptions: add watchlist_id + enabled columns (existing DBs).
try { db.exec(`ALTER TABLE alerts ADD COLUMN watchlist_id TEXT`); } catch { /* ignore */ }
try { db.exec(`ALTER TABLE alerts ADD COLUMN enabled INTEGER NOT NULL DEFAULT 1`); } catch { /* ignore */ }
// 8. Alert comparison state table.
try {
db.exec(`
CREATE TABLE IF NOT EXISTS alert_comparison_state (
symbol TEXT NOT NULL,
alert_type TEXT NOT NULL,
state TEXT NOT NULL DEFAULT '{}',
updated_at TEXT NOT NULL,
PRIMARY KEY (symbol, alert_type)
)
`);
} catch { /* ignore */ }
// 9. SMTP config table for email alerts.
try {
db.exec(`
CREATE TABLE IF NOT EXISTS smtp_config (
id TEXT PRIMARY KEY DEFAULT 'singleton',
host TEXT NOT NULL DEFAULT 'smtp.mail.me.com',
port INTEGER NOT NULL DEFAULT 587,
secure INTEGER NOT NULL DEFAULT 0,
user TEXT,
pass_enc TEXT,
from_name TEXT NOT NULL DEFAULT 'Investor Flow',
from_email TEXT NOT NULL DEFAULT '',
enabled INTEGER NOT NULL DEFAULT 0,
updated_at TEXT NOT NULL
)
`);
} catch { /* ignore */ }
}