upd: auth

Deskripsi:
-update login
- update struktur database

No Issues
This commit is contained in:
2026-04-15 11:17:04 +08:00
parent 840a89ea0a
commit c66ce4a39b
14 changed files with 273 additions and 173 deletions

View File

@@ -1,4 +1,5 @@
import { APP_CONFIGS } from '@/frontend/config/appMenus'
import { useLogout, useSession } from '@/frontend/hooks/useAuth'
import {
ActionIcon,
AppShell,
@@ -7,6 +8,7 @@ import {
Burger,
Button,
Group,
Loader,
Menu,
NavLink,
Select,
@@ -17,6 +19,7 @@ import {
useMantineColorScheme
} from '@mantine/core'
import { useDisclosure } from '@mantine/hooks'
import { useQuery } from '@tanstack/react-query'
import { Link, useLocation, useMatches, useNavigate, useParams } from '@tanstack/react-router'
import {
TbAlertTriangle,
@@ -50,6 +53,26 @@ export function DashboardLayout({ children }: DashboardLayoutProps) {
const matches = useMatches()
const currentPath = matches[matches.length - 1]?.pathname
// ─── Connect to auth system ──────────────────────────
const { data: sessionData } = useSession()
const user = sessionData?.user
const logout = useLogout()
// ─── Fetch registered apps from database ─────────────
const { data: appsData } = useQuery({
queryKey: ['apps'],
queryFn: () => fetch('/api/apps', { credentials: 'include' }).then((r) => r.json()),
staleTime: 60_000,
})
// ─── Fetch system status from database ───────────────
const { data: systemStatus } = useQuery({
queryKey: ['system', 'status'],
queryFn: () => fetch('/api/system/status', { credentials: 'include' }).then((r) => r.json()),
refetchInterval: 30_000, // refresh every 30 seconds
staleTime: 15_000,
})
const globalNav = [
{ label: 'Dashboard', icon: TbDashboard, to: '/dashboard' },
{ label: 'Applications', icon: TbApps, to: '/apps' },
@@ -61,6 +84,21 @@ export function DashboardLayout({ children }: DashboardLayoutProps) {
const activeApp = appId ? APP_CONFIGS[appId] : null
const navLinks = activeApp ? activeApp.menus : globalNav
// Build app selector data from API
const appSelectData = (appsData || []).map((app: any) => ({
value: app.id,
label: app.name,
}))
// System status indicator
const isOperational = systemStatus?.status === 'operational'
const statusColor = isOperational ? '#10b981' : '#f59e0b'
const statusText = isOperational ? 'All Systems Operational' : 'System Degraded'
const handleLogout = () => {
logout.mutate()
}
return (
<AppShell
header={{ height: 70 }}
@@ -115,21 +153,47 @@ export function DashboardLayout({ children }: DashboardLayoutProps) {
<Menu.Target>
<Avatar
src={undefined}
alt="User"
alt={user?.name || 'User'}
color="brand-blue"
radius="xl"
style={{ cursor: 'pointer' }}
/>
>
{user?.name?.charAt(0).toUpperCase()}
</Avatar>
</Menu.Target>
<Menu.Dropdown>
{user && (
<>
<Menu.Label>
<Text size="sm" fw={600} truncate>{user.name}</Text>
<Text size="xs" c="dimmed" truncate>{user.email}</Text>
</Menu.Label>
<Menu.Divider />
</>
)}
<Menu.Label>Application</Menu.Label>
<Menu.Item leftSection={<TbUserCircle size={16} />}>Profile</Menu.Item>
<Menu.Item leftSection={<TbSettings size={16} />}>Settings</Menu.Item>
<Menu.Item
leftSection={<TbUserCircle size={16} />}
onClick={() => navigate({ to: '/profile' })}
>
Profile
</Menu.Item>
<Menu.Item
leftSection={<TbSettings size={16} />}
onClick={() => navigate({ to: '/dashboard' })}
>
Settings
</Menu.Item>
<Menu.Divider />
<Menu.Label>Danger Zone</Menu.Label>
<Menu.Item color="red" leftSection={<TbLogout size={16} />}>
Logout
<Menu.Item
color="red"
leftSection={<TbLogout size={16} />}
onClick={handleLogout}
disabled={logout.isPending}
>
{logout.isPending ? 'Logging out...' : 'Logout'}
</Menu.Item>
</Menu.Dropdown>
</Menu>
@@ -160,10 +224,8 @@ export function DashboardLayout({ children }: DashboardLayoutProps) {
<Select
label="Selected Application"
value={appId}
data={[
data={appSelectData.length > 0 ? appSelectData : [
{ value: 'desa-plus', label: 'Desa+' },
{ value: 'e-commerce', label: 'E-Commerce' },
{ value: 'fitness-app', label: 'Fitness App' },
]}
onChange={(val) => val && navigate({ to: '/apps/$appId', params: { appId: val } })}
radius="md"
@@ -225,19 +287,26 @@ export function DashboardLayout({ children }: DashboardLayoutProps) {
>
<Text size="xs" c="dimmed" fw={600} mb="xs">SYSTEM STATUS</Text>
<Group gap="xs">
<Box style={{ width: 8, height: 8, borderRadius: '50%', background: '#10b981' }} />
<Text size="sm" fw={500}>All Systems Operational</Text>
<Box style={{ width: 8, height: 8, borderRadius: '50%', background: statusColor, boxShadow: `0 0 6px ${statusColor}` }} />
<Text size="sm" fw={500}>{statusText}</Text>
</Group>
{systemStatus && (
<Text size="xs" c="dimmed" mt={4}>
{systemStatus.activeSessions} active session{systemStatus.activeSessions !== 1 ? 's' : ''}
</Text>
)}
</Box>
<Button
variant="light"
color="red"
fullWidth
leftSection={<TbLogout size={16} />}
leftSection={logout.isPending ? <Loader size={16} color="red" /> : <TbLogout size={16} />}
mt="md"
onClick={handleLogout}
disabled={logout.isPending}
>
Log out
{logout.isPending ? 'Logging out...' : 'Log out'}
</Button>
</Stack>
</Box>