From 2e0d69e80444612f4b02c32867b10ce83e4f0b10 Mon Sep 17 00:00:00 2001 From: leoho2fi Date: Tue, 23 Apr 2024 18:07:43 +0800 Subject: [PATCH] add report ar04 --- public/temp/AR04_Cost and Expense Report.xlsx | Bin 0 -> 12817 bytes .../analytics/CostandExpenseReport/page.tsx | 10 +- src/app/api/report4/index.ts | 42 +++ .../CostandExpenseReport.tsx | 17 + .../Report/CostandExpenseReport/index.ts | 2 + .../CostandExpenseReportGen.tsx | 45 +++ .../CostandExpenseReportGenLoading.tsx | 41 +++ .../CostandExpenseReportGenWrapper.tsx | 19 ++ .../Report/CostandExpenseReportGen/index.ts | 2 + .../ReportSearchBox4/SearchBox4.tsx | 302 ++++++++++++++++++ src/components/ReportSearchBox4/index.ts | 3 + 11 files changed, 478 insertions(+), 5 deletions(-) create mode 100644 public/temp/AR04_Cost and Expense Report.xlsx create mode 100644 src/app/api/report4/index.ts create mode 100644 src/components/Report/CostandExpenseReport/CostandExpenseReport.tsx create mode 100644 src/components/Report/CostandExpenseReport/index.ts create mode 100644 src/components/Report/CostandExpenseReportGen/CostandExpenseReportGen.tsx create mode 100644 src/components/Report/CostandExpenseReportGen/CostandExpenseReportGenLoading.tsx create mode 100644 src/components/Report/CostandExpenseReportGen/CostandExpenseReportGenWrapper.tsx create mode 100644 src/components/Report/CostandExpenseReportGen/index.ts create mode 100644 src/components/ReportSearchBox4/SearchBox4.tsx create mode 100644 src/components/ReportSearchBox4/index.ts diff --git a/public/temp/AR04_Cost and Expense Report.xlsx b/public/temp/AR04_Cost and Expense Report.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..a414e1afcda8192ce80e8ffcf809f49378b80ceb GIT binary patch literal 12817 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 K{PV1T-2Fd_QUOo^ literal 0 HcmV?d00001 diff --git a/src/app/(main)/analytics/CostandExpenseReport/page.tsx b/src/app/(main)/analytics/CostandExpenseReport/page.tsx index 2f046c3..5cd328b 100644 --- a/src/app/(main)/analytics/CostandExpenseReport/page.tsx +++ b/src/app/(main)/analytics/CostandExpenseReport/page.tsx @@ -1,14 +1,14 @@ -//src\app\(main)\analytics\LateStartReport\page.tsx +//src\app\(main)\analytics\CostandExpenseReport\page.tsx import { Metadata } from "next"; import { I18nProvider } from "@/i18n"; import Typography from "@mui/material/Typography"; -import LateStartReportComponent from "@/components/LateStartReport"; +import CostandExpenseReportComponent from "@/components/Report/CostandExpenseReport"; export const metadata: Metadata = { title: "Project Status by Client", }; -const ProjectLateReport: React.FC = () => { +const CostandExpenseReport: React.FC = () => { return ( @@ -17,8 +17,8 @@ const ProjectLateReport: React.FC = () => { {/* }> */} - + ); }; -export default ProjectLateReport; +export default CostandExpenseReport; diff --git a/src/app/api/report4/index.ts b/src/app/api/report4/index.ts new file mode 100644 index 0000000..5117519 --- /dev/null +++ b/src/app/api/report4/index.ts @@ -0,0 +1,42 @@ +//src\app\api\report\index.ts +import { cache } from "react"; + +export interface CostandExpense { + id: number; + projectCode: string; + projectName: string; + team: string; + teamLeader: string; + startDate: string; + startDateFrom: string; + startDateTo: string; + targetEndDate: string; + client: string; + subsidiary: string; + remainPercent: string; +} + +export const preloadProjects = () => { + fetchProjectsCostandExpense(); +}; + +export const fetchProjectsCostandExpense = cache(async () => { + return mockProjects; +}); + +const mockProjects: CostandExpense[] = [ + { + id: 1, + projectCode: "CUST-001", + projectName: "Client A", + team: "N/A", + teamLeader: "N/A", + startDate: "1/2/2024", + startDateFrom: "1/2/2024", + startDateTo: "1/2/2024", + targetEndDate: "30/3/2024", + client: "ss", + subsidiary: "sus", + remainPercent: "1", + }, +]; diff --git a/src/components/Report/CostandExpenseReport/CostandExpenseReport.tsx b/src/components/Report/CostandExpenseReport/CostandExpenseReport.tsx new file mode 100644 index 0000000..6c43a0a --- /dev/null +++ b/src/components/Report/CostandExpenseReport/CostandExpenseReport.tsx @@ -0,0 +1,17 @@ +//src\components\DelayReport\DelayReport.tsx +"use client"; +import * as React from "react"; +import "../../../app/global.css"; +import { Suspense } from "react"; +import CostandExpenseReportGen from "@/components/Report/CostandExpenseReportGen"; + +const CostandExpenseReport: React.FC = () => { + + return ( + }> + + + ); +}; + +export default CostandExpenseReport; \ No newline at end of file diff --git a/src/components/Report/CostandExpenseReport/index.ts b/src/components/Report/CostandExpenseReport/index.ts new file mode 100644 index 0000000..0dd1e51 --- /dev/null +++ b/src/components/Report/CostandExpenseReport/index.ts @@ -0,0 +1,2 @@ +//src\components\LateStartReport\index.ts +export { default } from "./CostandExpenseReport"; diff --git a/src/components/Report/CostandExpenseReportGen/CostandExpenseReportGen.tsx b/src/components/Report/CostandExpenseReportGen/CostandExpenseReportGen.tsx new file mode 100644 index 0000000..eea61cf --- /dev/null +++ b/src/components/Report/CostandExpenseReportGen/CostandExpenseReportGen.tsx @@ -0,0 +1,45 @@ +//src\components\LateStartReportGen\LateStartReportGen.tsx +"use client"; +import React, { useMemo, useState } from "react"; +import SearchBox, { Criterion } from "../../ReportSearchBox4"; +import { useTranslation } from "react-i18next"; +import { CostandExpense } from "@/app/api/report4"; + +interface Props { + projects: CostandExpense[]; +} +type SearchQuery = Partial>; +type SearchParamNames = keyof SearchQuery; + +const ProgressByClientSearch: React.FC = ({ projects }) => { + const { t } = useTranslation("projects"); + + const searchCriteria: Criterion[] = useMemo( + () => [ + { label: "Team", paramName: "team", type: "select", options: ["AAA", "BBB", "CCC"] }, + { label: "Client", paramName: "client", type: "select", options: ["Cust A", "Cust B", "Cust C"] }, + { label: "Remaining Percentage", paramName: "remainPercent", type: "select", options: ["<50%", "50%-70%", ">70%"] }, + // { + // label: "Status", + // label2: "Remained Date To", + // paramName: "targetEndDate", + // type: "dateRange", + // }, + ], + [t], + ); + + return ( + <> + { + console.log(query); + }} + /> + {/* */} + + ); +}; + +export default ProgressByClientSearch; diff --git a/src/components/Report/CostandExpenseReportGen/CostandExpenseReportGenLoading.tsx b/src/components/Report/CostandExpenseReportGen/CostandExpenseReportGenLoading.tsx new file mode 100644 index 0000000..9b0341d --- /dev/null +++ b/src/components/Report/CostandExpenseReportGen/CostandExpenseReportGenLoading.tsx @@ -0,0 +1,41 @@ +//src\components\LateStartReportGen\LateStartReportGenLoading.tsx +import Card from "@mui/material/Card"; +import CardContent from "@mui/material/CardContent"; +import Skeleton from "@mui/material/Skeleton"; +import Stack from "@mui/material/Stack"; +import React from "react"; + +// Can make this nicer +export const DelayReportGenLoading: React.FC = () => { + return ( + <> + + + + + + + + + + + + + + + + + + + + + + ); +}; + +export default DelayReportGenLoading; diff --git a/src/components/Report/CostandExpenseReportGen/CostandExpenseReportGenWrapper.tsx b/src/components/Report/CostandExpenseReportGen/CostandExpenseReportGenWrapper.tsx new file mode 100644 index 0000000..b11b808 --- /dev/null +++ b/src/components/Report/CostandExpenseReportGen/CostandExpenseReportGenWrapper.tsx @@ -0,0 +1,19 @@ +//src\components\LateStartReportGen\LateStartReportGenWrapper.tsx +import { fetchProjectsCostandExpense } from "@/app/api/report4"; +import React from "react"; +import CostandExpenseReportGen from "./CostandExpenseReportGen"; +import CostandExpenseReportGenLoading from "./CostandExpenseReportGenLoading"; + +interface SubComponents { + Loading: typeof CostandExpenseReportGenLoading; +} + +const CostandExpenseReportGenWrapper: React.FC & SubComponents = async () => { + const clentprojects = await fetchProjectsCostandExpense(); + + return ; +}; + +CostandExpenseReportGenWrapper.Loading = CostandExpenseReportGenLoading; + +export default CostandExpenseReportGenWrapper; \ No newline at end of file diff --git a/src/components/Report/CostandExpenseReportGen/index.ts b/src/components/Report/CostandExpenseReportGen/index.ts new file mode 100644 index 0000000..0ef6085 --- /dev/null +++ b/src/components/Report/CostandExpenseReportGen/index.ts @@ -0,0 +1,2 @@ +//src\components\DelayReportGen\index.ts +export { default } from "./CostandExpenseReportGenWrapper"; diff --git a/src/components/ReportSearchBox4/SearchBox4.tsx b/src/components/ReportSearchBox4/SearchBox4.tsx new file mode 100644 index 0000000..8b9dd4d --- /dev/null +++ b/src/components/ReportSearchBox4/SearchBox4.tsx @@ -0,0 +1,302 @@ +//src\components\ReportSearchBox3\SearchBox3.tsx +"use client"; + +import Grid from "@mui/material/Grid"; +import Card from "@mui/material/Card"; +import CardContent from "@mui/material/CardContent"; +import Typography from "@mui/material/Typography"; +import React, { useCallback, useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; +import TextField from "@mui/material/TextField"; +import FormControl from "@mui/material/FormControl"; +import InputLabel from "@mui/material/InputLabel"; +import Select, { SelectChangeEvent } from "@mui/material/Select"; +import MenuItem from "@mui/material/MenuItem"; +import CardActions from "@mui/material/CardActions"; +import Button from "@mui/material/Button"; +import RestartAlt from "@mui/icons-material/RestartAlt"; +import Search from "@mui/icons-material/Search"; +import dayjs from "dayjs"; +import "dayjs/locale/zh-hk"; +import { DatePicker } from "@mui/x-date-pickers/DatePicker"; +import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; +import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; +import { Box } from "@mui/material"; +import * as XLSX from 'xlsx-js-style'; +//import { DownloadReportButton } from '../LateStartReportGen/DownloadReportButton'; + +interface BaseCriterion { + label: string; + label2?: string; + paramName: T; + paramName2?: T; +} + +interface TextCriterion extends BaseCriterion { + type: "text"; +} + +interface SelectCriterion extends BaseCriterion { + type: "select"; + options: string[]; +} + +interface DateRangeCriterion extends BaseCriterion { + type: "dateRange"; +} + +export type Criterion = + | TextCriterion + | SelectCriterion + | DateRangeCriterion; + +interface Props { + criteria: Criterion[]; + onSearch: (inputs: Record) => void; + onReset?: () => void; +} + +function SearchBox({ + criteria, + onSearch, + onReset, +}: Props) { + const { t } = useTranslation("common"); + const defaultInputs = useMemo( + () => + criteria.reduce>( + (acc, c) => { + return { ...acc, [c.paramName]: c.type === "select" ? "All" : "" }; + }, + {} as Record, + ), + [criteria], + ); + const [inputs, setInputs] = useState(defaultInputs); + + const makeInputChangeHandler = useCallback( + (paramName: T): React.ChangeEventHandler => { + return (e) => { + setInputs((i) => ({ ...i, [paramName]: e.target.value })); + }; + }, + [], + ); + + const makeSelectChangeHandler = useCallback((paramName: T) => { + return (e: SelectChangeEvent) => { + setInputs((i) => ({ ...i, [paramName]: e.target.value })); + }; + }, []); + + const makeDateChangeHandler = useCallback((paramName: T) => { + return (e: any) => { + setInputs((i) => ({ ...i, [paramName]: dayjs(e).format("YYYY-MM-DD") })); + }; + }, []); + + const makeDateToChangeHandler = useCallback((paramName: T) => { + return (e: any) => { + setInputs((i) => ({ + ...i, + [paramName + "To"]: dayjs(e).format("YYYY-MM-DD"), + })); + }; + }, []); + + const handleReset = () => { + setInputs(defaultInputs); + onReset?.(); + }; + + const handleSearch = () => { + onSearch(inputs); + + }; + + const handleDownload = async () => { + //setIsLoading(true); + + try { + const response = await fetch('/temp/AR04_Cost and Expense Report.xlsx', { + headers: { + 'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + }, + }); + if (!response.ok) throw new Error('Network response was not ok.'); + + const data = await response.blob(); + const reader = new FileReader(); + reader.onload = (e) => { + if (e.target && e.target.result) { + const ab = e.target.result as ArrayBuffer; + const workbook = XLSX.read(ab, { type: 'array' }); + const firstSheetName = workbook.SheetNames[0]; + const worksheet = workbook.Sheets[firstSheetName]; + + // Add the current date to cell C2 + const cellAddress = 'C2'; + const date = new Date().toISOString().split('T')[0]; // Format YYYY-MM-DD + const formattedDate = date.replace(/-/g, '/'); // Change format to YYYY/MM/DD + XLSX.utils.sheet_add_aoa(worksheet, [[formattedDate]], { origin: cellAddress }); + + // Calculate the maximum length of content in each column and set column width + const colWidths: number[] = []; + + const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1, defval: "", blankrows: true }) as (string | number)[][]; + jsonData.forEach((row: (string | number)[]) => { + row.forEach((cell: string | number, index: number) => { + const valueLength = cell.toString().length; + colWidths[index] = Math.max(colWidths[index] || 0, valueLength); + }); + }); + + // Apply calculated widths to each column, skipping column A + worksheet['!cols'] = colWidths.map((width, index) => { + if (index === 0) { + return { wch: 8 }; // Set default or specific width for column A if needed + } + return { wch: width + 2 }; // Add padding to width + }); + + // Style for cell A1: Font size 16 and bold + if (worksheet['A1']) { + worksheet['A1'].s = { + font: { + bold: true, + sz: 16, // Font size 16 + //name: 'Times New Roman' // Specify font + } + }; + } + + // Apply styles from A2 to A4 (bold) + ['A2', 'A3', 'A4'].forEach(cell => { + if (worksheet[cell]) { + worksheet[cell].s = { font: { bold: true } }; + } + }); + + // Formatting from A6 to J6 + // Apply styles from A6 to J6 (bold, bottom border, center alignment) + for (let col = 0; col < 10; col++) { // Columns A to K + const cellRef = XLSX.utils.encode_col(col) + '6'; + if (worksheet[cellRef]) { + worksheet[cellRef].s = { + font: { bold: true }, + alignment: { horizontal: 'center' }, + border: { + bottom: { style: 'thin', color: { auto: 1 } } + } + }; + } + } + + // Format filename with date + const today = new Date().toISOString().split('T')[0].replace(/-/g, '_'); // Get current date and format as YYYY_MM_DD + const filename = `AR04_Cost_and_Expense_Report_${today}.xlsx`; // Append formatted date to the filename + + // Convert workbook back to XLSX file + XLSX.writeFile(workbook, filename); + } else { + throw new Error('Failed to load file'); + } + }; + reader.readAsArrayBuffer(data); + } catch (error) { + console.error('Error downloading the file: ', error); + } + + //setIsLoading(false); + }; + return ( + + + {t("Search Criteria")} + + {criteria.map((c) => { + return ( + + {c.type === "text" && ( + + )} + {c.type === "select" && ( + + {c.label} + + + )} + {c.type === "dateRange" && ( + + + + + + + {"-"} + + + + + + + )} + + ); + })} + + + + + + + + ); +} + +export default SearchBox; diff --git a/src/components/ReportSearchBox4/index.ts b/src/components/ReportSearchBox4/index.ts new file mode 100644 index 0000000..0e518ff --- /dev/null +++ b/src/components/ReportSearchBox4/index.ts @@ -0,0 +1,3 @@ +//src\components\SearchBox\index.ts +export { default } from "./SearchBox4"; +export type { Criterion } from "./SearchBox4";