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.

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