Files
monitoring-app/src/frontend/routes/apps.$appId.tsx
amaliadwiy ef852842b4 feat: improve UI/UX consistency across all dashboard pages
Apply uniform design system across all routes and components:
- Consistent header pattern with gradient-text titles, dimmed subtitles
- Loader type="dots" replacing text-based loading states
- Icon + text empty/error states with Paper+glass containers
- Full STATUS_COLOR/STATUS_LABEL maps for all BugStatus values
- dayjs timestamps, Tooltip on action icons, size="sm" on badges/pagination
- Modals with overlayProps blur and gradient save buttons
- Replace left-border Papers with clean Stack headers
- Translate all remaining Indonesian UI strings to English
- New monitoring-themed SVG logo and redesigned splash screen
2026-05-05 12:42:41 +08:00

112 lines
3.4 KiB
TypeScript

import { DashboardLayout } from '@/frontend/components/DashboardLayout'
import { APP_CONFIGS } from '@/frontend/config/appMenus'
import {
Badge,
Box,
Container,
Divider,
Group,
Skeleton,
Stack,
Text,
Title,
} from '@mantine/core'
import { useQuery } from '@tanstack/react-query'
import { createFileRoute, Outlet, useParams } from '@tanstack/react-router'
import { TbAlertTriangle, TbTools } from 'react-icons/tb'
export const Route = createFileRoute('/apps/$appId')({
component: AppDetailLayout,
})
const STATUS_COLOR: Record<string, string> = {
active: 'teal',
warning: 'orange',
error: 'red',
}
const STATUS_LABEL: Record<string, string> = {
active: 'Active',
warning: 'Warning',
error: 'Error',
}
function AppDetailLayout() {
const { appId } = useParams({ from: '/apps/$appId' })
const { data: appData, isLoading } = useQuery({
queryKey: ['apps', appId],
queryFn: () => fetch(`/api/apps/${appId}`).then((r) => r.json()),
staleTime: 30_000,
})
const configName = APP_CONFIGS[appId]?.name
const displayName = appData?.name ?? configName ?? appId
const statusKey = appData?.maintenance ? 'maintenance' : (appData?.status ?? 'active')
const statusColor = appData?.maintenance ? 'gray' : (STATUS_COLOR[appData?.status] ?? 'gray')
const statusLabel = appData?.maintenance ? 'Maintenance' : (STATUS_LABEL[appData?.status] ?? appData?.status)
return (
<DashboardLayout>
<Container size="xl" py="lg">
<Stack gap="md">
<Group justify="space-between" align="flex-start">
<Stack gap={6}>
<Group gap="sm" align="center">
{isLoading ? (
<Skeleton height={36} width={180} radius="md" />
) : (
<Title order={2} className="gradient-text">{displayName}</Title>
)}
{!isLoading && appData && (
<Badge color={statusColor} variant="dot" size="md">
{statusLabel}
</Badge>
)}
</Group>
<Group gap="xs" align="center">
<Text size="xs" c="dimmed" fw={500} style={{ fontFamily: 'monospace' }}>
{appId}
</Text>
{isLoading ? (
<Skeleton height={20} width={60} radius="xl" />
) : (
<>
{(appData?.errors ?? 0) > 0 && (
<Badge
variant="light"
color="red"
size="sm"
leftSection={<TbAlertTriangle size={10} />}
>
{appData.errors} open {appData.errors === 1 ? 'error' : 'errors'}
</Badge>
)}
{appData?.maintenance && (
<Badge
variant="light"
color="orange"
size="sm"
leftSection={<TbTools size={10} />}
>
Maintenance mode
</Badge>
)}
</>
)}
</Group>
</Stack>
</Group>
<Divider />
<Box>
<Outlet />
</Box>
</Stack>
</Container>
</DashboardLayout>
)
}