feat: per-user module access, classification watchlists, watchlist persistence & move

**Module access control (admin)**
- Added modules column to users table (JSON array of allowed module keys)
- auth.me returns modules; admin.setUserModules/disableUser/enableUser/deleteUser procedures
- UserActions dropdown with Reset Password, Manage Modules, Disable/Enable, Delete
- useFeatureAccess hook + FeatureGate component for page-level gating
- SidebarNav, CommandPalette, MobileTabNav filter by modules

**Classification watchlists (auto-generated sector/thematic/style/region)**
- watchlists schema: added kind, class_key, class_label columns
- materializeClassificationWatchlists(): lazy idempotent materialization of system lists from Yahoo sector data
- 11 GICS sector lists, Semiconductors, Biotech thematic lists, Small Cap style, Intl region
- Materialization triggered on listWatchlists + addSymbol/removeSymbol/add
- Sector thematic filter uses industry keywords (semiconductors, biotech) to avoid misclassification
- Symbol overrides fixed: IREN removed from SMH theme, bitcoin miners XLE->XLK, SLNH added as Technology

**Watchlist persistence & move**
- active-watchlist-store.ts: Zustand persist store mirroring active-symbol-store pattern
- moveSymbol procedure: removes from source, adds to target (idempotent, preserves empty lists)
- Action menu (⋯) per row: Move to + Remove, click-outside close
- Active watchlist survives navigation and page reloads

**List protections**
- default list: non-deletable, non-renamable, keeps empty row when pruned
- System lists (sector/thematic etc.): read-only, add form hidden, no remove/move/delete

**Per-user module restrictions**
- ProtectedProcedure blocks non-active users
- deleteUser refuses self-delete and last-admin-delete, cleans halt_state manually
- Module gating on execution/analytics pages, sidebar, command palette, mobile nav

Also fixed: schema.sql index moved to migration, materialize DB column fixes, test updates.
This commit is contained in:
Investor Flow Build
2026-07-25 11:38:23 -04:00
parent 63d9f80c09
commit 7492317ebb
27 changed files with 1187 additions and 387 deletions
+7
View File
@@ -64,6 +64,13 @@ function runMigrations(db: DatabaseSync): void {
`ALTER TABLE users ADD COLUMN backup_codes_hashed TEXT`,
`ALTER TABLE users ADD COLUMN is_admin INTEGER NOT NULL DEFAULT 0`,
`ALTER TABLE rotation_state ADD COLUMN signal_since TEXT`,
`ALTER TABLE users ADD COLUMN modules TEXT NOT NULL DEFAULT '["research","settings"]'`,
`UPDATE users SET modules='["research","execution","analytics","settings"]' WHERE modules='["research","settings"]'`,
`ALTER TABLE watchlists ADD COLUMN kind TEXT NOT NULL DEFAULT 'user'`,
`ALTER TABLE watchlists ADD COLUMN class_key TEXT`,
`ALTER TABLE watchlists ADD COLUMN class_label TEXT`,
`DROP INDEX IF EXISTS idx_watchlists_owner_name`,
`CREATE UNIQUE INDEX IF NOT EXISTS idx_watchlists_owner_kind_name ON watchlists(owner_id, kind, name)`,
`CREATE TABLE IF NOT EXISTS rotation_state (
id TEXT PRIMARY KEY DEFAULT 'singleton',
signal TEXT NOT NULL DEFAULT 'none',