diff --git a/frontend/app/(dashboard)/notifications-live/page.tsx b/frontend/app/(dashboard)/notifications-live/page.tsx new file mode 100644 index 00000000..29d7b905 --- /dev/null +++ b/frontend/app/(dashboard)/notifications-live/page.tsx @@ -0,0 +1,55 @@ +'use client'; + +import { useEffect, useRef, useState } from 'react'; +import { Bell, Wifi, WifiOff } from 'lucide-react'; + +interface Notification { id: string; message: string; type: 'info' | 'warning' | 'success'; timestamp: string } + +export default function NotificationsRealtimePage() { + const [connected, setConnected] = useState(false); + const [notifications, setNotifications] = useState([ + { id:'1', message:'Asset AST-042 has been checked out by Alice M.', type:'info', timestamp: new Date().toISOString() }, + { id:'2', message:'Maintenance scheduled for Server Rack A on Jan 25', type:'warning', timestamp: new Date().toISOString() }, + ]); + const wsRef = useRef(null); + + useEffect(() => { + const wsUrl = (process.env.NEXT_PUBLIC_WS_URL ?? 'ws://localhost:3001') + '/notifications'; + try { + const ws = new WebSocket(wsUrl); + wsRef.current = ws; + ws.onopen = () => setConnected(true); + ws.onclose = () => setConnected(false); + ws.onmessage = (e) => { + try { + const data = JSON.parse(e.data as string) as Notification; + setNotifications(prev => [data, ...prev].slice(0, 50)); + } catch { /* ignore malformed */ } + }; + } catch { /* ws not available in test env */ } + return () => { wsRef.current?.close(); }; + }, []); + + const TYPE_STYLE: Record = { info:'border-blue-200 bg-blue-50', warning:'border-yellow-200 bg-yellow-50', success:'border-green-200 bg-green-50' }; + + return ( +
+
+

Real-time Notifications

Live updates via WebSocket

+ + {connected ? : }{connected ? 'Connected' : 'Disconnected'} + +
+
+ {notifications.length === 0 + ?
No notifications yet
+ : notifications.map(n => ( +
+

{n.message}

+

{new Date(n.timestamp).toLocaleString()}

+
+ ))} +
+
+ ); +} \ No newline at end of file diff --git a/frontend/app/(dashboard)/rbac/page.tsx b/frontend/app/(dashboard)/rbac/page.tsx new file mode 100644 index 00000000..f572402e --- /dev/null +++ b/frontend/app/(dashboard)/rbac/page.tsx @@ -0,0 +1,56 @@ +'use client'; + +import { useState } from 'react'; +import { Plus, Shield } from 'lucide-react'; +import { Button } from '@/components/ui/button'; + +interface Role { id: string; name: string; department: string; permissions: string[] } + +const MOCK: Role[] = [ + { id:'1', name:'Admin', department:'All', permissions:['create','read','update','delete','manage_users'] }, + { id:'2', name:'Manager', department:'Engineering', permissions:['create','read','update'] }, + { id:'3', name:'Viewer', department:'Finance', permissions:['read'] }, +]; + +const ALL_PERMS = ['create','read','update','delete','manage_users']; + +export default function RBACPage() { + const [roles] = useState(MOCK); + const [selected, setSelected] = useState(null); + + return ( +
+
+

Roles & Permissions

Manage roles, permissions, and department access

+ +
+
+
+
Roles
+ {roles.map(r => ( + + ))} +
+
+ {selected ? ( + <> +

{selected.name} — Permissions

+
+ {ALL_PERMS.map(perm => ( + + ))} +
+ + + ) :

Select a role to manage permissions

} +
+
+
+ ); +} \ No newline at end of file