From 11a9e4f490ca3b589cec57c4af0507c9f7d66881 Mon Sep 17 00:00:00 2001 From: Songming Fan Date: Thu, 11 Sep 2025 13:36:56 -0400 Subject: [PATCH] Create simulation table --- app/src/components/SimulationTable.tsx | 324 +++++++++++++++++++++++++ 1 file changed, 324 insertions(+) create mode 100644 app/src/components/SimulationTable.tsx diff --git a/app/src/components/SimulationTable.tsx b/app/src/components/SimulationTable.tsx new file mode 100644 index 00000000..9b60500c --- /dev/null +++ b/app/src/components/SimulationTable.tsx @@ -0,0 +1,324 @@ +import { useState } from 'react'; +import { + Table, + Checkbox, + Text, + Badge, + Group, + Button, + Menu, + ActionIcon, + Pagination, + Stack, + Box, + Anchor, +} from '@mantine/core'; +import { IconChevronDown, IconDots } from '@tabler/icons-react'; +import { colors } from '@/designTokens'; + +// Types +export interface SimulationRecord { + id: string; + name: string; + simulationId: string; + dateCreated: Date; + policy: { + name: string; + provisions: number; + additionalCount?: number; + }; + population: { + type: 'household' | 'geographic'; + name: string; + id?: string; + }; + connectedReports: { + title: string; + additionalCount?: number; + }; +} + +interface SimulationTableProps { + data: SimulationRecord[]; + onSimulationSelect?: (simulationId: string) => void; + onAddToReport?: (simulationId: string) => void; + onAction?: (action: string, simulationId: string) => void; + selectedSimulations?: string[]; + onSelectionChange?: (selectedIds: string[]) => void; + currentPage?: number; + totalPages?: number; + onPageChange?: (page: number) => void; +} + +export default function SimulationTable({ + data, + onSimulationSelect, + onAction, + selectedSimulations = [], + onSelectionChange, + currentPage = 1, + totalPages = 1, + onPageChange, +}: SimulationTableProps) { + const [openedMenuId, setOpenedMenuId] = useState(null); + + const handleSelectAll = (checked: boolean) => { + if (checked) { + onSelectionChange?.(data.map(item => item.id)); + } else { + onSelectionChange?.([]); + } + }; + + const handleSelectRow = (simulationId: string, checked: boolean) => { + if (checked) { + onSelectionChange?.([...selectedSimulations, simulationId]); + } else { + onSelectionChange?.(selectedSimulations.filter(id => id !== simulationId)); + } + }; + + const formatRelativeTime = (date: Date): string => { + const now = new Date(); + const diffInHours = Math.floor((now.getTime() - date.getTime()) / (1000 * 60 * 60)); + + if (diffInHours < 1) return 'Just now'; + if (diffInHours < 24) return `${diffInHours}h ago`; + + const diffInDays = Math.floor(diffInHours / 24); + if (diffInDays < 7) return `${diffInDays}d ago`; + + return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); + }; + + const allSelected = data.length > 0 && selectedSimulations.length === data.length; + const someSelected = selectedSimulations.length > 0 && selectedSimulations.length < data.length; + + const handlePreviousPage = () => { + if (currentPage > 1) { + onPageChange?.(currentPage - 1); + } + }; + + const handleNextPage = () => { + if (currentPage < totalPages) { + onPageChange?.(currentPage + 1); + } + }; + + return ( + + + + + + handleSelectAll(event.currentTarget.checked)} + /> + + Name + Date Create + Policy + Population + Connected Reports + + + + {data.map((simulation) => ( + + + handleSelectRow(simulation.id, event.currentTarget.checked)} + /> + + + + + + {simulation.name} + + onSimulationSelect?.(simulation.simulationId)} + style={{ textDecoration: 'none' }} + > + #{simulation.simulationId} + + + + + + + {formatRelativeTime(simulation.dateCreated)} + + + + + + + {simulation.policy.name} + + + + {simulation.policy.provisions} Provisions + + {simulation.policy.additionalCount && ( + + +{simulation.policy.additionalCount} + + )} + + + + + + + {simulation.population.type === 'household' ? 'Household' : ''} #{simulation.population.id || simulation.population.name} + + + + + + + } + > + {simulation.connectedReports.title} + + {simulation.connectedReports.additionalCount && ( + + +{simulation.connectedReports.additionalCount} + + )} + + + + + + + + + + + onAction?.('bookmark', simulation.id)}> + Bookmark + + onAction?.('edit', simulation.id)}> + Edit + + onAction?.('share', simulation.id)}> + Share + + + onAction?.('delete', simulation.id)} + > + Delete + + + + + setOpenedMenuId(opened ? simulation.id : null)} + > + + + + + + + + + + + + ))} + +
+ + + + + + + + Page {currentPage} of {totalPages} + + + + + + ); +}