diff --git a/package.json b/package.json
index 5db5867..2578a2f 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,7 @@
"@emotion/cache": "^11.11.0",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
+ "@faker-js/faker": "^8.4.1",
"@fontsource/inter": "^5.0.16",
"@fontsource/plus-jakarta-sans": "^5.0.18",
"@mui/icons-material": "^5.15.0",
@@ -38,7 +39,8 @@
"react-select": "^5.8.0",
"reactstrap": "^9.2.2",
"styled-components": "^6.1.8",
- "sweetalert2": "^11.10.3"
+ "sweetalert2": "^11.10.3",
+ "xlsx-js-style": "^1.2.0"
},
"devDependencies": {
"@types/lodash": "^4.14.202",
diff --git a/public/temp/AR01_Late Start Report.xlsx b/public/temp/AR01_Late Start Report.xlsx
new file mode 100644
index 0000000..03d6b6b
Binary files /dev/null and b/public/temp/AR01_Late Start Report.xlsx differ
diff --git a/src/app/(main)/analytics/CostandExpenseReport/page.tsx b/src/app/(main)/analytics/CostandExpenseReport/page.tsx
new file mode 100644
index 0000000..2f046c3
--- /dev/null
+++ b/src/app/(main)/analytics/CostandExpenseReport/page.tsx
@@ -0,0 +1,24 @@
+//src\app\(main)\analytics\LateStartReport\page.tsx
+import { Metadata } from "next";
+import { I18nProvider } from "@/i18n";
+import Typography from "@mui/material/Typography";
+import LateStartReportComponent from "@/components/LateStartReport";
+
+export const metadata: Metadata = {
+ title: "Project Status by Client",
+};
+
+const ProjectLateReport: React.FC = () => {
+ return (
+
+
+ Cost and Expense Report
+
+ {/* }>
+
+ */}
+
+
+ );
+};
+export default ProjectLateReport;
diff --git a/src/app/(main)/analytics/DelayReport/page.tsx b/src/app/(main)/analytics/DelayReport/page.tsx
new file mode 100644
index 0000000..154fd58
--- /dev/null
+++ b/src/app/(main)/analytics/DelayReport/page.tsx
@@ -0,0 +1,24 @@
+//src\app\(main)\analytics\LateStartReport\page.tsx
+import { Metadata } from "next";
+import { I18nProvider } from "@/i18n";
+import Typography from "@mui/material/Typography";
+import LateStartReportComponent from "@/components/LateStartReport";
+
+export const metadata: Metadata = {
+ title: "Project Status by Client",
+};
+
+const ProjectLateReport: React.FC = () => {
+ return (
+
+
+ Delay Report
+
+ {/* }>
+
+ */}
+
+
+ );
+};
+export default ProjectLateReport;
diff --git a/src/app/(main)/analytics/LateStartReport/page.tsx b/src/app/(main)/analytics/LateStartReport/page.tsx
new file mode 100644
index 0000000..7f4ade5
--- /dev/null
+++ b/src/app/(main)/analytics/LateStartReport/page.tsx
@@ -0,0 +1,24 @@
+//src\app\(main)\analytics\LateStartReport\page.tsx
+import { Metadata } from "next";
+import { I18nProvider } from "@/i18n";
+import Typography from "@mui/material/Typography";
+import LateStartReportComponent from "@/components/LateStartReport";
+
+export const metadata: Metadata = {
+ title: "Project Status by Client",
+};
+
+const ProjectLateReport: React.FC = () => {
+ return (
+
+
+ Late Start Report
+
+ {/* }>
+
+ */}
+
+
+ );
+};
+export default ProjectLateReport;
diff --git a/src/app/(main)/analytics/ProjectCompletionReport/page.tsx b/src/app/(main)/analytics/ProjectCompletionReport/page.tsx
new file mode 100644
index 0000000..c938efc
--- /dev/null
+++ b/src/app/(main)/analytics/ProjectCompletionReport/page.tsx
@@ -0,0 +1,24 @@
+//src\app\(main)\analytics\LateStartReport\page.tsx
+import { Metadata } from "next";
+import { I18nProvider } from "@/i18n";
+import Typography from "@mui/material/Typography";
+import LateStartReportComponent from "@/components/LateStartReport";
+
+export const metadata: Metadata = {
+ title: "Project Status by Client",
+};
+
+const ProjectLateReport: React.FC = () => {
+ return (
+
+
+ Project Completion Report
+
+ {/* }>
+
+ */}
+
+
+ );
+};
+export default ProjectLateReport;
diff --git a/src/app/(main)/analytics/ProjectCompletionReportWO/page.tsx b/src/app/(main)/analytics/ProjectCompletionReportWO/page.tsx
new file mode 100644
index 0000000..5b914ad
--- /dev/null
+++ b/src/app/(main)/analytics/ProjectCompletionReportWO/page.tsx
@@ -0,0 +1,24 @@
+//src\app\(main)\analytics\LateStartReport\page.tsx
+import { Metadata } from "next";
+import { I18nProvider } from "@/i18n";
+import Typography from "@mui/material/Typography";
+import LateStartReportComponent from "@/components/LateStartReport";
+
+export const metadata: Metadata = {
+ title: "Project Status by Client",
+};
+
+const ProjectLateReport: React.FC = () => {
+ return (
+
+
+ Project Completion Report with Outstanding Un-billed Hours
+
+ {/* }>
+
+ */}
+
+
+ );
+};
+export default ProjectLateReport;
diff --git a/src/app/(main)/analytics/ResourceOvercomsumptionReport/page.tsx b/src/app/(main)/analytics/ResourceOvercomsumptionReport/page.tsx
new file mode 100644
index 0000000..4f5c75a
--- /dev/null
+++ b/src/app/(main)/analytics/ResourceOvercomsumptionReport/page.tsx
@@ -0,0 +1,24 @@
+//src\app\(main)\analytics\LateStartReport\page.tsx
+import { Metadata } from "next";
+import { I18nProvider } from "@/i18n";
+import Typography from "@mui/material/Typography";
+import LateStartReportComponent from "@/components/LateStartReport";
+
+export const metadata: Metadata = {
+ title: "Project Status by Client",
+};
+
+const ProjectLateReport: React.FC = () => {
+ return (
+
+
+ Resource Overconsumption Report
+
+ {/* }>
+
+ */}
+
+
+ );
+};
+export default ProjectLateReport;
diff --git a/src/app/(main)/analytics/page.tsx b/src/app/(main)/analytics/page.tsx
index 9ca9a24..d20fa34 100644
--- a/src/app/(main)/analytics/page.tsx
+++ b/src/app/(main)/analytics/page.tsx
@@ -1,3 +1,4 @@
+//src\app\(main)\analytics\page.tsx
import { Metadata } from "next";
export const metadata: Metadata = {
@@ -5,7 +6,8 @@ export const metadata: Metadata = {
};
const Analytics: React.FC = async () => {
- return "Analytics";
+ //return "Analytics";
+ return
Analytics
;
};
export default Analytics;
diff --git a/src/app/api/report/index.ts b/src/app/api/report/index.ts
new file mode 100644
index 0000000..588692c
--- /dev/null
+++ b/src/app/api/report/index.ts
@@ -0,0 +1,44 @@
+//src\app\api\report\index.ts
+import { cache } from "react";
+
+export interface LateStart {
+ id: number;
+ projectCode: string;
+ projectName: string;
+ team: string;
+ teamLeader: string;
+ startDate: string;
+ startDateFrom: string;
+ startDateTo: string;
+ targetEndDate: string;
+ client: string;
+ subsidiary: string;
+ nextstage: string;
+ nextstageenddate: string;
+}
+
+export const preloadProjects = () => {
+ fetchProjectsCashFlow();
+};
+
+export const fetchProjectsCashFlow = cache(async () => {
+ return mockProjects;
+});
+
+const mockProjects: LateStart[] = [
+ {
+ 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",
+ nextstage:"s1",
+ nextstageenddate:"30/2/2024",
+ },
+];
diff --git a/src/components/LateStartReport/LateStartReport.tsx b/src/components/LateStartReport/LateStartReport.tsx
new file mode 100644
index 0000000..85df0b9
--- /dev/null
+++ b/src/components/LateStartReport/LateStartReport.tsx
@@ -0,0 +1,17 @@
+//src\components\LateStartReport\LateStartReport.tsx
+"use client";
+import * as React from "react";
+import "../../app/global.css";
+import { Suspense } from "react";
+import LateStartReportGen from "@/components/LateStartReportGen";
+
+const LateStartReport: React.FC = () => {
+
+ return (
+ }>
+
+
+ );
+};
+
+export default LateStartReport;
\ No newline at end of file
diff --git a/src/components/LateStartReport/index.ts b/src/components/LateStartReport/index.ts
new file mode 100644
index 0000000..bb8ef69
--- /dev/null
+++ b/src/components/LateStartReport/index.ts
@@ -0,0 +1,2 @@
+//src\components\LateStartReport\index.ts
+export { default } from "./LateStartReport";
diff --git a/src/components/LateStartReportGen/DownloadReportButton.tsx b/src/components/LateStartReportGen/DownloadReportButton.tsx
new file mode 100644
index 0000000..b838fba
--- /dev/null
+++ b/src/components/LateStartReportGen/DownloadReportButton.tsx
@@ -0,0 +1,40 @@
+// DownloadReportButton.tsx
+// import React, { useState } from 'react';
+// import { generateFakeData } from '../utils/generateFakeData';
+// import { downloadExcel } from '../utils/downloadExcel';
+
+// export const DownloadReportButton: React.FC = () => {
+// const [isLoading, setIsLoading] = useState(false);
+
+// const handleDownload = async () => {
+// setIsLoading(true);
+// const data = generateFakeData(10);
+// downloadExcel(data);
+// setIsLoading(false);
+// };
+
+// return (
+//
+// );
+// };
+
+import React from 'react';
+
+export const DownloadReportButton: React.FC = () => {
+ const handleDownload = () => {
+ const link = document.createElement('a');
+ link.href = '/temp/AR01_Late Start Report.xlsx'; // Adjust the path as necessary
+ link.download = 'AR01_Late Start Report.xlsx';
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ };
+
+ return (
+
+ );
+};
\ No newline at end of file
diff --git a/src/components/LateStartReportGen/LateStartReportGen.tsx b/src/components/LateStartReportGen/LateStartReportGen.tsx
new file mode 100644
index 0000000..8ebfae2
--- /dev/null
+++ b/src/components/LateStartReportGen/LateStartReportGen.tsx
@@ -0,0 +1,44 @@
+//src\components\LateStartReportGen\LateStartReportGen.tsx
+"use client";
+import React, { useMemo, useState } from "react";
+import SearchBox, { Criterion } from "../SearchBox";
+import { useTranslation } from "react-i18next";
+import { CashFlow } from "@/app/api/cashflow";
+import { DownloadReportButton } from './DownloadReportButton';
+interface Props {
+ projects: CashFlow[];
+}
+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: "Remained Date From",
+ label2: "Remained Date To",
+ paramName: "targetEndDate",
+ type: "dateRange",
+ },
+ ],
+ [t],
+ );
+
+ return (
+ <>
+ {
+ console.log(query);
+ }}
+ />
+
+ >
+ );
+};
+
+export default ProgressByClientSearch;
diff --git a/src/components/LateStartReportGen/LateStartReportGenLoading.tsx b/src/components/LateStartReportGen/LateStartReportGenLoading.tsx
new file mode 100644
index 0000000..93143ce
--- /dev/null
+++ b/src/components/LateStartReportGen/LateStartReportGenLoading.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 LateStartReportGenLoading: React.FC = () => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default LateStartReportGenLoading;
diff --git a/src/components/LateStartReportGen/LateStartReportGenWrapper.tsx b/src/components/LateStartReportGen/LateStartReportGenWrapper.tsx
new file mode 100644
index 0000000..5ba8325
--- /dev/null
+++ b/src/components/LateStartReportGen/LateStartReportGenWrapper.tsx
@@ -0,0 +1,19 @@
+//src\components\LateStartReportGen\LateStartReportGenWrapper.tsx
+import { fetchProjectsCashFlow } from "@/app/api/cashflow";
+import React from "react";
+import LateStartReportGen from "./LateStartReportGen";
+import LateStartReportGenLoading from "./LateStartReportGenLoading";
+
+interface SubComponents {
+ Loading: typeof LateStartReportGenLoading;
+}
+
+const LateStartReportGenWrapper: React.FC & SubComponents = async () => {
+ const clentprojects = await fetchProjectsCashFlow();
+
+ return ;
+};
+
+LateStartReportGenWrapper.Loading = LateStartReportGenLoading;
+
+export default LateStartReportGenWrapper;
\ No newline at end of file
diff --git a/src/components/LateStartReportGen/index.ts b/src/components/LateStartReportGen/index.ts
new file mode 100644
index 0000000..55e0f5d
--- /dev/null
+++ b/src/components/LateStartReportGen/index.ts
@@ -0,0 +1,2 @@
+//src\components\LateStartReportGen\index.ts
+export { default } from "./LateStartReportGenWrapper";
diff --git a/src/components/ReportSearchBox/SearchBox.tsx b/src/components/ReportSearchBox/SearchBox.tsx
new file mode 100644
index 0000000..fe058f7
--- /dev/null
+++ b/src/components/ReportSearchBox/SearchBox.tsx
@@ -0,0 +1,201 @@
+"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";
+
+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);
+ };
+
+ return (
+
+
+ {t("Search Criteria")}
+
+ {criteria.map((c) => {
+ return (
+
+ {c.type === "text" && (
+
+ )}
+ {c.type === "select" && (
+
+ {c.label}
+
+
+ )}
+ {c.type === "dateRange" && (
+
+
+
+
+
+
+ {"-"}
+
+
+
+
+
+
+ )}
+
+ );
+ })}
+
+
+ }
+ onClick={handleReset}
+ >
+ {t("Reset")}
+
+ }
+ onClick={handleSearch}
+ >
+ {t("Search")}
+
+
+
+
+ );
+}
+
+export default SearchBox;
diff --git a/src/components/ReportSearchBox/index.ts b/src/components/ReportSearchBox/index.ts
new file mode 100644
index 0000000..37a0659
--- /dev/null
+++ b/src/components/ReportSearchBox/index.ts
@@ -0,0 +1,3 @@
+//src\components\SearchBox\index.ts
+export { default } from "./SearchBox";
+export type { Criterion } from "./SearchBox";
diff --git a/src/components/utils/downloadExcel.ts b/src/components/utils/downloadExcel.ts
new file mode 100644
index 0000000..825ccb3
--- /dev/null
+++ b/src/components/utils/downloadExcel.ts
@@ -0,0 +1,9 @@
+// downloadExcel.ts
+import * as XLSX from 'xlsx-js-style';
+
+export const downloadExcel = (data: any[]) => {
+ const worksheet = XLSX.utils.json_to_sheet(data);
+ const workbook = XLSX.utils.book_new();
+ XLSX.utils.book_append_sheet(workbook, worksheet, 'Report');
+ XLSX.writeFile(workbook, 'Report.xlsx');
+};
diff --git a/src/components/utils/generateFakeData.ts b/src/components/utils/generateFakeData.ts
new file mode 100644
index 0000000..2b2df45
--- /dev/null
+++ b/src/components/utils/generateFakeData.ts
@@ -0,0 +1,40 @@
+// generateFakeData.ts
+import { faker } from '@faker-js/faker';
+
+interface ProjectData {
+ id: number;
+ projectCode: string;
+ projectName: string;
+ team: string;
+ teamLeader: string;
+ startDate: string;
+ startDateFrom: string;
+ startDateTo: string;
+ targetEndDate: string;
+ client: string;
+ subsidiary: string;
+ nextstage: string;
+ nextstageenddate: string;
+}
+
+export const generateFakeData = (numEntries: number): ProjectData[] => {
+ const data: ProjectData[] = [];
+ for (let i = 0; i < numEntries; i++) {
+ data.push({
+ id: i + 1,
+ projectCode: faker.datatype.uuid(),
+ projectName: faker.commerce.productName(),
+ team: faker.commerce.department(),
+ teamLeader: faker.name.fullName(), // Corrected from findName to fullName
+ startDate: faker.date.recent(90).toISOString().split('T')[0],
+ startDateFrom: faker.date.past(1).toISOString().split('T')[0],
+ startDateTo: faker.date.future(1).toISOString().split('T')[0],
+ targetEndDate: faker.date.future(1).toISOString().split('T')[0],
+ client: faker.company.name(), // Corrected from companyName to name
+ subsidiary: faker.company.name(), // Corrected from companyName to name
+ nextstage: "Design",
+ nextstageenddate: faker.date.future(2).toISOString().split('T')[0],
+ });
+ }
+ return data;
+};