FPSMS-frontend
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

193 line
5.1 KiB

  1. "use client";
  2. import React from "react";
  3. import {
  4. Box,
  5. Card,
  6. CardContent,
  7. Grid,
  8. Typography,
  9. } from "@mui/material";
  10. import Inventory2OutlinedIcon from "@mui/icons-material/Inventory2Outlined";
  11. import MonetizationOnOutlinedIcon from "@mui/icons-material/MonetizationOnOutlined";
  12. import LayersOutlinedIcon from "@mui/icons-material/LayersOutlined";
  13. import SearchOutlinedIcon from "@mui/icons-material/SearchOutlined";
  14. import LocalShippingOutlinedIcon from "@mui/icons-material/LocalShippingOutlined";
  15. import OutboundOutlinedIcon from "@mui/icons-material/OutboundOutlined";
  16. import BarChartOutlinedIcon from "@mui/icons-material/BarChartOutlined";
  17. import PieChartOutlineOutlinedIcon from "@mui/icons-material/PieChartOutlineOutlined";
  18. import type { SvgIconComponent } from "@mui/icons-material";
  19. import { REPORTS } from "@/config/reportConfig";
  20. import { REPORT_CATEGORIES, type ReportCategoryConfig } from "./reportCategories";
  21. const REPORT_ICON_MAP: Record<string, SvgIconComponent> = {
  22. "rep-011": Inventory2OutlinedIcon,
  23. "rep-007": MonetizationOnOutlinedIcon,
  24. "rep-012": LayersOutlinedIcon,
  25. "rep-010": SearchOutlinedIcon,
  26. "rep-004": LocalShippingOutlinedIcon,
  27. "rep-014": LocalShippingOutlinedIcon,
  28. "rep-008": OutboundOutlinedIcon,
  29. "rep-009": OutboundOutlinedIcon,
  30. "rep-013": LocalShippingOutlinedIcon,
  31. "rep-016": OutboundOutlinedIcon,
  32. "rep-017": LocalShippingOutlinedIcon,
  33. "rep-018": SearchOutlinedIcon,
  34. "rep-006": BarChartOutlinedIcon,
  35. "rep-005": PieChartOutlineOutlinedIcon,
  36. "rep-015": LayersOutlinedIcon,
  37. };
  38. const reportById = Object.fromEntries(REPORTS.map((r) => [r.id, r]));
  39. interface ReportSelectionDashboardProps {
  40. selectedReportId: string;
  41. onSelectReport: (reportId: string) => void;
  42. }
  43. function ReportCard({
  44. reportId,
  45. title,
  46. accent,
  47. selected,
  48. onClick,
  49. }: {
  50. reportId: string;
  51. title: string;
  52. accent: string;
  53. selected: boolean;
  54. onClick: () => void;
  55. }) {
  56. const Icon = REPORT_ICON_MAP[reportId] ?? Inventory2OutlinedIcon;
  57. return (
  58. <Box
  59. component="button"
  60. type="button"
  61. onClick={onClick}
  62. sx={{
  63. display: "flex",
  64. alignItems: "center",
  65. gap: 1.5,
  66. width: "100%",
  67. p: 1.5,
  68. textAlign: "left",
  69. cursor: "pointer",
  70. border: "1px solid",
  71. borderColor: selected ? accent : "divider",
  72. borderRadius: 1.5,
  73. bgcolor: "background.paper",
  74. boxShadow: selected ? 2 : "0 1px 3px rgba(0,0,0,0.06)",
  75. transition: "border-color 0.15s, box-shadow 0.15s",
  76. "&:hover": {
  77. borderColor: accent,
  78. boxShadow: 2,
  79. },
  80. }}
  81. >
  82. <Box
  83. sx={{
  84. display: "flex",
  85. alignItems: "center",
  86. justifyContent: "center",
  87. width: 40,
  88. height: 40,
  89. borderRadius: 1,
  90. bgcolor: `${accent}18`,
  91. color: accent,
  92. flexShrink: 0,
  93. }}
  94. >
  95. <Icon fontSize="small" />
  96. </Box>
  97. <Typography variant="body2" fontWeight={selected ? 600 : 400} sx={{ lineHeight: 1.35 }}>
  98. {title}
  99. </Typography>
  100. </Box>
  101. );
  102. }
  103. function CategoryColumn({
  104. category,
  105. selectedReportId,
  106. onSelectReport,
  107. }: {
  108. category: ReportCategoryConfig;
  109. selectedReportId: string;
  110. onSelectReport: (reportId: string) => void;
  111. }) {
  112. const reports = category.reportIds
  113. .map((id) => reportById[id])
  114. .filter(Boolean);
  115. return (
  116. <Box
  117. sx={{
  118. display: "flex",
  119. flexDirection: "column",
  120. height: "100%",
  121. borderRadius: 1.5,
  122. overflow: "hidden",
  123. border: "1px solid",
  124. borderColor: "divider",
  125. }}
  126. >
  127. <Box
  128. sx={{
  129. px: 2,
  130. py: 1.25,
  131. bgcolor: category.headerBg,
  132. }}
  133. >
  134. <Typography variant="subtitle1" fontWeight="bold">
  135. {category.title}
  136. </Typography>
  137. </Box>
  138. <Box
  139. sx={{
  140. flex: 1,
  141. p: 1.5,
  142. bgcolor: category.bodyBg,
  143. }}
  144. >
  145. <Grid container spacing={1.5}>
  146. {reports.map((report) => (
  147. <Grid item xs={6} key={report.id}>
  148. <ReportCard
  149. reportId={report.id}
  150. title={report.title}
  151. accent={category.accent}
  152. selected={selectedReportId === report.id}
  153. onClick={() => onSelectReport(report.id)}
  154. />
  155. </Grid>
  156. ))}
  157. </Grid>
  158. </Box>
  159. </Box>
  160. );
  161. }
  162. export default function ReportSelectionDashboard({
  163. selectedReportId,
  164. onSelectReport,
  165. }: ReportSelectionDashboardProps) {
  166. return (
  167. <Card sx={{ mb: 4, boxShadow: 2, border: "1px solid", borderColor: "divider" }}>
  168. <CardContent sx={{ p: { xs: 2, sm: 3 } }}>
  169. <Grid container spacing={2}>
  170. {REPORT_CATEGORIES.map((category) => (
  171. <Grid item xs={12} md={4} key={category.id}>
  172. <CategoryColumn
  173. category={category}
  174. selectedReportId={selectedReportId}
  175. onSelectReport={onSelectReport}
  176. />
  177. </Grid>
  178. ))}
  179. </Grid>
  180. </CardContent>
  181. </Card>
  182. );
  183. }