diff --git a/src/app/(main)/analytics/DelayReport/page.tsx b/src/app/(main)/analytics/DelayReport/page.tsx
index 154fd58..64c1105 100644
--- a/src/app/(main)/analytics/DelayReport/page.tsx
+++ b/src/app/(main)/analytics/DelayReport/page.tsx
@@ -1,8 +1,8 @@
-//src\app\(main)\analytics\LateStartReport\page.tsx
+//src\app\(main)\analytics\DelayReport\page.tsx
import { Metadata } from "next";
import { I18nProvider } from "@/i18n";
import Typography from "@mui/material/Typography";
-import LateStartReportComponent from "@/components/LateStartReport";
+import DelayReportComponent from "@/components/Report/DelayReport";
export const metadata: Metadata = {
title: "Project Status by Client",
@@ -17,7 +17,7 @@ const ProjectLateReport: React.FC = () => {
{/* }>
*/}
-
+
);
};
diff --git a/src/app/api/report2/index.ts b/src/app/api/report2/index.ts
new file mode 100644
index 0000000..e8cc14c
--- /dev/null
+++ b/src/app/api/report2/index.ts
@@ -0,0 +1,42 @@
+//src\app\api\report\index.ts
+import { cache } from "react";
+
+export interface Delay {
+ id: number;
+ projectCode: string;
+ projectName: string;
+ team: string;
+ teamLeader: string;
+ startDate: string;
+ startDateFrom: string;
+ startDateTo: string;
+ targetEndDate: string;
+ client: string;
+ subsidiary: string;
+ status: string;
+}
+
+export const preloadProjects = () => {
+ fetchProjectsDelay();
+};
+
+export const fetchProjectsDelay = cache(async () => {
+ return mockProjects;
+});
+
+const mockProjects: Delay[] = [
+ {
+ id: 1,
+ projectCode: "CUST-001",
+ projectName: "Client A",
+ team: "N/A",
+ teamLeader: "N/A",
+ startDate: "5",
+ startDateFrom: "5",
+ startDateTo: "5",
+ targetEndDate: "s",
+ client: "ss",
+ subsidiary: "ss",
+ status: "1",
+ },
+];
diff --git a/src/components/Report/DelayReport/DelayReport.tsx b/src/components/Report/DelayReport/DelayReport.tsx
new file mode 100644
index 0000000..c956fa7
--- /dev/null
+++ b/src/components/Report/DelayReport/DelayReport.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 DelayReportGen from "@/components/Report/DelayReportGen";
+
+const DelayReport: React.FC = () => {
+
+ return (
+ }>
+
+
+ );
+};
+
+export default DelayReport;
\ No newline at end of file
diff --git a/src/components/Report/DelayReport/index.ts b/src/components/Report/DelayReport/index.ts
new file mode 100644
index 0000000..bbcc1e5
--- /dev/null
+++ b/src/components/Report/DelayReport/index.ts
@@ -0,0 +1,2 @@
+//src\components\LateStartReport\index.ts
+export { default } from "./DelayReport";
diff --git a/src/components/Report/DelayReportGen/DelayReportGen.tsx b/src/components/Report/DelayReportGen/DelayReportGen.tsx
new file mode 100644
index 0000000..4155613
--- /dev/null
+++ b/src/components/Report/DelayReportGen/DelayReportGen.tsx
@@ -0,0 +1,45 @@
+//src\components\LateStartReportGen\LateStartReportGen.tsx
+"use client";
+import React, { useMemo, useState } from "react";
+import SearchBox, { Criterion } from "../../ReportSearchBox2";
+import { useTranslation } from "react-i18next";
+import { Delay } from "@/app/api/report2";
+//import { DownloadReportButton } from './DownloadReportButton';
+interface Props {
+ projects: Delay[];
+}
+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: "text" },
+ { label: "Client", paramName: "client", type: "text" },
+ { label: "Status", paramName: "status", type: "select", options: ["Delayed", "Potential Delay"] },
+ // {
+ // label: "Status",
+ // label2: "Remained Date To",
+ // paramName: "targetEndDate",
+ // type: "dateRange",
+ // },
+ ],
+ [t],
+ );
+
+ return (
+ <>
+ {
+ console.log(query);
+ }}
+ />
+ {/* */}
+ >
+ );
+};
+
+export default ProgressByClientSearch;
diff --git a/src/components/Report/DelayReportGen/DelayReportGenLoading.tsx b/src/components/Report/DelayReportGen/DelayReportGenLoading.tsx
new file mode 100644
index 0000000..9b0341d
--- /dev/null
+++ b/src/components/Report/DelayReportGen/DelayReportGenLoading.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/DelayReportGen/DelayReportGenWrapper.tsx b/src/components/Report/DelayReportGen/DelayReportGenWrapper.tsx
new file mode 100644
index 0000000..1a6caa3
--- /dev/null
+++ b/src/components/Report/DelayReportGen/DelayReportGenWrapper.tsx
@@ -0,0 +1,19 @@
+//src\components\LateStartReportGen\LateStartReportGenWrapper.tsx
+import { fetchProjectsDelay } from "@/app/api/report2";
+import React from "react";
+import DelayReportGen from "./DelayReportGen";
+import DelayReportGenLoading from "./DelayReportGenLoading";
+
+interface SubComponents {
+ Loading: typeof DelayReportGenLoading;
+}
+
+const DelayReportGenWrapper: React.FC & SubComponents = async () => {
+ const clentprojects = await fetchProjectsDelay();
+
+ return ;
+};
+
+DelayReportGenWrapper.Loading = DelayReportGenLoading;
+
+export default DelayReportGenWrapper;
\ No newline at end of file
diff --git a/src/components/Report/DelayReportGen/index.ts b/src/components/Report/DelayReportGen/index.ts
new file mode 100644
index 0000000..0c49846
--- /dev/null
+++ b/src/components/Report/DelayReportGen/index.ts
@@ -0,0 +1,2 @@
+//src\components\DelayReportGen\index.ts
+export { default } from "./DelayReportGenWrapper";
diff --git a/src/components/ReportSearchBox2/SearchBox2.tsx b/src/components/ReportSearchBox2/SearchBox2.tsx
new file mode 100644
index 0000000..16b72df
--- /dev/null
+++ b/src/components/ReportSearchBox2/SearchBox2.tsx
@@ -0,0 +1,300 @@
+//src\components\ReportSearchBox\SearchBox2.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/AR02_Delay 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 K6
+ // Apply styles from A6 to K6 (bold, bottom border, center alignment)
+ for (let col = 0; col < 11; 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 = `AR02_Delay_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" && (
+
+
+
+
+
+
+ {"-"}
+
+
+
+
+
+
+ )}
+
+ );
+ })}
+
+
+ }
+ onClick={handleReset}
+ >
+ {t("Reset")}
+
+ }
+ onClick={handleDownload}
+ >
+ {t("Download Report")}
+
+
+
+
+ );
+}
+
+export default SearchBox;
diff --git a/src/components/ReportSearchBox2/index.ts b/src/components/ReportSearchBox2/index.ts
new file mode 100644
index 0000000..27210b3
--- /dev/null
+++ b/src/components/ReportSearchBox2/index.ts
@@ -0,0 +1,3 @@
+//src\components\SearchBox\index.ts
+export { default } from "./SearchBox2";
+export type { Criterion } from "./SearchBox2";