FPSMS-frontend
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

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