diff --git a/public/temp/AR06_Project Completion Report with Outstanding Un-billed Hours.xlsx b/public/temp/AR06_Project Completion Report with Outstanding Un-billed Hours.xlsx
new file mode 100644
index 0000000..419bd2e
Binary files /dev/null and b/public/temp/AR06_Project Completion Report with Outstanding Un-billed Hours.xlsx differ
diff --git a/src/app/(main)/analytics/ProjectCompletionReportWO/page.tsx b/src/app/(main)/analytics/ProjectCompletionReportWO/page.tsx
index 5b914ad..5ab9a39 100644
--- a/src/app/(main)/analytics/ProjectCompletionReportWO/page.tsx
+++ b/src/app/(main)/analytics/ProjectCompletionReportWO/page.tsx
@@ -1,14 +1,14 @@
-//src\app\(main)\analytics\LateStartReport\page.tsx
+//src\app\(main)\analytics\ProjectCompletionReport\page.tsx
import { Metadata } from "next";
import { I18nProvider } from "@/i18n";
import Typography from "@mui/material/Typography";
-import LateStartReportComponent from "@/components/LateStartReport";
+import ProjectCompletionReportWOComponent from "@/components/Report/ProjectCompletionReportWO";
export const metadata: Metadata = {
title: "Project Status by Client",
};
-const ProjectLateReport: React.FC = () => {
+const ProjectCompletionReportWO: React.FC = () => {
return (
@@ -17,8 +17,8 @@ const ProjectLateReport: React.FC = () => {
{/* }>
*/}
-
+
);
};
-export default ProjectLateReport;
+export default ProjectCompletionReportWO;
diff --git a/src/app/api/report6/index.ts b/src/app/api/report6/index.ts
new file mode 100644
index 0000000..e1f2925
--- /dev/null
+++ b/src/app/api/report6/index.ts
@@ -0,0 +1,42 @@
+//src\app\api\report\index.ts
+import { cache } from "react";
+
+export interface ProjectClaims {
+ id: number;
+ projectCode: string;
+ projectName: string;
+ team: string;
+ teamLeader: string;
+ startDate: string;
+ startDateFrom: string;
+ startDateTo: string;
+ targetEndDate: string;
+ client: string;
+ subsidiary: string;
+ completeDate: string;
+}
+
+export const preloadProjects = () => {
+ fetchProjectsProjectClaims();
+};
+
+export const fetchProjectsProjectClaims = cache(async () => {
+ return mockProjects;
+});
+
+const mockProjects: ProjectClaims[] = [
+ {
+ 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",
+ completeDate:"30/2/2024",
+ },
+];
diff --git a/src/components/Report/ProjectCompletionReportWO/ProjectCompletionReportWO.tsx b/src/components/Report/ProjectCompletionReportWO/ProjectCompletionReportWO.tsx
new file mode 100644
index 0000000..c0165f2
--- /dev/null
+++ b/src/components/Report/ProjectCompletionReportWO/ProjectCompletionReportWO.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 ProjectCompletionReportWOGen from "@/components/Report/ProjectCompletionReportWOGen";
+
+const ProjectCompletionReportWO: React.FC = () => {
+
+ return (
+ }>
+
+
+ );
+};
+
+export default ProjectCompletionReportWO;
\ No newline at end of file
diff --git a/src/components/Report/ProjectCompletionReportWO/index.ts b/src/components/Report/ProjectCompletionReportWO/index.ts
new file mode 100644
index 0000000..0382210
--- /dev/null
+++ b/src/components/Report/ProjectCompletionReportWO/index.ts
@@ -0,0 +1,2 @@
+//src\components\LateStartReport\index.ts
+export { default } from "./ProjectCompletionReportWO";
diff --git a/src/components/Report/ProjectCompletionReportWOGen/ProjectCompletionReportWOGen.tsx b/src/components/Report/ProjectCompletionReportWOGen/ProjectCompletionReportWOGen.tsx
new file mode 100644
index 0000000..2942e38
--- /dev/null
+++ b/src/components/Report/ProjectCompletionReportWOGen/ProjectCompletionReportWOGen.tsx
@@ -0,0 +1,44 @@
+//src\components\LateStartReportGen\LateStartReportGen.tsx
+"use client";
+import React, { useMemo, useState } from "react";
+import SearchBox, { Criterion } from "../../ReportSearchBox6";
+import { useTranslation } from "react-i18next";
+import { ProjectCompletionWO } from "@/app/api/report6";
+//import { DownloadReportButton } from './DownloadReportButton';
+interface Props {
+ projects: ProjectCompletionWO[];
+}
+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: "Report Period From",
+ label2: "Report Period To",
+ paramName: "targetEndDate",
+ type: "dateRange",
+ },
+ ],
+ [t],
+ );
+
+ return (
+ <>
+ {
+ console.log(query);
+ }}
+ />
+ {/* */}
+ >
+ );
+};
+
+export default ProgressByClientSearch;
diff --git a/src/components/Report/ProjectCompletionReportWOGen/ProjectCompletionReportWOGenLoading.tsx b/src/components/Report/ProjectCompletionReportWOGen/ProjectCompletionReportWOGenLoading.tsx
new file mode 100644
index 0000000..466b74d
--- /dev/null
+++ b/src/components/Report/ProjectCompletionReportWOGen/ProjectCompletionReportWOGenLoading.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 ProjectCompletionReportGenLoading: React.FC = () => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default ProjectCompletionReportGenLoading;
diff --git a/src/components/Report/ProjectCompletionReportWOGen/ProjectCompletionReportWOGenWrapper.tsx b/src/components/Report/ProjectCompletionReportWOGen/ProjectCompletionReportWOGenWrapper.tsx
new file mode 100644
index 0000000..c294e0c
--- /dev/null
+++ b/src/components/Report/ProjectCompletionReportWOGen/ProjectCompletionReportWOGenWrapper.tsx
@@ -0,0 +1,19 @@
+//src\components\LateStartReportGen\LateStartReportGenWrapper.tsx
+import { fetchProjectsProjectCompletionWO } from "@/app/api/report6";
+import React from "react";
+import ProjectCompletionReportWOGen from "./ProjectCompletionReportWOGen";
+import ProjectCompletionReportWOGenLoading from "./ProjectCompletionReportWOGenLoading";
+
+interface SubComponents {
+ Loading: typeof ProjectCompletionReportWOGenLoading;
+}
+
+const ProjectCompletionReportWOGenWrapper: React.FC & SubComponents = async () => {
+ const clentprojects = await fetchProjectsProjectCompletionWO();
+
+ return ;
+};
+
+ProjectCompletionReportWOGenWrapper.Loading = ProjectCompletionReportWOGenLoading;
+
+export default ProjectCompletionReportWOGenWrapper;
\ No newline at end of file
diff --git a/src/components/Report/ProjectCompletionReportWOGen/index.ts b/src/components/Report/ProjectCompletionReportWOGen/index.ts
new file mode 100644
index 0000000..20bd632
--- /dev/null
+++ b/src/components/Report/ProjectCompletionReportWOGen/index.ts
@@ -0,0 +1,2 @@
+//src\components\LateStartReportGen\index.ts
+export { default } from "./ProjectCompletionReportWOGenWrapper";
diff --git a/src/components/ReportSearchBox6/SearchBox6.tsx b/src/components/ReportSearchBox6/SearchBox6.tsx
new file mode 100644
index 0000000..1f201b1
--- /dev/null
+++ b/src/components/ReportSearchBox6/SearchBox6.tsx
@@ -0,0 +1,302 @@
+//src\components\ReportSearchBox\SearchBox.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/AR06_Project Completion Report with Outstanding Un-billed Hours.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 A3 (bold)
+ ['A2', 'A3'].forEach(cell => {
+ if (worksheet[cell]) {
+ worksheet[cell].s = { font: { bold: true } };
+ }
+ });
+
+ // Formatting from A5 to G5
+ // Apply styles from A5 to G5 (bold, bottom border, center alignment)
+ for (let col = 0; col < 7; col++) { // Columns A to G
+ const cellRef = XLSX.utils.encode_col(col) + '5';
+ 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 = `AR06_Project_Completion_Report_with_Outstanding_Un-billed_Hours_${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")}
+
+
+
+
+ );
+}
+
+export default SearchBox;
diff --git a/src/components/ReportSearchBox6/index.ts b/src/components/ReportSearchBox6/index.ts
new file mode 100644
index 0000000..9ead696
--- /dev/null
+++ b/src/components/ReportSearchBox6/index.ts
@@ -0,0 +1,3 @@
+//src\components\SearchBox\index.ts
+export { default } from "./SearchBox6";
+export type { Criterion } from "./SearchBox6";