|
|
|
@@ -0,0 +1,188 @@ |
|
|
|
"use client"; |
|
|
|
|
|
|
|
import React from "react"; |
|
|
|
import { |
|
|
|
Box, |
|
|
|
Card, |
|
|
|
CardContent, |
|
|
|
Grid, |
|
|
|
Typography, |
|
|
|
} from "@mui/material"; |
|
|
|
import Inventory2OutlinedIcon from "@mui/icons-material/Inventory2Outlined"; |
|
|
|
import MonetizationOnOutlinedIcon from "@mui/icons-material/MonetizationOnOutlined"; |
|
|
|
import LayersOutlinedIcon from "@mui/icons-material/LayersOutlined"; |
|
|
|
import SearchOutlinedIcon from "@mui/icons-material/SearchOutlined"; |
|
|
|
import LocalShippingOutlinedIcon from "@mui/icons-material/LocalShippingOutlined"; |
|
|
|
import OutboundOutlinedIcon from "@mui/icons-material/OutboundOutlined"; |
|
|
|
import BarChartOutlinedIcon from "@mui/icons-material/BarChartOutlined"; |
|
|
|
import PieChartOutlineOutlinedIcon from "@mui/icons-material/PieChartOutlineOutlined"; |
|
|
|
import type { SvgIconComponent } from "@mui/icons-material"; |
|
|
|
import { REPORTS } from "@/config/reportConfig"; |
|
|
|
import { REPORT_CATEGORIES, type ReportCategoryConfig } from "./reportCategories"; |
|
|
|
|
|
|
|
const REPORT_ICON_MAP: Record<string, SvgIconComponent> = { |
|
|
|
"rep-011": Inventory2OutlinedIcon, |
|
|
|
"rep-007": MonetizationOnOutlinedIcon, |
|
|
|
"rep-012": LayersOutlinedIcon, |
|
|
|
"rep-010": SearchOutlinedIcon, |
|
|
|
"rep-004": LocalShippingOutlinedIcon, |
|
|
|
"rep-014": LocalShippingOutlinedIcon, |
|
|
|
"rep-008": OutboundOutlinedIcon, |
|
|
|
"rep-009": OutboundOutlinedIcon, |
|
|
|
"rep-013": LocalShippingOutlinedIcon, |
|
|
|
"rep-006": BarChartOutlinedIcon, |
|
|
|
"rep-005": PieChartOutlineOutlinedIcon, |
|
|
|
}; |
|
|
|
|
|
|
|
const reportById = Object.fromEntries(REPORTS.map((r) => [r.id, r])); |
|
|
|
|
|
|
|
interface ReportSelectionDashboardProps { |
|
|
|
selectedReportId: string; |
|
|
|
onSelectReport: (reportId: string) => void; |
|
|
|
} |
|
|
|
|
|
|
|
function ReportCard({ |
|
|
|
reportId, |
|
|
|
title, |
|
|
|
accent, |
|
|
|
selected, |
|
|
|
onClick, |
|
|
|
}: { |
|
|
|
reportId: string; |
|
|
|
title: string; |
|
|
|
accent: string; |
|
|
|
selected: boolean; |
|
|
|
onClick: () => void; |
|
|
|
}) { |
|
|
|
const Icon = REPORT_ICON_MAP[reportId] ?? Inventory2OutlinedIcon; |
|
|
|
|
|
|
|
return ( |
|
|
|
<Box |
|
|
|
component="button" |
|
|
|
type="button" |
|
|
|
onClick={onClick} |
|
|
|
sx={{ |
|
|
|
display: "flex", |
|
|
|
alignItems: "center", |
|
|
|
gap: 1.5, |
|
|
|
width: "100%", |
|
|
|
p: 1.5, |
|
|
|
textAlign: "left", |
|
|
|
cursor: "pointer", |
|
|
|
border: "1px solid", |
|
|
|
borderColor: selected ? accent : "divider", |
|
|
|
borderRadius: 1.5, |
|
|
|
bgcolor: "background.paper", |
|
|
|
boxShadow: selected ? 2 : "0 1px 3px rgba(0,0,0,0.06)", |
|
|
|
transition: "border-color 0.15s, box-shadow 0.15s", |
|
|
|
"&:hover": { |
|
|
|
borderColor: accent, |
|
|
|
boxShadow: 2, |
|
|
|
}, |
|
|
|
}} |
|
|
|
> |
|
|
|
<Box |
|
|
|
sx={{ |
|
|
|
display: "flex", |
|
|
|
alignItems: "center", |
|
|
|
justifyContent: "center", |
|
|
|
width: 40, |
|
|
|
height: 40, |
|
|
|
borderRadius: 1, |
|
|
|
bgcolor: `${accent}18`, |
|
|
|
color: accent, |
|
|
|
flexShrink: 0, |
|
|
|
}} |
|
|
|
> |
|
|
|
<Icon fontSize="small" /> |
|
|
|
</Box> |
|
|
|
<Typography variant="body2" fontWeight={selected ? 600 : 400} sx={{ lineHeight: 1.35 }}> |
|
|
|
{title} |
|
|
|
</Typography> |
|
|
|
</Box> |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
function CategoryColumn({ |
|
|
|
category, |
|
|
|
selectedReportId, |
|
|
|
onSelectReport, |
|
|
|
}: { |
|
|
|
category: ReportCategoryConfig; |
|
|
|
selectedReportId: string; |
|
|
|
onSelectReport: (reportId: string) => void; |
|
|
|
}) { |
|
|
|
const reports = category.reportIds |
|
|
|
.map((id) => reportById[id]) |
|
|
|
.filter(Boolean); |
|
|
|
|
|
|
|
return ( |
|
|
|
<Box |
|
|
|
sx={{ |
|
|
|
display: "flex", |
|
|
|
flexDirection: "column", |
|
|
|
height: "100%", |
|
|
|
borderRadius: 1.5, |
|
|
|
overflow: "hidden", |
|
|
|
border: "1px solid", |
|
|
|
borderColor: "divider", |
|
|
|
}} |
|
|
|
> |
|
|
|
<Box |
|
|
|
sx={{ |
|
|
|
px: 2, |
|
|
|
py: 1.25, |
|
|
|
bgcolor: category.headerBg, |
|
|
|
}} |
|
|
|
> |
|
|
|
<Typography variant="subtitle1" fontWeight="bold"> |
|
|
|
{category.title} |
|
|
|
</Typography> |
|
|
|
</Box> |
|
|
|
<Box |
|
|
|
sx={{ |
|
|
|
flex: 1, |
|
|
|
p: 1.5, |
|
|
|
bgcolor: category.bodyBg, |
|
|
|
}} |
|
|
|
> |
|
|
|
<Grid container spacing={1.5}> |
|
|
|
{reports.map((report) => ( |
|
|
|
<Grid item xs={6} key={report.id}> |
|
|
|
<ReportCard |
|
|
|
reportId={report.id} |
|
|
|
title={report.title} |
|
|
|
accent={category.accent} |
|
|
|
selected={selectedReportId === report.id} |
|
|
|
onClick={() => onSelectReport(report.id)} |
|
|
|
/> |
|
|
|
</Grid> |
|
|
|
))} |
|
|
|
</Grid> |
|
|
|
</Box> |
|
|
|
</Box> |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
export default function ReportSelectionDashboard({ |
|
|
|
selectedReportId, |
|
|
|
onSelectReport, |
|
|
|
}: ReportSelectionDashboardProps) { |
|
|
|
return ( |
|
|
|
<Card sx={{ mb: 4, boxShadow: 2, border: "1px solid", borderColor: "divider" }}> |
|
|
|
<CardContent sx={{ p: { xs: 2, sm: 3 } }}> |
|
|
|
<Grid container spacing={2}> |
|
|
|
{REPORT_CATEGORIES.map((category) => ( |
|
|
|
<Grid item xs={12} md={4} key={category.id}> |
|
|
|
<CategoryColumn |
|
|
|
category={category} |
|
|
|
selectedReportId={selectedReportId} |
|
|
|
onSelectReport={onSelectReport} |
|
|
|
/> |
|
|
|
</Grid> |
|
|
|
))} |
|
|
|
</Grid> |
|
|
|
</CardContent> |
|
|
|
</Card> |
|
|
|
); |
|
|
|
} |