Initial Switch to V2. Completely Overhauled Backend, Frontend and General Structure.
This commit is contained in:
206
frontend/src/router/index.jsx
Normal file
206
frontend/src/router/index.jsx
Normal file
@@ -0,0 +1,206 @@
|
||||
// frontend/src/router/index.jsx
|
||||
// Root router for v2. All routes start from /.
|
||||
|
||||
import { Routes, Route, Navigate } from 'react-router-dom'
|
||||
import { useAuth } from '@/hooks/useAuth'
|
||||
import MainLayout from '@/components/layout/MainLayout'
|
||||
import LoginPage from '@/pages/auth/LoginPage'
|
||||
import DashboardPage from '@/pages/dashboard/DashboardPage'
|
||||
import DeviceList from '@/pages/bellcloud/devices/DeviceList'
|
||||
import DeviceDetail from '@/pages/bellcloud/devices/DeviceDetail'
|
||||
import DeviceMapPage from '@/pages/bellcloud/devices/DeviceMapPage'
|
||||
import StyleGuide from '@/pages/dev/StyleGuide'
|
||||
import CardFontSample from '@/pages/dev/CardFontSample'
|
||||
import UserList from '@/pages/bellcloud/users/UserList'
|
||||
import UserDetail from '@/pages/bellcloud/users/UserDetail'
|
||||
import UserForm from '@/pages/bellcloud/users/UserForm'
|
||||
import MelodyList from '@/pages/bellcloud/melodies/MelodyList'
|
||||
import ArchetypeList from '@/pages/bellcloud/melodies/archetypes/ArchetypeList'
|
||||
import ArchetypeForm from '@/pages/bellcloud/melodies/archetypes/ArchetypeForm'
|
||||
import MelodyComposer from '@/pages/bellcloud/melodies/MelodyComposer'
|
||||
import MelodySettings from '@/pages/bellcloud/melodies/MelodySettings'
|
||||
import MelodyDetail from '@/pages/bellcloud/melodies/MelodyDetail'
|
||||
import MelodyForm from '@/pages/bellcloud/melodies/MelodyForm'
|
||||
import MailPage from '@/pages/crm/comms/mail/MailPage'
|
||||
import CommsPage from '@/pages/crm/comms/CommsPage'
|
||||
import ProductList from '@/pages/crm/products/ProductList'
|
||||
import ProductForm from '@/pages/crm/products/ProductForm'
|
||||
import QuotationList from '@/pages/crm/quotations/QuotationList'
|
||||
import QuotationForm from '@/pages/crm/quotations/QuotationForm'
|
||||
import StaffList from '@/pages/settings/staff/StaffList'
|
||||
import StaffDetail from '@/pages/settings/staff/StaffDetail'
|
||||
import StaffForm from '@/pages/settings/staff/StaffForm'
|
||||
import PublicFeaturesSettings from '@/pages/settings/PublicFeaturesSettings'
|
||||
import AutomationsPage from '@/pages/settings/automations/AutomationsPage'
|
||||
import ApiReferencePage from '@/pages/engineering/developer/ApiReferencePage'
|
||||
import CustomerList from '@/pages/crm/customers/CustomerList'
|
||||
import CustomerDetail from '@/pages/crm/customers/CustomerDetail'
|
||||
import CustomerForm from '@/pages/crm/customers/CustomerForm'
|
||||
import OrderList from '@/pages/crm/orders/OrderList'
|
||||
import OrderDetail from '@/pages/crm/orders/OrderDetail'
|
||||
import DeviceInventory from '@/pages/engineering/manufacturing/DeviceInventory'
|
||||
import DeviceInventoryDetail from '@/pages/engineering/manufacturing/DeviceInventoryDetail'
|
||||
import FlashAssetManager from '@/pages/engineering/manufacturing/FlashAssetManager'
|
||||
import FirmwareManagerPage from '@/pages/engineering/firmware/FirmwareManagerPage'
|
||||
import ProvisioningWizard from '@/pages/engineering/manufacturing/ProvisioningWizard'
|
||||
import HelpdeskPage from '@/pages/crm/comms/helpdesk/HelpdeskPage'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Coming Soon placeholder
|
||||
// ---------------------------------------------------------------------------
|
||||
function ComingSoon() {
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', padding: 'var(--space-16)', gap: 'var(--space-4)' }}>
|
||||
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
||||
<path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83" />
|
||||
</svg>
|
||||
<p style={{ fontSize: 'var(--font-size-lg)', fontWeight: 'var(--font-weight-semibold)', color: 'var(--color-text-secondary)' }}>Coming Soon</p>
|
||||
<p style={{ fontSize: 'var(--font-size-sm)', color: 'var(--color-text-muted)' }}>This page is being built.</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ProtectedRoute
|
||||
// ---------------------------------------------------------------------------
|
||||
function ProtectedRoute() {
|
||||
const { user, loading } = useAuth()
|
||||
if (loading) {
|
||||
return (
|
||||
<div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', backgroundColor: 'var(--color-bg-base)' }}>
|
||||
<p style={{ color: 'var(--color-text-muted)', fontSize: 'var(--font-size-sm)' }}>Loading…</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (!user) return <Navigate to="/login" replace />
|
||||
return <MainLayout />
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// PermissionGate
|
||||
// ---------------------------------------------------------------------------
|
||||
function PermissionGate({ section, action = 'view', children }) {
|
||||
const { hasPermission } = useAuth()
|
||||
if (!hasPermission(section, action)) return <AccessDenied />
|
||||
return children
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// RoleGate
|
||||
// ---------------------------------------------------------------------------
|
||||
function RoleGate({ roles, children }) {
|
||||
const { hasRole } = useAuth()
|
||||
if (!hasRole(...roles)) return <AccessDenied />
|
||||
return children
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AccessDenied
|
||||
// ---------------------------------------------------------------------------
|
||||
function AccessDenied() {
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', padding: 'var(--space-16)' }}>
|
||||
<div style={{ backgroundColor: 'var(--color-bg-surface)', border: '1px solid var(--color-border)', borderRadius: 'var(--radius-lg)', padding: 'var(--space-8)', textAlign: 'center', maxWidth: '400px' }}>
|
||||
<h2 style={{ fontSize: 'var(--font-size-lg)', fontWeight: 'var(--font-weight-semibold)', color: 'var(--color-text-primary)', marginBottom: 'var(--space-2)' }}>Access Denied</h2>
|
||||
<p style={{ fontSize: 'var(--font-size-sm)', color: 'var(--color-text-muted)' }}>You don't have permission to access this feature.</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Root router
|
||||
// ---------------------------------------------------------------------------
|
||||
export default function V2Router() {
|
||||
return (
|
||||
<div className="app">
|
||||
<Routes>
|
||||
|
||||
{/* ── Public routes ─────────────────────────────────────────────── */}
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
<Route path="/cloudflash" element={<ComingSoon />} />
|
||||
<Route path="/serial-monitor" element={<ComingSoon />} />
|
||||
<Route path="/dev/styleguide" element={<StyleGuide />} />
|
||||
<Route path="/dev/card-fonts" element={<CardFontSample />} />
|
||||
|
||||
{/* ── Protected routes ──────────────────────────────────────────── */}
|
||||
<Route element={<ProtectedRoute />}>
|
||||
<Route index element={<DashboardPage />} />
|
||||
|
||||
{/* Melodies */}
|
||||
<Route path="melodies" element={<PermissionGate section="melodies"><MelodyList /></PermissionGate>} />
|
||||
<Route path="melodies/settings" element={<PermissionGate section="melodies"><MelodySettings /></PermissionGate>} />
|
||||
<Route path="melodies/composer" element={<PermissionGate section="melodies" action="edit"><MelodyComposer /></PermissionGate>} />
|
||||
<Route path="melodies/new" element={<PermissionGate section="melodies" action="add"><MelodyForm /></PermissionGate>} />
|
||||
<Route path="melodies/archetypes" element={<PermissionGate section="melodies" action="edit"><ArchetypeList /></PermissionGate>} />
|
||||
<Route path="melodies/archetypes/new" element={<PermissionGate section="melodies" action="edit"><ArchetypeForm /></PermissionGate>} />
|
||||
<Route path="melodies/archetypes/:id" element={<PermissionGate section="melodies" action="edit"><ArchetypeForm /></PermissionGate>} />
|
||||
<Route path="melodies/:id" element={<PermissionGate section="melodies"><MelodyDetail /></PermissionGate>} />
|
||||
<Route path="melodies/:id/edit" element={<PermissionGate section="melodies" action="edit"><MelodyForm /></PermissionGate>} />
|
||||
|
||||
{/* Devices */}
|
||||
<Route path="devices" element={<PermissionGate section="devices"><DeviceList /></PermissionGate>} />
|
||||
<Route path="devices/new" element={<PermissionGate section="devices" action="add"><ComingSoon /></PermissionGate>} />
|
||||
{/* Map preview — temporary unlisted route while map view is under construction */}
|
||||
<Route path="devices/map-preview" element={<PermissionGate section="devices"><DeviceMapPage /></PermissionGate>} />
|
||||
<Route path="devices/:id" element={<PermissionGate section="devices"><DeviceDetail /></PermissionGate>} />
|
||||
<Route path="devices/:id/edit" element={<PermissionGate section="devices" action="edit"><ComingSoon /></PermissionGate>} />
|
||||
|
||||
{/* App Users */}
|
||||
<Route path="users" element={<PermissionGate section="app_users"><UserList /></PermissionGate>} />
|
||||
<Route path="users/new" element={<PermissionGate section="app_users" action="add"><UserForm /></PermissionGate>} />
|
||||
<Route path="users/:id" element={<PermissionGate section="app_users"><UserDetail /></PermissionGate>} />
|
||||
<Route path="users/:id/edit" element={<PermissionGate section="app_users" action="edit"><UserForm /></PermissionGate>} />
|
||||
|
||||
{/* MQTT */}
|
||||
<Route path="mqtt" element={<PermissionGate section="mqtt"><ComingSoon /></PermissionGate>} />
|
||||
<Route path="mqtt/commands" element={<PermissionGate section="mqtt"><ComingSoon /></PermissionGate>} />
|
||||
<Route path="mqtt/logs" element={<PermissionGate section="mqtt"><ComingSoon /></PermissionGate>} />
|
||||
|
||||
{/* Manufacturing */}
|
||||
<Route path="manufacturing" element={<PermissionGate section="manufacturing"><DeviceInventory /></PermissionGate>} />
|
||||
<Route path="manufacturing/provision" element={<PermissionGate section="manufacturing" action="edit"><ProvisioningWizard /></PermissionGate>} />
|
||||
<Route path="manufacturing/devices/:sn" element={<PermissionGate section="manufacturing"><DeviceInventoryDetail /></PermissionGate>} />
|
||||
<Route path="firmware" element={<PermissionGate section="manufacturing"><FirmwareManagerPage /></PermissionGate>} />
|
||||
|
||||
{/* Mail */}
|
||||
<Route path="mail" element={<PermissionGate section="mail"><MailPage /></PermissionGate>} />
|
||||
|
||||
{/* Helpdesk */}
|
||||
<Route path="crm/comms/helpdesk" element={<PermissionGate section="crm"><HelpdeskPage /></PermissionGate>} />
|
||||
|
||||
{/* CRM */}
|
||||
<Route path="crm/comms" element={<PermissionGate section="crm_customers" action="comms_view"><CommsPage /></PermissionGate>} />
|
||||
<Route path="crm/products" element={<PermissionGate section="crm_products"><ProductList /></PermissionGate>} />
|
||||
<Route path="crm/products/new" element={<PermissionGate section="crm_products" action="add"><ProductForm /></PermissionGate>} />
|
||||
<Route path="crm/products/:id" element={<PermissionGate section="crm_products"><ProductForm /></PermissionGate>} />
|
||||
<Route path="crm/customers" element={<PermissionGate section="crm_customers"><CustomerList /></PermissionGate>} />
|
||||
<Route path="crm/customers/new" element={<PermissionGate section="crm_customers" action="add"><CustomerForm /></PermissionGate>} />
|
||||
<Route path="crm/customers/:id" element={<PermissionGate section="crm_customers"><CustomerDetail /></PermissionGate>} />
|
||||
<Route path="crm/customers/:id/edit" element={<PermissionGate section="crm_customers" action="full_access"><CustomerForm /></PermissionGate>} />
|
||||
<Route path="crm/orders" element={<PermissionGate section="crm_orders"><OrderList /></PermissionGate>} />
|
||||
<Route path="crm/orders/:id" element={<PermissionGate section="crm_orders"><OrderDetail /></PermissionGate>} />
|
||||
<Route path="crm/quotations" element={<PermissionGate section="crm_customers" action="quotations_view"><QuotationList /></PermissionGate>} />
|
||||
<Route path="crm/quotations/new" element={<PermissionGate section="crm_customers" action="quotations_edit"><QuotationForm /></PermissionGate>} />
|
||||
<Route path="crm/quotations/:id" element={<PermissionGate section="crm_customers" action="quotations_edit"><QuotationForm /></PermissionGate>} />
|
||||
|
||||
{/* Developer */}
|
||||
<Route path="developer/api" element={<RoleGate roles={['sysadmin', 'admin']}><ApiReferencePage /></RoleGate>} />
|
||||
|
||||
{/* Settings */}
|
||||
<Route path="settings/staff" element={<RoleGate roles={['sysadmin', 'admin']}><StaffList /></RoleGate>} />
|
||||
<Route path="settings/staff/new" element={<RoleGate roles={['sysadmin', 'admin']}><StaffForm /></RoleGate>} />
|
||||
<Route path="settings/staff/:id" element={<RoleGate roles={['sysadmin', 'admin']}><StaffDetail /></RoleGate>} />
|
||||
<Route path="settings/staff/:id/edit" element={<RoleGate roles={['sysadmin', 'admin']}><StaffForm /></RoleGate>} />
|
||||
<Route path="settings/public-features" element={<RoleGate roles={['sysadmin', 'admin']}><PublicFeaturesSettings /></RoleGate>} />
|
||||
<Route path="settings/automations" element={<RoleGate roles={['sysadmin', 'admin']}><AutomationsPage /></RoleGate>} />
|
||||
<Route path="settings/serial-logs" element={<RoleGate roles={['sysadmin', 'admin']}><ComingSoon /></RoleGate>} />
|
||||
|
||||
{/* Catch-all */}
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
</Route>
|
||||
|
||||
</Routes>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user