Spaces:
Running
Running
joseph njoroge kariuki
feat: introduce dedicated Senti Institutional Login Portal with technical styling and dynamic role propagation
5c7593f | import { useState, useEffect } from 'react' | |
| import { Outlet, useLocation } from 'react-router-dom' | |
| import InstSidebar from './InstSidebar' | |
| import InstControlPanel from './InstControlPanel' | |
| import { api } from '../../lib/api' | |
| import { useAuthStore } from '../../store/authStore' | |
| const ORG_THEMES = { | |
| bank: { primary: '#fbbc04', glow: 'rgba(251,188,4,0.08)', container: 'rgba(251,188,4,0.15)' }, | |
| sacco: { primary: '#10b981', glow: 'rgba(16,185,129,0.08)', container: 'rgba(16,185,129,0.15)' }, | |
| fintech: { primary: '#a855f7', glow: 'rgba(168,85,247,0.08)', container: 'rgba(168,85,247,0.15)' }, | |
| mfi: { primary: '#14b8a6', glow: 'rgba(20,184,166,0.08)', container: 'rgba(20,184,166,0.15)' }, | |
| insurer: { primary: '#ef4444', glow: 'rgba(239,68,68,0.08)', container: 'rgba(239,68,68,0.15)' }, | |
| } | |
| export default function InstAppShell() { | |
| const { user } = useAuthStore() | |
| const [sidebarCollapsed, setSidebarCollapsed] = useState(false) | |
| const [orgId, setOrgId] = useState(user?.orgId || 'bank') | |
| const [manifest, setManifest] = useState(null) | |
| const [selectedEntity, setSelectedEntity] = useState(null) | |
| const [entityContext, setEntityContext] = useState(null) | |
| const [loadingContext, setLoadingContext] = useState(false) | |
| const location = useLocation() | |
| const activeModule = location.pathname.split('/')[2] || 'risk' | |
| useEffect(() => { | |
| api.institutional.getManifest(orgId).then(d => setManifest(d)).catch(console.error) | |
| }, [orgId]) | |
| useEffect(() => { | |
| if (!selectedEntity) { setEntityContext(null); return } | |
| setLoadingContext(true) | |
| api.institutional.getEntityContext(selectedEntity) | |
| .then(d => { setEntityContext(d); setLoadingContext(false) }) | |
| .catch(() => setLoadingContext(false)) | |
| }, [selectedEntity]) | |
| const t = ORG_THEMES[orgId] || ORG_THEMES.bank | |
| return ( | |
| <div className="flex flex-col h-screen w-full bg-[#060a14] text-inst-on-surface font-sans overflow-hidden"> | |
| <header className="h-10 flex-shrink-0 flex items-center justify-between px-4 border-b border-white/[0.06] bg-[#080d1a]"> | |
| <div className="flex items-center gap-3"> | |
| <div className="w-2 h-2 rounded-full" style={{ background: t.primary, boxShadow: '0 0 6px ' + t.primary }} /> | |
| <span className="text-[11px] font-bold tracking-wide text-white/90">SENTI</span> | |
| <span className="text-[11px] font-bold tracking-wide" style={{ color: t.primary }}>INSTITUTIONAL</span> | |
| <span className="text-white/20 text-[10px]">|</span> | |
| <span className="text-[10px] text-white/40 font-mono">{manifest?.organization?.name || '...'}</span> | |
| </div> | |
| <div className="flex items-center gap-4"> | |
| <div className="flex items-center gap-1.5 text-[10px] font-mono text-white/30"> | |
| <span className="w-1.5 h-1.5 rounded-full bg-emerald-400 alert-pulse" /> | |
| <span>LIVE</span> | |
| </div> | |
| <span className="text-[10px] font-mono text-white/20">{new Date().toLocaleTimeString([], {hour:'2-digit',minute:'2-digit'})} UTC</span> | |
| <button className="p-1 text-white/30 hover:text-white/60 transition-colors duration-150 ease-[cubic-bezier(0.23,1,0.32,1)] btn-press"><span className="material-symbols-outlined text-[16px]">notifications</span></button> | |
| </div> | |
| </header> | |
| <div className="flex flex-1 overflow-hidden"> | |
| <InstSidebar collapsed={sidebarCollapsed} onToggle={() => setSidebarCollapsed(!sidebarCollapsed)} orgId={orgId} setOrgId={setOrgId} manifest={manifest} /> | |
| <main className="flex-1 flex flex-col overflow-hidden bg-[#0a0f1e]"> | |
| <Outlet context={{ orgId, manifest, activeModule, selectedEntity, setSelectedEntity, entityContext, setOrgId }} /> | |
| </main> | |
| <InstControlPanel selectedEntity={selectedEntity} entityContext={entityContext} loading={loadingContext} setSelectedEntity={setSelectedEntity} manifest={manifest} activeView={activeModule} /> | |
| </div> | |
| </div> | |
| ) | |
| } |