diff --git a/index.html b/index.html index f7a73bd..fbe7d51 100644 --- a/index.html +++ b/index.html @@ -4,9 +4,10 @@ + - My App + Monitoring System
-
-
Loading...
+ +
Monitoring System
+
+ + + +
diff --git a/src/frontend/components/AppCard.tsx b/src/frontend/components/AppCard.tsx index 7c5fcff..fca444a 100644 --- a/src/frontend/components/AppCard.tsx +++ b/src/frontend/components/AppCard.tsx @@ -1,6 +1,6 @@ -import { Avatar, Button, Card, Group, Stack, Text, useComputedColorScheme } from '@mantine/core' +import { Avatar, Badge, Button, Card, Group, Stack, Text, useComputedColorScheme } from '@mantine/core' import { Link } from '@tanstack/react-router' -import { TbChevronRight, TbDeviceMobile } from 'react-icons/tb' +import { TbAlertTriangle, TbChevronRight, TbDeviceMobile } from 'react-icons/tb' interface AppCardProps { id: string @@ -12,8 +12,9 @@ interface AppCardProps { maintenance?: boolean } -export function AppCard({ id, name, status, errors, version }: AppCardProps) { - const statusColor = status === 'active' ? 'teal' : status === 'warning' ? 'orange' : 'red' +export function AppCard({ id, name, status, errors, version, maintenance }: AppCardProps) { + const statusColor = maintenance ? 'gray' : status === 'active' ? 'teal' : status === 'warning' ? 'orange' : 'red' + const statusLabel = maintenance ? 'Maintenance' : status === 'active' ? 'Active' : status === 'warning' ? 'Warning' : 'Error' const scheme = useComputedColorScheme('light', { getInitialValueInEffect: true }) return ( @@ -35,7 +36,7 @@ export function AppCard({ id, name, status, errors, version }: AppCardProps) { }, })} > - + - + {name} - {/* VERSION {version} */} + {/* v{version} */} - {/* - {status.toUpperCase()} - */} + + {statusLabel} + - {/* - - - - - USER ADOPTION - - {users.toLocaleString()} - - - - - - - - 0 ? '#ef4444' : '#64748b'} /> - ERROR - - 0 ? 'red' : 'dimmed'}>{errors} - - 0 ? 30 : 0} size="sm" color="red" radius="xl" /> - - */} + + Open Errors + 0 ? 'red' : 'teal'} + variant="light" + size="sm" + leftSection={errors > 0 ? : undefined} + > + {errors > 0 ? errors : 'None'} + + ) diff --git a/src/frontend/components/ErrorDataTable.tsx b/src/frontend/components/ErrorDataTable.tsx index 7c73515..cd0c2f7 100644 --- a/src/frontend/components/ErrorDataTable.tsx +++ b/src/frontend/components/ErrorDataTable.tsx @@ -6,16 +6,21 @@ import { Divider, Drawer, Group, + Loader, Paper, ScrollArea, + SimpleGrid, Stack, Table, Text, - Title + ThemeIcon, + Title, + Tooltip, } from '@mantine/core' import { useDisclosure } from '@mantine/hooks' import { useQuery } from '@tanstack/react-query' import { Link } from '@tanstack/react-router' +import dayjs from 'dayjs' import { useState } from 'react' import { TbBug, TbExternalLink, TbHistory, TbMessageReport } from 'react-icons/tb' @@ -23,6 +28,23 @@ export interface ErrorDataTableProps { appId?: string } +const STATUS_COLOR: Record = { + OPEN: 'red', + IN_PROGRESS: 'blue', + ON_HOLD: 'orange', + RESOLVED: 'teal', + RELEASED: 'green', + CLOSED: 'gray', +} +const STATUS_LABEL: Record = { + OPEN: 'Open', + ON_HOLD: 'On Hold', + IN_PROGRESS: 'In Progress', + RESOLVED: 'Resolved', + RELEASED: 'Released', + CLOSED: 'Closed', +} + export function ErrorDataTable({ appId }: ErrorDataTableProps) { const [opened, { open, close }] = useDisclosure(false) const [selectedError, setSelectedError] = useState(null) @@ -41,54 +63,62 @@ export function ErrorDataTable({ appId }: ErrorDataTableProps) { open() } - const getSeverityColor = (sev: string) => { - switch (sev?.toUpperCase()) { - case 'OPEN': return 'red' - case 'IN_PROGRESS': return 'orange' - case 'ON_HOLD': return 'yellow' - default: return 'gray' - } - } - return ( <> - - + + - LATEST ERROR REPORTS + + Latest Error Reports + Most recent open bugs + - + + + - - +
+ - Error Message + Error Description Reporter - App Version - Timestamp - Severity + Version + Reported + Status {isLoading ? ( - - Loading errors... + + + + ) : bugs.length === 0 ? ( - - No errors found. + + + + No error reports found. + ) : bugs.map((error: any) => ( @@ -97,24 +127,34 @@ export function ErrorDataTable({ appId }: ErrorDataTableProps) { onClick={() => handleRowClick(error)} style={{ cursor: 'pointer' }} > - + {error.description} - {error.user?.name || error.userId || 'System'} + + {error.user?.name || error.userId || 'System'} + - {error.affectedVersion || 'N/A'} + + v{error.affectedVersion || 'N/A'} + - + - {new Date(error.createdAt).toLocaleString('en-GB', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false })} + + {dayjs(error.createdAt).format('D MMM YYYY, HH:mm')} + - - - {(error.status || '').toUpperCase()} + + + {STATUS_LABEL[error.status?.toUpperCase()] ?? error.status} @@ -131,37 +171,68 @@ export function ErrorDataTable({ appId }: ErrorDataTableProps) { size="md" title={ - - Error Investigation + + Error Detail } styles={{ - header: { padding: '24px', borderBottom: '1px solid var(--mantine-color-default-border)' }, + header: { padding: '20px 24px', borderBottom: '1px solid var(--mantine-color-default-border)' }, }} > {selectedError && ( - MESSAGE - {selectedError.description} + Description + {selectedError.description} - SOURCE - {selectedError.source} + Status + + {STATUS_LABEL[selectedError.status?.toUpperCase()] ?? selectedError.status} + - APP VERSION - {selectedError.affectedVersion || 'N/A'} + Source + {selectedError.source} + + + App Version + v{selectedError.affectedVersion || 'N/A'} + + + Reported + {dayjs(selectedError.createdAt).format('D MMM YYYY, HH:mm')} + {selectedError.device && ( + + Device + {selectedError.device} · {selectedError.os} + + )} + + {selectedError.feedBack && ( + <> + + + Developer Feedback + {selectedError.feedBack} + + + )} + - STACK TRACE + Stack Trace