first commit
This commit is contained in:
commit
efa5080897
127 changed files with 18610 additions and 0 deletions
146
frontend/src/components/AppShell.tsx
Normal file
146
frontend/src/components/AppShell.tsx
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
import { useState } from 'react'
|
||||
import {
|
||||
Activity, Bell, BookOpen, Bot, Boxes, ChevronDown, CircleDollarSign,
|
||||
Code2, CreditCard, Cpu, FileCheck2, Gauge, KeyRound, LayoutDashboard,
|
||||
LogOut, Menu, MessageSquareText, Network, Phone, Search, Settings, ShieldCheck,
|
||||
SlidersHorizontal, TestTube2, Users, Webhook, X, Zap,
|
||||
} from 'lucide-react'
|
||||
import { NavLink, Outlet, useNavigate } from 'react-router-dom'
|
||||
import { useAuth } from '../features/auth/auth-context'
|
||||
import { ComplianceProvider } from '../features/compliance/ComplianceProvider'
|
||||
import { useCompliance } from '../features/compliance/compliance-context'
|
||||
import { COMPLIANCE_ONLY_PATHS } from '../features/compliance/compliance-context'
|
||||
|
||||
const groups = [
|
||||
{ label: 'Compliance', links: [
|
||||
{ label: 'KYC', path: '/console/kyc', icon: ShieldCheck },
|
||||
{ label: 'DLT', path: '/console/dlt', icon: FileCheck2 },
|
||||
{ label: 'Message settings', path: '/console/message-settings', icon: SlidersHorizontal },
|
||||
]},
|
||||
{ label: 'Workspace', links: [
|
||||
{ label: 'Overview', path: '/console', icon: LayoutDashboard },
|
||||
{ label: 'Activity', path: '/console/activity', icon: Activity },
|
||||
]},
|
||||
{ label: 'Agent', links: [
|
||||
{ label: 'UrmiAI', path: '/console/agent/urmiai', icon: Bot },
|
||||
{ label: 'UrmIOT', path: '/console/agent/urmiot', icon: Cpu },
|
||||
{ label: 'UrmiOTP', path: '/console/agent/urmiotp', icon: KeyRound },
|
||||
]},
|
||||
{ label: 'Communicate', links: [
|
||||
{ label: 'Messaging', path: '/console/messaging', icon: MessageSquareText },
|
||||
{ label: 'Campaigns', path: '/console/campaigns', icon: Zap },
|
||||
{ label: 'Templates', path: '/console/templates', icon: BookOpen },
|
||||
]},
|
||||
{ label: 'Numbers', links: [
|
||||
{ label: 'Voice', path: '/console/voice', icon: Phone },
|
||||
{ label: 'Buy Numbers', path: '/console/numbers/buy', icon: Search },
|
||||
{ label: 'My Numbers', path: '/console/numbers', icon: Network, end: true },
|
||||
]},
|
||||
{ label: 'Develop', links: [
|
||||
{ label: 'Webhooks & Sockets', path: '/console/webhooks', icon: Webhook },
|
||||
{ label: 'API Keys', path: '/console/api-keys', icon: KeyRound },
|
||||
{ label: 'API Explorer', path: '/console/api-explorer', icon: Code2 },
|
||||
]},
|
||||
{ label: 'Test & observe', links: [
|
||||
{ label: 'Testing Center', path: '/console/testing', icon: TestTube2 },
|
||||
{ label: 'Logs', path: '/console/logs', icon: Boxes },
|
||||
{ label: 'Analytics', path: '/console/analytics', icon: Gauge },
|
||||
]},
|
||||
{ label: 'Manage', links: [
|
||||
{ label: 'Billing', path: '/console/billing', icon: CreditCard },
|
||||
{ label: 'Team', path: '/console/team', icon: Users },
|
||||
{ label: 'Settings', path: '/console/settings', icon: Settings },
|
||||
]},
|
||||
]
|
||||
|
||||
function AppShellContent() {
|
||||
const [open, setOpen] = useState(false)
|
||||
const [accountOpen, setAccountOpen] = useState(false)
|
||||
const { user, logout } = useAuth()
|
||||
const { status } = useCompliance()
|
||||
const navigate = useNavigate()
|
||||
|
||||
const unlocked = status?.console_unlocked ?? false
|
||||
const visibleGroups = unlocked
|
||||
? groups
|
||||
: [{
|
||||
label: 'Compliance',
|
||||
links: groups[0].links.filter((link) => COMPLIANCE_ONLY_PATHS.includes(link.path)),
|
||||
}]
|
||||
|
||||
async function signOut() {
|
||||
await logout()
|
||||
navigate('/')
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="app-shell">
|
||||
<aside className={`sidebar ${open ? 'is-open' : ''}`}>
|
||||
<div className="brand">
|
||||
<NavLink to={unlocked ? '/console' : '/console/dlt'} className="brand-mark" onClick={() => setOpen(false)}>
|
||||
<img src="/urmi-logo.png" alt="Urmi" />
|
||||
</NavLink>
|
||||
<button className="icon-button sidebar-close" onClick={() => setOpen(false)}><X size={18} /></button>
|
||||
</div>
|
||||
<nav>
|
||||
{visibleGroups.map((group) => (
|
||||
<div className="nav-group" key={group.label}>
|
||||
<p>{group.label}</p>
|
||||
{group.links.map(({ label, path, icon: Icon, end }) => (
|
||||
<NavLink key={path} to={path} end={end ?? path === '/console'} onClick={() => setOpen(false)}>
|
||||
<Icon size={17} strokeWidth={1.8} />
|
||||
<span>{label}</span>
|
||||
</NavLink>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
{unlocked && (
|
||||
<div className="sidebar-plan">
|
||||
<div><CircleDollarSign size={17} /><span>Growth plan</span></div>
|
||||
<strong>₹24,820.00</strong>
|
||||
<small>Available balance</small>
|
||||
<button>Add funds</button>
|
||||
</div>
|
||||
)}
|
||||
</aside>
|
||||
|
||||
{open && <button className="sidebar-scrim" aria-label="Close navigation" onClick={() => setOpen(false)} />}
|
||||
|
||||
<div className="workspace">
|
||||
<header className="topbar">
|
||||
<button className="icon-button menu-button" onClick={() => setOpen(true)}><Menu size={20} /></button>
|
||||
<button className="selector">Urmi Technologies <ChevronDown size={14} /></button>
|
||||
<button className="selector hide-mobile">Production API <ChevronDown size={14} /></button>
|
||||
<span className="environment"><i /> Production</span>
|
||||
<label className="global-search">
|
||||
<Search size={16} />
|
||||
<input aria-label="Global search" placeholder="Search numbers, calls, messages..." />
|
||||
<kbd>⌘ K</kbd>
|
||||
</label>
|
||||
<span className="system-status"><i /> Operational</span>
|
||||
<button className="icon-button notification-button"><Bell size={19} /><b>3</b></button>
|
||||
<div className="account-menu">
|
||||
<button className="avatar" onClick={() => setAccountOpen(!accountOpen)}>
|
||||
{user?.avatar_url ? <img src={user.avatar_url} alt="" /> : user?.name.slice(0, 2).toUpperCase()}
|
||||
</button>
|
||||
{accountOpen && <div className="account-popover">
|
||||
<div><strong>{user?.name}</strong><span>{user?.email}</span></div>
|
||||
<NavLink to="/console/account" onClick={() => setAccountOpen(false)}><Settings size={15} /> Sign-in & security</NavLink>
|
||||
<button onClick={signOut}><LogOut size={15} /> Log out</button>
|
||||
</div>}
|
||||
</div>
|
||||
</header>
|
||||
<main><Outlet /></main>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function AppShell() {
|
||||
return (
|
||||
<ComplianceProvider>
|
||||
<AppShellContent />
|
||||
</ComplianceProvider>
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue