Browse Source

Report Page UI Update

production
B.E.N.S.O.N 2 weeks ago
parent
commit
22614b63e5
3 changed files with 237 additions and 26 deletions
  1. +188
    -0
      src/app/(main)/report/ReportSelectionDashboard.tsx
  2. +11
    -26
      src/app/(main)/report/page.tsx
  3. +38
    -0
      src/app/(main)/report/reportCategories.ts

+ 188
- 0
src/app/(main)/report/ReportSelectionDashboard.tsx View File

@@ -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>
);
}

+ 11
- 26
src/app/(main)/report/page.tsx View File

@@ -24,6 +24,7 @@ import { REPORTS, ReportDefinition } from '@/config/reportConfig';
import { NEXT_PUBLIC_API_URL } from '@/config/api';
import { clientAuthFetch } from '@/app/utils/clientAuthFetch';
import SemiFGProductionAnalysisReport from './SemiFGProductionAnalysisReport';
import ReportSelectionDashboard from './ReportSelectionDashboard';
import {
fetchSemiFGItemCodes,
fetchSemiFGItemCodesWithCategory
@@ -66,9 +67,10 @@ export default function ReportPage() {
REPORTS.find((r) => r.id === selectedReportId),
[selectedReportId]);

const handleReportChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setSelectedReportId(event.target.value);
setCriteria({}); // Clear criteria when switching reports
const handleSelectReport = (reportId: string) => {
if (reportId === selectedReportId) return;
setSelectedReportId(reportId);
setCriteria({});
};

const handleFieldChange = (name: string, value: string | string[]) => {
@@ -368,32 +370,15 @@ export default function ReportPage() {
};

return (
<Box sx={{ p: 4, maxWidth: 1000, margin: '0 auto' }}>
<Box sx={{ p: 4, maxWidth: 1280, margin: '0 auto' }}>
<Typography variant="h4" gutterBottom fontWeight="bold">
報告管理
</Typography>
<Card sx={{ mb: 4, boxShadow: 3 }}>
<CardContent>
<Typography variant="h6" gutterBottom>
選擇報告
</Typography>
<TextField
select
fullWidth
label="報告列表"
value={selectedReportId}
onChange={handleReportChange}
helperText="選擇報告"
>
{REPORTS.map((report) => (
<MenuItem key={report.id} value={report.id}>
{report.title}
</MenuItem>
))}
</TextField>
</CardContent>
</Card>

<ReportSelectionDashboard
selectedReportId={selectedReportId}
onSelectReport={handleSelectReport}
/>

{currentReport && (
<Card sx={{ boxShadow: 3, animation: 'fadeIn 0.5s' }}>


+ 38
- 0
src/app/(main)/report/reportCategories.ts View File

@@ -0,0 +1,38 @@
export type ReportCategoryId = "inventory" | "inbound-outbound" | "production";

export interface ReportCategoryConfig {
id: ReportCategoryId;
title: string;
headerBg: string;
bodyBg: string;
accent: string;
reportIds: string[];
}

/** Display order and grouping for the report management dashboard. */
export const REPORT_CATEGORIES: ReportCategoryConfig[] = [
{
id: "inventory",
title: "庫存管理",
headerBg: "#b8e0b8",
bodyBg: "#eef8ee",
accent: "#2e7d32",
reportIds: ["rep-011", "rep-007", "rep-012", "rep-010"],
},
{
id: "inbound-outbound",
title: "出入倉作業",
headerBg: "#b3d4f0",
bodyBg: "#eef5fc",
accent: "#1565c0",
reportIds: ["rep-004", "rep-014", "rep-008", "rep-009", "rep-013"],
},
{
id: "production",
title: "生產與趨勢",
headerBg: "#f5d4a8",
bodyBg: "#fdf6ec",
accent: "#e65100",
reportIds: ["rep-006", "rep-005"],
},
];

Loading…
Cancel
Save