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:
@@ -145,11 +145,20 @@ export const SYMBOL_CONTEXT_OVERRIDES: Record<string, SymbolContextOverride> = {
|
||||
peers: ['CRWV', 'NBIS', 'APLD', 'VRT', 'SMCI', 'ANET', 'EQIX', 'DLR'],
|
||||
sectorEtf: 'XLK',
|
||||
sectorLabel: 'Technology (AI infrastructure)',
|
||||
themeEtf: 'SMH',
|
||||
themeEtf: null,
|
||||
themeLabel: 'AI / hyperscaler infrastructure',
|
||||
classificationNote:
|
||||
'Vendor feeds often label IREN as Financial Services / Capital Markets (crypto-era bucket). Operating comparison uses AI infrastructure / GPU-cloud peers (e.g. CRWV, NBIS), not banks or pure crypto miners.',
|
||||
},
|
||||
SLNH: {
|
||||
peers: ['IREN', 'CIFR', 'WULF', 'CORZ', 'APLD'],
|
||||
sectorEtf: 'XLK',
|
||||
sectorLabel: 'Technology (green data centers)',
|
||||
themeEtf: null,
|
||||
themeLabel: null,
|
||||
classificationNote:
|
||||
'Vendor feeds label SLNH as Financial Services; operating profile is green-energy data centers and crypto-mining infrastructure.',
|
||||
},
|
||||
CRWV: {
|
||||
peers: ['NBIS', 'IREN', 'APLD', 'VRT', 'SMCI', 'ANET', 'EQIX', 'DLR'],
|
||||
sectorEtf: 'XLK',
|
||||
@@ -168,8 +177,8 @@ export const SYMBOL_CONTEXT_OVERRIDES: Record<string, SymbolContextOverride> = {
|
||||
},
|
||||
CIFR: {
|
||||
peers: ['RIOT', 'MARA', 'CLSK', 'WULF', 'CORZ', 'HUT', 'BITF', 'IREN'],
|
||||
sectorEtf: 'XLE',
|
||||
sectorLabel: 'Energy-linked digital assets',
|
||||
sectorEtf: 'XLK',
|
||||
sectorLabel: 'Technology (digital assets)',
|
||||
themeEtf: null,
|
||||
themeLabel: 'Digital-asset mining',
|
||||
classificationNote:
|
||||
@@ -177,24 +186,24 @@ export const SYMBOL_CONTEXT_OVERRIDES: Record<string, SymbolContextOverride> = {
|
||||
},
|
||||
RIOT: {
|
||||
peers: [...CRYPTO_MINING_PEERS],
|
||||
sectorEtf: 'XLE',
|
||||
sectorLabel: 'Energy-linked digital assets',
|
||||
sectorEtf: 'XLK',
|
||||
sectorLabel: 'Technology (digital assets)',
|
||||
themeEtf: null,
|
||||
themeLabel: 'Digital-asset mining',
|
||||
classificationNote: 'Yahoo may list under Financials; peer set is crypto miners.',
|
||||
},
|
||||
MARA: {
|
||||
peers: [...CRYPTO_MINING_PEERS],
|
||||
sectorEtf: 'XLE',
|
||||
sectorLabel: 'Energy-linked digital assets',
|
||||
sectorEtf: 'XLK',
|
||||
sectorLabel: 'Technology (digital assets)',
|
||||
themeEtf: null,
|
||||
themeLabel: 'Digital-asset mining',
|
||||
classificationNote: 'Yahoo may list under Financials; peer set is crypto miners.',
|
||||
},
|
||||
CLSK: {
|
||||
peers: [...CRYPTO_MINING_PEERS],
|
||||
sectorEtf: 'XLE',
|
||||
sectorLabel: 'Energy-linked digital assets',
|
||||
sectorEtf: 'XLK',
|
||||
sectorLabel: 'Technology (digital assets)',
|
||||
themeEtf: null,
|
||||
themeLabel: 'Digital-asset mining',
|
||||
classificationNote: 'Yahoo may list under Financials; peer set is crypto miners.',
|
||||
@@ -203,7 +212,7 @@ export const SYMBOL_CONTEXT_OVERRIDES: Record<string, SymbolContextOverride> = {
|
||||
peers: [...CRYPTO_MINING_PEERS],
|
||||
sectorEtf: 'XLK',
|
||||
sectorLabel: 'Technology (compute infrastructure)',
|
||||
themeEtf: 'SMH',
|
||||
themeEtf: null,
|
||||
themeLabel: 'AI / HPC infrastructure',
|
||||
classificationNote: 'Mining + HPC/AI data-center transition; not a traditional financial.',
|
||||
},
|
||||
@@ -211,7 +220,7 @@ export const SYMBOL_CONTEXT_OVERRIDES: Record<string, SymbolContextOverride> = {
|
||||
peers: [...CRYPTO_MINING_PEERS],
|
||||
sectorEtf: 'XLK',
|
||||
sectorLabel: 'Technology (compute infrastructure)',
|
||||
themeEtf: 'SMH',
|
||||
themeEtf: null,
|
||||
themeLabel: 'AI / HPC infrastructure',
|
||||
classificationNote: 'Mining + AI hosting; not a traditional financial.',
|
||||
},
|
||||
@@ -219,7 +228,7 @@ export const SYMBOL_CONTEXT_OVERRIDES: Record<string, SymbolContextOverride> = {
|
||||
peers: ['IREN', 'CIFR', 'CRWV', 'NBIS', 'EQIX', 'DLR', 'VRT', 'SMCI'],
|
||||
sectorEtf: 'XLK',
|
||||
sectorLabel: 'Technology (data centers)',
|
||||
themeEtf: 'SMH',
|
||||
themeEtf: null,
|
||||
themeLabel: 'AI / HPC infrastructure',
|
||||
classificationNote: 'AI data-center developer/operator peer set.',
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user