diff --git a/public/temp/AR01_Late Start Report.xlsx b/public/temp/AR01_Late Start Report.xlsx
index 24da97f..f93a8e8 100644
Binary files a/public/temp/AR01_Late Start Report.xlsx and b/public/temp/AR01_Late Start Report.xlsx differ
diff --git a/public/temp/AR02_Delay Report.xlsx b/public/temp/AR02_Delay Report.xlsx
index a8c53a8..4db1f65 100644
Binary files a/public/temp/AR02_Delay Report.xlsx and b/public/temp/AR02_Delay Report.xlsx differ
diff --git a/public/temp/AR03_Resource Overconsumption.xlsx b/public/temp/AR03_Resource Overconsumption.xlsx
new file mode 100644
index 0000000..013c31c
Binary files /dev/null and b/public/temp/AR03_Resource Overconsumption.xlsx differ
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 0000000..a414e1a
Binary files /dev/null and b/public/temp/AR04_Cost and Expense Report.xlsx differ
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/(main)/analytics/ResourceOverconsumptionReport/page.tsx b/src/app/(main)/analytics/ResourceOverconsumptionReport/page.tsx
new file mode 100644
index 0000000..ee2c8ba
--- /dev/null
+++ b/src/app/(main)/analytics/ResourceOverconsumptionReport/page.tsx
@@ -0,0 +1,24 @@
+//src\app\(main)\analytics\ResourceOvercomsumptionReport\page.tsx
+import { Metadata } from "next";
+import { I18nProvider } from "@/i18n";
+import Typography from "@mui/material/Typography";
+import ResourceOverconsumptionReportComponent from "@/components/Report/ResourceOverconsumptionReport";
+
+export const metadata: Metadata = {
+ title: "Project Status by Client",
+};
+
+const ResourceOverconsumptionReport: React.FC = () => {
+ return (
+
+
+ Resource Overconsumption Report
+
+ {/* }>
+
+ */}
+
+
+ );
+};
+export default ResourceOverconsumptionReport;
diff --git a/src/app/api/report3/index.ts b/src/app/api/report3/index.ts
new file mode 100644
index 0000000..b2c8751
--- /dev/null
+++ b/src/app/api/report3/index.ts
@@ -0,0 +1,42 @@
+//src\app\api\report\index.ts
+import { cache } from "react";
+
+export interface ResourceOverconsumption {
+ 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 = () => {
+ fetchProjectsResourceOverconsumption();
+};
+
+export const fetchProjectsResourceOverconsumption = cache(async () => {
+ return mockProjects;
+});
+
+const mockProjects: ResourceOverconsumption[] = [
+ {
+ 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",
+ status: "1",
+ },
+];
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/app/utils/formatUtil.ts b/src/app/utils/formatUtil.ts
index 14d2ed1..4f364e8 100644
--- a/src/app/utils/formatUtil.ts
+++ b/src/app/utils/formatUtil.ts
@@ -39,7 +39,7 @@ export const shortDateFormatter = (locale?: string) => {
}
};
-export function convertLocaleStringToNumber(numberString: String): number {
+export function convertLocaleStringToNumber(numberString: string): number {
const numberWithoutCommas = numberString.replace(/,/g, "");
return parseFloat(numberWithoutCommas);
}
diff --git a/src/components/CreateProject/CreateProject.tsx b/src/components/CreateProject/CreateProject.tsx
index 61409b0..b8faa5e 100644
--- a/src/components/CreateProject/CreateProject.tsx
+++ b/src/components/CreateProject/CreateProject.tsx
@@ -61,7 +61,9 @@ const hasErrorsInTab = (
) => {
switch (tabIndex) {
case 0:
- return errors.projectName;
+ return (
+ errors.projectName || errors.projectCode || errors.projectDescription
+ );
default:
false;
}
@@ -101,7 +103,6 @@ const CreateProject: React.FC = ({
const onSubmit = useCallback>(
async (data) => {
try {
- console.log(data);
setServerError("");
await saveProject(data);
router.replace("/projects");
@@ -115,7 +116,11 @@ const CreateProject: React.FC = ({
const onSubmitError = useCallback>(
(errors) => {
// Set the tab so that the focus will go there
- if (errors.projectName) {
+ if (
+ errors.projectName ||
+ errors.projectDescription ||
+ errors.projectCode
+ ) {
setTabIndex(0);
}
},
diff --git a/src/components/CreateProject/MilestoneSection.tsx b/src/components/CreateProject/MilestoneSection.tsx
index 9d18717..68a35a2 100644
--- a/src/components/CreateProject/MilestoneSection.tsx
+++ b/src/components/CreateProject/MilestoneSection.tsx
@@ -29,7 +29,7 @@ import React, { useCallback, useEffect, useMemo, useState } from "react";
import { useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import StyledDataGrid from "../StyledDataGrid";
-import { moneyFormatter } from "@/app/utils/formatUtil";
+import { INPUT_DATE_FORMAT, moneyFormatter } from "@/app/utils/formatUtil";
import isDate from "lodash/isDate";
interface Props {
@@ -206,7 +206,7 @@ const MilestoneSection: React.FC = ({ taskGroupId }) => {
description: p.description!,
id: p.id!,
amount: p.amount!,
- date: dayjs(p.date!).toISOString(),
+ date: dayjs(p.date!).format(INPUT_DATE_FORMAT),
})),
},
});
@@ -245,7 +245,7 @@ const MilestoneSection: React.FC = ({ taskGroupId }) => {
...milestones,
[taskGroupId]: {
...milestones[taskGroupId],
- startDate: date.toISOString(),
+ startDate: date.format(INPUT_DATE_FORMAT),
},
});
}}
@@ -264,7 +264,7 @@ const MilestoneSection: React.FC = ({ taskGroupId }) => {
...milestones,
[taskGroupId]: {
...milestones[taskGroupId],
- endDate: date.toISOString(),
+ endDate: date.format(INPUT_DATE_FORMAT),
},
});
}}
diff --git a/src/components/NavigationContent/NavigationContent.tsx b/src/components/NavigationContent/NavigationContent.tsx
index db5b544..53dbb5c 100644
--- a/src/components/NavigationContent/NavigationContent.tsx
+++ b/src/components/NavigationContent/NavigationContent.tsx
@@ -103,8 +103,8 @@ const navigationItems: NavigationItem[] = [
{icon: , label:"Delay Report", path: "/analytics/DelayReport"},
{icon: , label:"Resource Overconsumption Report", path: "/analytics/ResourceOverconsumptionReport"},
{icon: , label:"Cost and Expense Report", path: "/analytics/CostandExpenseReport"},
- {icon: , label:"Completion Report", path: "/analytics/CompletionReport"},
- {icon: , label:"Completion Report with Outstanding Un-billed Hours Report", path: "/analytics/CompletionReportWO"},
+ {icon: , label:"Completion Report", path: "/analytics/ProjectCompletionReport"},
+ {icon: , label:"Completion Report with Outstanding Un-billed Hours Report", path: "/analytics/ProjectCompletionReportWO"},
],
},
{
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/Report/DelayReportGen/DelayReportGen.tsx b/src/components/Report/DelayReportGen/DelayReportGen.tsx
index 4155613..8f8b10d 100644
--- a/src/components/Report/DelayReportGen/DelayReportGen.tsx
+++ b/src/components/Report/DelayReportGen/DelayReportGen.tsx
@@ -16,8 +16,8 @@ const ProgressByClientSearch: React.FC = ({ projects }) => {
const searchCriteria: Criterion[] = useMemo(
() => [
- { label: "Team", paramName: "team", type: "text" },
- { label: "Client", paramName: "client", type: "text" },
+ { label: "Team", paramName: "team", type: "select", options: ["AAA", "BBB", "CCC"] },
+ { label: "Client", paramName: "client", type: "select", options: ["Cust A", "Cust B", "Cust C"] },
{ label: "Status", paramName: "status", type: "select", options: ["Delayed", "Potential Delay"] },
// {
// label: "Status",
diff --git a/src/components/Report/LateStartReportGen/LateStartReportGen.tsx b/src/components/Report/LateStartReportGen/LateStartReportGen.tsx
index 81ff1a0..2fca827 100644
--- a/src/components/Report/LateStartReportGen/LateStartReportGen.tsx
+++ b/src/components/Report/LateStartReportGen/LateStartReportGen.tsx
@@ -5,6 +5,9 @@ import SearchBox, { Criterion } from "../../ReportSearchBox";
import { useTranslation } from "react-i18next";
import { LateStart } from "@/app/api/report";
//import { DownloadReportButton } from './DownloadReportButton';
+import axios from 'axios';
+import { apiPath } from '../../../auth/utils';
+//import { GET_QC_CATEGORY_COMBO } from 'utils/ApiPathConst';
interface Props {
projects: LateStart[];
}
@@ -13,11 +16,19 @@ type SearchParamNames = keyof SearchQuery;
const ProgressByClientSearch: React.FC = ({ projects }) => {
const { t } = useTranslation("projects");
-
+ // const [teamCombo, setteamCombo] = useState([]);
+ // const getteamCombo = () => {
+ // axios.get(`${apiPath}${GET_QC_CATEGORY_COMBO}`)
+ // .then(response => {
+ // setteamCombo(response.data.records)})
+ // .catch(error => {
+ // console.error('Error fetching data: ', error);
+ // });
+ // }
const searchCriteria: Criterion[] = useMemo(
() => [
- { label: "Team", paramName: "team", type: "text" },
- { label: "Client", paramName: "client", type: "text" },
+ { label: "Team", paramName: "team", type: "select", options: ["AAA", "BBB", "CCC"] },
+ { label: "Client", paramName: "client", type: "select", options: ["Cust A", "Cust B", "Cust C"] },
{
label: "Remained Date From",
label2: "Remained Date To",
diff --git a/src/components/Report/ResourceOverconsumptionReport/ResourceOverconsumptionReport.tsx b/src/components/Report/ResourceOverconsumptionReport/ResourceOverconsumptionReport.tsx
new file mode 100644
index 0000000..345b2f2
--- /dev/null
+++ b/src/components/Report/ResourceOverconsumptionReport/ResourceOverconsumptionReport.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 ResourceOverconsumptionReportGen from "@/components/Report/ResourceOverconsumptionReportGen";
+
+const ResourceOverconsumptionReport: React.FC = () => {
+
+ return (
+ }>
+
+
+ );
+};
+
+export default ResourceOverconsumptionReport;
\ No newline at end of file
diff --git a/src/components/Report/ResourceOverconsumptionReport/index.ts b/src/components/Report/ResourceOverconsumptionReport/index.ts
new file mode 100644
index 0000000..ce20324
--- /dev/null
+++ b/src/components/Report/ResourceOverconsumptionReport/index.ts
@@ -0,0 +1,2 @@
+//src\components\LateStartReport\index.ts
+export { default } from "./ResourceOverconsumptionReport";
diff --git a/src/components/Report/ResourceOverconsumptionReportGen/ResourceOverconsumptionReportGen.tsx b/src/components/Report/ResourceOverconsumptionReportGen/ResourceOverconsumptionReportGen.tsx
new file mode 100644
index 0000000..0faf37e
--- /dev/null
+++ b/src/components/Report/ResourceOverconsumptionReportGen/ResourceOverconsumptionReportGen.tsx
@@ -0,0 +1,45 @@
+//src\components\LateStartReportGen\LateStartReportGen.tsx
+"use client";
+import React, { useMemo, useState } from "react";
+import SearchBox, { Criterion } from "../../ReportSearchBox3";
+import { useTranslation } from "react-i18next";
+import { ResourceOverconsumption } from "@/app/api/report3";
+
+interface Props {
+ projects: ResourceOverconsumption[];
+}
+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: "Status", paramName: "status", type: "select", options: ["Overconsumption", "Potential Overconsumption"] },
+ // {
+ // label: "Status",
+ // label2: "Remained Date To",
+ // paramName: "targetEndDate",
+ // type: "dateRange",
+ // },
+ ],
+ [t],
+ );
+
+ return (
+ <>
+ {
+ console.log(query);
+ }}
+ />
+ {/* */}
+ >
+ );
+};
+
+export default ProgressByClientSearch;
diff --git a/src/components/Report/ResourceOverconsumptionReportGen/ResourceOverconsumptionReportGenLoading.tsx b/src/components/Report/ResourceOverconsumptionReportGen/ResourceOverconsumptionReportGenLoading.tsx
new file mode 100644
index 0000000..9ae7417
--- /dev/null
+++ b/src/components/Report/ResourceOverconsumptionReportGen/ResourceOverconsumptionReportGenLoading.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 ResourceOvercomsumptionReportGenLoading: React.FC = () => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default ResourceOvercomsumptionReportGenLoading;
diff --git a/src/components/Report/ResourceOverconsumptionReportGen/ResourceOverconsumptionReportGenWrapper.tsx b/src/components/Report/ResourceOverconsumptionReportGen/ResourceOverconsumptionReportGenWrapper.tsx
new file mode 100644
index 0000000..a93f64b
--- /dev/null
+++ b/src/components/Report/ResourceOverconsumptionReportGen/ResourceOverconsumptionReportGenWrapper.tsx
@@ -0,0 +1,19 @@
+//src\components\LateStartReportGen\LateStartReportGenWrapper.tsx
+import { fetchProjectsResourceOverconsumption } from "@/app/api/report3";
+import React from "react";
+import ResourceOvercomsumptionReportGen from "./ResourceOverconsumptionReportGen";
+import ResourceOvercomsumptionReportGenLoading from "./ResourceOverconsumptionReportGenLoading";
+
+interface SubComponents {
+ Loading: typeof ResourceOvercomsumptionReportGenLoading;
+}
+
+const ResourceOvercomsumptionReportGenWrapper: React.FC & SubComponents = async () => {
+ const clentprojects = await fetchProjectsResourceOverconsumption();
+
+ return ;
+};
+
+ResourceOvercomsumptionReportGenWrapper.Loading = ResourceOvercomsumptionReportGenLoading;
+
+export default ResourceOvercomsumptionReportGenWrapper;
\ No newline at end of file
diff --git a/src/components/Report/ResourceOverconsumptionReportGen/index.ts b/src/components/Report/ResourceOverconsumptionReportGen/index.ts
new file mode 100644
index 0000000..82fb633
--- /dev/null
+++ b/src/components/Report/ResourceOverconsumptionReportGen/index.ts
@@ -0,0 +1,2 @@
+//src\components\DelayReportGen\index.ts
+export { default } from "./ResourceOverconsumptionReportGenWrapper";
diff --git a/src/components/ReportSearchBox/SearchBox.tsx b/src/components/ReportSearchBox/SearchBox.tsx
index 6ca34f1..c629ad4 100644
--- a/src/components/ReportSearchBox/SearchBox.tsx
+++ b/src/components/ReportSearchBox/SearchBox.tsx
@@ -253,6 +253,7 @@ function SearchBox({
({
@@ -287,9 +289,9 @@ function SearchBox({
}
- onClick={handleDownload}
+ onClick={handleSearch}
>
- {t("Download Report")}
+ {t("Search")}
diff --git a/src/components/ReportSearchBox2/SearchBox2.tsx b/src/components/ReportSearchBox2/SearchBox2.tsx
index 16b72df..37d65ba 100644
--- a/src/components/ReportSearchBox2/SearchBox2.tsx
+++ b/src/components/ReportSearchBox2/SearchBox2.tsx
@@ -253,6 +253,7 @@ function SearchBox({
({
@@ -287,9 +289,9 @@ function SearchBox({
}
- onClick={handleDownload}
+ onClick={handleSearch}
>
- {t("Download Report")}
+ {t("Search")}
diff --git a/src/components/ReportSearchBox3/SearchBox3.tsx b/src/components/ReportSearchBox3/SearchBox3.tsx
new file mode 100644
index 0000000..8bf2fea
--- /dev/null
+++ b/src/components/ReportSearchBox3/SearchBox3.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/AR03_Resource Overconsumption.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 L6
+ // Apply styles from A6 to L6 (bold, bottom border, center alignment)
+ for (let col = 0; col < 12; 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 = `AR03_Resource_Overconsumption_${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={handleSearch}
+ >
+ {t("Search")}
+
+
+
+
+ );
+}
+
+export default SearchBox;
diff --git a/src/components/ReportSearchBox3/index.ts b/src/components/ReportSearchBox3/index.ts
new file mode 100644
index 0000000..d481fbd
--- /dev/null
+++ b/src/components/ReportSearchBox3/index.ts
@@ -0,0 +1,3 @@
+//src\components\SearchBox\index.ts
+export { default } from "./SearchBox3";
+export type { Criterion } from "./SearchBox3";
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" && (
+
+
+
+
+
+
+ {"-"}
+
+
+
+
+
+
+ )}
+
+ );
+ })}
+
+
+ }
+ onClick={handleReset}
+ >
+ {t("Reset")}
+
+ }
+ onClick={handleSearch}
+ >
+ {t("Search")}
+
+
+
+
+ );
+}
+
+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";
diff --git a/src/components/SearchBox/SearchBox.tsx b/src/components/SearchBox/SearchBox.tsx
index fe058f7..13b03ee 100644
--- a/src/components/SearchBox/SearchBox.tsx
+++ b/src/components/SearchBox/SearchBox.tsx
@@ -154,6 +154,7 @@ function SearchBox({
({
diff --git a/src/components/SearchResults/SearchResults.tsx b/src/components/SearchResults/SearchResults.tsx
index 4c82280..bfecd36 100644
--- a/src/components/SearchResults/SearchResults.tsx
+++ b/src/components/SearchResults/SearchResults.tsx
@@ -11,7 +11,7 @@ import TablePagination, {
TablePaginationProps,
} from "@mui/material/TablePagination";
import TableRow from "@mui/material/TableRow";
-import IconButton from "@mui/material/IconButton";
+import IconButton, { IconButtonOwnProps, IconButtonPropsColorOverrides } from "@mui/material/IconButton";
export interface ResultWithId {
id: string | number;
@@ -20,6 +20,7 @@ export interface ResultWithId {
interface BaseColumn {
name: keyof T;
label: string;
+ color?: IconButtonOwnProps["color"];
}
interface ColumnWithAction extends BaseColumn {
@@ -91,7 +92,7 @@ function SearchResults({
{isActionColumn(column) ? (
column.onClick(item)}
>
{column.buttonIcon}
diff --git a/src/components/TimesheetTable/EntryInputTable.tsx b/src/components/TimesheetTable/EntryInputTable.tsx
index 57a18b3..7d9e9de 100644
--- a/src/components/TimesheetTable/EntryInputTable.tsx
+++ b/src/components/TimesheetTable/EntryInputTable.tsx
@@ -22,6 +22,9 @@ import { AssignedProject } from "@/app/api/projects";
import uniqBy from "lodash/uniqBy";
import { TaskGroup } from "@/app/api/tasks";
import dayjs from "dayjs";
+import isBetween from "dayjs/plugin/isBetween";
+
+dayjs.extend(isBetween);
const mockProjects: AssignedProject[] = [
{