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
+33 -7
View File
@@ -348,14 +348,17 @@ CREATE TABLE IF NOT EXISTS reports (
generated_at TEXT NOT NULL
);
-- ===== Alert subscriptions (user-defined rules for which alerts to receive) =====
CREATE TABLE IF NOT EXISTS alerts (
id TEXT PRIMARY KEY,
owner_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
symbol TEXT,
alert_type TEXT NOT NULL, -- informed_buy|informed_sell|new_13da|rotation_incipient|regime_shift|conviction_unlock|...
params TEXT NOT NULL, -- JSON thresholds
last_fired TEXT,
created_at TEXT NOT NULL
id TEXT PRIMARY KEY,
owner_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
watchlist_id TEXT, -- NULL = global default, non-NULL = list-level
symbol TEXT, -- NULL = list-level, non-NULL = ticker-level
alert_type TEXT NOT NULL, -- informed_buy|informed_sell|new_13da|rotation_incipient|regime_shift|conviction_unlock|...
enabled INTEGER NOT NULL DEFAULT 1,
params TEXT NOT NULL DEFAULT '{}', -- JSON thresholds
last_fired TEXT,
created_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS trusted_accounts (
@@ -566,6 +569,29 @@ CREATE INDEX IF NOT EXISTS idx_alert_events_user ON alert_events(user_id, create
CREATE INDEX IF NOT EXISTS idx_alert_events_dedup ON alert_events(dedup_key);
CREATE INDEX IF NOT EXISTS idx_alert_events_throttle ON alert_events(throttle_key);
-- ===== Alert comparison state (post-fetch change detection) =====
CREATE TABLE IF NOT EXISTS alert_comparison_state (
symbol TEXT NOT NULL,
alert_type TEXT NOT NULL,
state TEXT NOT NULL DEFAULT '{}', -- JSON snapshot
updated_at TEXT NOT NULL,
PRIMARY KEY (symbol, alert_type)
);
-- ===== SMTP config for email alerts =====
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
);
-- ===== Slice 12 — Strategy Lab + Backtest =====
CREATE TABLE IF NOT EXISTS strategies (
id TEXT PRIMARY KEY,