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/(main)/dashboard/ProjectFinancialSummary/page.tsx b/src/app/(main)/dashboard/ProjectFinancialSummary/page.tsx
index 3549f77..fcfd256 100644
--- a/src/app/(main)/dashboard/ProjectFinancialSummary/page.tsx
+++ b/src/app/(main)/dashboard/ProjectFinancialSummary/page.tsx
@@ -17,7 +17,7 @@ const ProjectFinancialSummary: React.FC = () => {
return (
- Project Financial Summary
+ Financial Summary
diff --git a/src/app/(main)/settings/customer/create/page.tsx b/src/app/(main)/settings/customer/create/page.tsx
index c4f13b4..e0dc0e0 100644
--- a/src/app/(main)/settings/customer/create/page.tsx
+++ b/src/app/(main)/settings/customer/create/page.tsx
@@ -6,7 +6,7 @@ import Typography from "@mui/material/Typography";
import { Metadata } from "next";
export const metadata: Metadata = {
- title: "Create Customer",
+ title: "Create Client",
};
const CreateCustomer: React.FC = async () => {
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/app/api/staff/actions.ts b/src/app/api/staff/actions.ts
index 8eb5ff3..1098508 100644
--- a/src/app/api/staff/actions.ts
+++ b/src/app/api/staff/actions.ts
@@ -19,7 +19,7 @@ export interface CreateStaffInputs {
companyId: number;
gradeId: number;
teamId: number;
- salaryEffId: number;
+ salaryId: number;
email: string;
phone1: string;
phone2: string;
diff --git a/src/components/CreateStaff/CreateStaff.tsx b/src/components/CreateStaff/CreateStaff.tsx
index 0aed5ab..35b8ded 100644
--- a/src/components/CreateStaff/CreateStaff.tsx
+++ b/src/components/CreateStaff/CreateStaff.tsx
@@ -179,7 +179,7 @@ const CreateStaff: React.FC = ({ Title }) => {
label: t("Team"),
type: "combo-Obj",
options: teamCombo,
- required: true,
+ required: false,
},
{
id: "departmentId",
@@ -193,14 +193,14 @@ const CreateStaff: React.FC = ({ Title }) => {
label: t("Grade"),
type: "combo-Obj",
options: gradeCombo,
- required: true,
+ required: false,
},
{
id: "skillSetId",
label: t("Skillset"),
type: "combo-Obj",
options: skillCombo,
- required: true,
+ required: false,
},
{
id: "currentPositionId",
@@ -210,19 +210,19 @@ const CreateStaff: React.FC = ({ Title }) => {
required: true,
},
{
- id: "salaryEffId",
+ id: "salaryId",
label: t("Salary Point"),
type: "combo-Obj",
options: salaryCombo,
required: true,
},
- {
- id: "hourlyRate",
- label: t("Hourly Rate"),
- type: "numeric-testing",
- value: "",
- required: true,
- },
+ // {
+ // id: "hourlyRate",
+ // label: t("Hourly Rate"),
+ // type: "numeric-testing",
+ // value: "",
+ // required: false,
+ // },
{
id: "employType",
label: t("Employ Type"),
@@ -245,7 +245,7 @@ const CreateStaff: React.FC = ({ Title }) => {
label: t("Phone1"),
type: "text",
value: "",
- pattern: "^\\d{8}$",
+ // pattern: "^\\d{8}$",
message: t("input correct phone no."),
required: true,
},
@@ -254,9 +254,9 @@ const CreateStaff: React.FC = ({ Title }) => {
label: t("Phone2"),
type: "text",
value: "",
- pattern: "^\\d{8}$",
+ // pattern: "^\\d{8}$",
message: t("input correct phone no."),
- required: true,
+ required: false,
},
],
[
@@ -272,7 +272,7 @@ const CreateStaff: React.FC = ({ Title }) => {
label: t("Emergency Contact Phone"),
type: "text",
value: "",
- pattern: "^\\d{8}$",
+ // pattern: "^\\d{8}$",
message: t("input correct phone no."),
required: true,
},
diff --git a/src/components/CustomInputForm/CustomInputForm.tsx b/src/components/CustomInputForm/CustomInputForm.tsx
index ffcaa7a..dc7f269 100644
--- a/src/components/CustomInputForm/CustomInputForm.tsx
+++ b/src/components/CustomInputForm/CustomInputForm.tsx
@@ -274,7 +274,7 @@ const CustomInputForm: React.FC = ({
fullWidth
{...register(field.id, {
pattern:
- /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/,
+ /^([a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})$/,
})}
defaultValue={!field.value ? `${field.value}` : ""}
required={field.required ?? false}
diff --git a/src/components/CustomerDetail/ContactInfo.tsx b/src/components/CustomerDetail/ContactInfo.tsx
index 9566264..f608b00 100644
--- a/src/components/CustomerDetail/ContactInfo.tsx
+++ b/src/components/CustomerDetail/ContactInfo.tsx
@@ -231,7 +231,7 @@ const ContactInfo: React.FC = ({
if (errorRows.length > 0) {
setError("addContacts", { message: "Contact details include empty fields", type: "required" })
} else {
- const errorRows_EmailFormat = rows.filter(row => !/^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/.test(String(row.email)))
+ const errorRows_EmailFormat = rows.filter(row => !/^([a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})$/.test(String(row.email)))
if (errorRows_EmailFormat.length > 0) {
setError("addContacts", { message: "Contact details include empty fields", type: "email_format" })
} else {
diff --git a/src/components/CustomerDetail/CustomerDetail.tsx b/src/components/CustomerDetail/CustomerDetail.tsx
index 47b47c0..88a99ad 100644
--- a/src/components/CustomerDetail/CustomerDetail.tsx
+++ b/src/components/CustomerDetail/CustomerDetail.tsx
@@ -75,7 +75,9 @@ const CustomerDetail: React.FC = ({
const customer = await fetchCustomer(parseInt(id))
- if (customer !== null && Object.keys(customer).length > 0) {
+ console.log(customer)
+
+ if (customer !== null && Object.keys(customer).length > 0 && !Object.values(customer.customer).every(x => x === null)) {
const tempCustomerInput = {
id: customer.customer.id,
code: customer.customer.code ?? "",
@@ -165,7 +167,7 @@ const CustomerDetail: React.FC = ({
formProps.setError("code", { message: "Code is empty", type: "required" })
}
- // if (data.email && data.email?.length > 0 && !/^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/.test(data.email)) {
+ // if (data.email && data.email?.length > 0 && !/^([a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})$/.test(data.email)) {
// haveError = true
// formProps.setError("email", { message: "Email format is not valid", type: "custom" })
// }
@@ -180,7 +182,7 @@ const CustomerDetail: React.FC = ({
formProps.setError("addContacts", { message: "Contact info includes empty fields", type: "required" })
}
- if (data.addContacts.length > 0 && data.addContacts.filter(row => !/^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/.test(String(row.email))).length > 0) {
+ if (data.addContacts.length > 0 && data.addContacts.filter(row => !/^([a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})$/.test(String(row.email))).length > 0) {
haveError = true
formProps.setError("addContacts", { message: "Contact info includes invalid email", type: "email_format" })
}
diff --git a/src/components/CustomerDetail/CustomerInfo.tsx b/src/components/CustomerDetail/CustomerInfo.tsx
index 695f6d5..3eaa327 100644
--- a/src/components/CustomerDetail/CustomerInfo.tsx
+++ b/src/components/CustomerDetail/CustomerInfo.tsx
@@ -107,7 +107,7 @@ const CustomerInfo: React.FC = ({
label={t("Customer Email")}
fullWidth
{...register("email", {
- pattern: /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/,
+ pattern: /^([a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})$/,
})}
error={Boolean(errors.email)}
helperText={Boolean(errors.email) && t("Please input correct customer email")}
diff --git a/src/components/DashboardPage/DashboardPage.tsx b/src/components/DashboardPage/DashboardPage.tsx
index d3d70c7..7fb107c 100644
--- a/src/components/DashboardPage/DashboardPage.tsx
+++ b/src/components/DashboardPage/DashboardPage.tsx
@@ -31,9 +31,9 @@ const DashboardPage: React.FC = () => {
return (
-
+
-
+
diff --git a/src/components/DashboardPage/DashboardTabButton.tsx b/src/components/DashboardPage/DashboardTabButton.tsx
index 1f95098..b822605 100644
--- a/src/components/DashboardPage/DashboardTabButton.tsx
+++ b/src/components/DashboardPage/DashboardTabButton.tsx
@@ -16,7 +16,7 @@ const DashboardTabButton: React.FC = () => {
const renderContent = () => {
switch (activeTab) {
case "financialSummary":
- return Project Financial Summary
;
+ return Financial Summary
;
case "cashFlow":
return Project Cash Flow
;
case "progressByClient":
@@ -26,7 +26,7 @@ const DashboardTabButton: React.FC = () => {
case "staffUtilization":
return Staff Utilization
;
default:
- return Project Financial Summary
;
+ return Financial Summary
;
}
};
const [tabIndex, setTabIndex] = useState(0);
@@ -40,16 +40,16 @@ const DashboardTabButton: React.FC = () => {
//
//
// {activeTab !== 'financialSummary' ?
- // :
- //
+ // :
+ //
// }
// {activeTab !== 'cashFlow' ?
// :
//
// }
// {activeTab !== 'progressByClient' ?
- // :
- //
+ // :
+ //
// }
// {activeTab !== 'resourceUtilization' ?
// :
@@ -65,9 +65,9 @@ const DashboardTabButton: React.FC = () => {
//
//
-
+
-
+
diff --git a/src/components/DashboardPage/ProgressByClient.tsx b/src/components/DashboardPage/ProgressByClient.tsx
index 6475c03..2392c97 100644
--- a/src/components/DashboardPage/ProgressByClient.tsx
+++ b/src/components/DashboardPage/ProgressByClient.tsx
@@ -298,7 +298,7 @@ const ProgressByClient: React.FC = () => {
const series: ApexAxisChartSeries | ApexNonAxisChartSeries = [
{
- name: "Current Stage Completion Percentage",
+ name: "Project Resource Consumption Percentage",
data: [80, 55, 40, 65, 70],
},
];
@@ -372,7 +372,7 @@ const ProgressByClient: React.FC = () => {
},
},
title: {
- text: "Current Stage Completion Percentage",
+ text: "Project Resource Consumption Percentage",
align: "center",
},
grid: {
@@ -426,7 +426,7 @@ const ProgressByClient: React.FC = () => {
-
+
{
"grade",
"skill",
"currentPosition",
- "salaryEffective",
+ "salary",
"hourlyRate",
"employType",
"email",
@@ -140,6 +140,7 @@ const EditStaff: React.FC = async () => {
label: t(`Staff ID`),
type: "text",
value: data[key] ?? "",
+ required: true,
};
case "name":
return {
@@ -147,6 +148,7 @@ const EditStaff: React.FC = async () => {
label: t(`Staff Name`),
type: "text",
value: data[key] ?? "",
+ required: true,
};
case "company":
return {
@@ -155,6 +157,7 @@ const EditStaff: React.FC = async () => {
type: "combo-Obj",
options: companyCombo,
value: data[key].id ?? "",
+ required: true,
};
case "team":
return {
@@ -171,6 +174,7 @@ const EditStaff: React.FC = async () => {
type: "combo-Obj",
options: departmentCombo,
value: data[key]?.id ?? "",
+ required: true,
// later check
};
case "grade":
@@ -179,7 +183,7 @@ const EditStaff: React.FC = async () => {
label: t(`Grade`),
type: "combo-Obj",
options: gradeCombo,
- value: data[key].id ?? "",
+ value: data[key] !== null ? data[key].id ?? "" : "",
};
case "skill":
return {
@@ -187,7 +191,7 @@ const EditStaff: React.FC = async () => {
label: t(`Skillset`),
type: "combo-Obj",
options: skillCombo,
- value: data[key].id ?? "",
+ value: data[key] !== null ? data[key].id ?? "" : "",
};
case "currentPosition":
return {
@@ -196,24 +200,26 @@ const EditStaff: React.FC = async () => {
type: "combo-Obj",
options: positionCombo,
value: data[key].id ?? "",
+ required: true,
};
- case "salaryEffective":
+ case "salary":
return {
- id: `salaryEffId`,
+ id: `salaryId`,
label: t(`Salary Point`),
type: "combo-Obj",
options: salaryCombo,
- value: data[key].salary.id ?? "",
- };
- case "hourlyRate":
- return {
- id: `${key}`,
- label: t(`hourlyRate`),
- type: "text",
- value: "",
- // value: data[key],
- readOnly: true,
+ value: data[key] !== null ? data[key].id ?? "" : "",
+ required: true,
};
+ // case "hourlyRate":
+ // return {
+ // id: `${key}`,
+ // label: t(`hourlyRate`),
+ // type: "text",
+ // value: "",
+ // // value: data[key],
+ // readOnly: true,
+ // };
case "employType":
return {
id: `${key}`,
@@ -221,6 +227,7 @@ const EditStaff: React.FC = async () => {
type: "combo-Obj",
options: employTypeCombo,
value: data[key] ?? "",
+ required: true,
};
case "email":
return {
@@ -230,22 +237,24 @@ const EditStaff: React.FC = async () => {
value: data[key] ?? "",
pattern: "^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$",
message: t("input matching format"),
+ required: true,
};
case "phone1":
return {
id: `${key}`,
label: t(`Phone1`),
type: "text",
- pattern: "^\\d{8}$",
+ // pattern: "^\\d{8}$",
message: t("input correct phone no."),
value: data[key] ?? "",
+ required: true,
};
case "phone2":
return {
id: `${key}`,
label: t(`Phone2`),
type: "text",
- pattern: "^\\d{8}$",
+ // pattern: "^\\d{8}$",
message: t("input correct phone no."),
value: data[key] ?? "",
} as Field;
@@ -263,15 +272,17 @@ const EditStaff: React.FC = async () => {
label: t(`Emergency Contact Name`),
type: "text",
value: data[key] ?? "",
+ required: true,
} as Field;
case "emergContactPhone":
return {
id: `${key}`,
label: t(`Emergency Contact Phonee`),
type: "text",
- pattern: "^\\d{8}$",
+ // pattern: "^\\d{8}$",
message: t("input correct phone no."),
value: data[key] ?? "",
+ required: true,
} as Field;
case "joinDate":
return {
@@ -279,6 +290,7 @@ const EditStaff: React.FC = async () => {
label: t(`Join Date`),
type: "multiDate",
value: data[key] ?? "",
+ required: true,
} as Field;
case "joinPosition":
return {
@@ -287,6 +299,7 @@ const EditStaff: React.FC = async () => {
type: "combo-Obj",
options: positionCombo,
value: data[key].id ?? "",
+ required: true,
} as Field;
case "departDate":
return {
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/NavigationContent/NavigationContent.tsx b/src/components/NavigationContent/NavigationContent.tsx
index 4e46632..db5b544 100644
--- a/src/components/NavigationContent/NavigationContent.tsx
+++ b/src/components/NavigationContent/NavigationContent.tsx
@@ -47,7 +47,7 @@ const navigationItems: NavigationItem[] = [
children: [
{
icon: ,
- label: "Project Financial Summary",
+ label: "Financial Summary",
path: "/dashboard/ProjectFinancialSummary",
},
{
@@ -84,12 +84,12 @@ const navigationItems: NavigationItem[] = [
children: [
{
icon: ,
- label: "ClaimApproval",
+ label: "Claim Approval",
path: "/staffReimbursement/ClaimApproval",
},
{
icon: ,
- label: "ClaimSummary",
+ label: "Claim Summary",
path: "/staffReimbursement/ClaimSummary",
},
],
diff --git a/src/components/ProgressByClient/ProgressByClient.tsx b/src/components/ProgressByClient/ProgressByClient.tsx
index 939f5d3..6815f73 100644
--- a/src/components/ProgressByClient/ProgressByClient.tsx
+++ b/src/components/ProgressByClient/ProgressByClient.tsx
@@ -333,7 +333,7 @@ const ProgressByClient: React.FC = () => {
const series: ApexAxisChartSeries | ApexNonAxisChartSeries = [
{
- name: "Current Stage Completion Percentage",
+ name: "Project Resource Consumption Percentage",
data: [80, 55, 40, 65, 70],
},
];
@@ -426,7 +426,7 @@ const ProgressByClient: React.FC = () => {
},
},
title: {
- text: "Current Stage Completion Percentage",
+ text: "Project Resource Consumption Percentage",
align: "center",
},
grid: {
@@ -503,7 +503,7 @@ const ProgressByClient: React.FC = () => {
-
+
{
const series: ApexAxisChartSeries | ApexNonAxisChartSeries = [
{
- name: "Current Stage Completion Percentage",
+ name: "Project Resource Consumption Percentage",
data: [80, 55, 40, 65, 70],
},
];
@@ -403,7 +403,7 @@ const ProgressByTeam: React.FC = () => {
},
},
title: {
- text: "Current Stage Completion Percentage",
+ text: "Project Resource Consumption Percentage",
align: "center",
},
grid: {
@@ -480,7 +480,7 @@ const ProgressByTeam: React.FC = () => {
-
+
{
className="text-sm font-medium ml-5 mt-2"
style={{ color: "#898d8d" }}
>
- Total A. Receivable
+ Total Invoiced Amount
{
className="text-sm font-medium ml-5"
style={{ color: "#898d8d" }}
>
- Amount Received
+ Total Received Amount
{
className="text-sm font-medium ml-5"
style={{ color: "#898d8d" }}
>
- Remaining Balance
+ Accounts Receivable
{
className="text-sm font-medium ml-5 mt-2"
style={{ color: "#898d8d" }}
>
- Budgeted Expenditure
+ Total Budget
{
className="text-sm font-medium ml-5"
style={{ color: "#898d8d" }}
>
- Actual Expenditure
+ Total Cumulative Expenditure
{
className="text-sm font-medium ml-5"
style={{ color: "#898d8d" }}
>
- Remaining Balance
+ Accounts Receivable
{
+ 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/SubsidiaryDetail/ContactInfo.tsx b/src/components/SubsidiaryDetail/ContactInfo.tsx
index 9e2c16d..ef84ad7 100644
--- a/src/components/SubsidiaryDetail/ContactInfo.tsx
+++ b/src/components/SubsidiaryDetail/ContactInfo.tsx
@@ -231,7 +231,7 @@ const ContactInfo: React.FC = ({
if (errorRows.length > 0) {
setError("addContacts", { message: "Contact details include empty fields", type: "required" })
} else {
- const errorRows_EmailFormat = rows.filter(row => !/^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/.test(String(row.email)))
+ const errorRows_EmailFormat = rows.filter(row => !/^([a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})$/.test(String(row.email)))
if (errorRows_EmailFormat.length > 0) {
setError("addContacts", { message: "Contact details include empty fields", type: "email_format" })
diff --git a/src/components/SubsidiaryDetail/SubsidiaryDetail.tsx b/src/components/SubsidiaryDetail/SubsidiaryDetail.tsx
index c458ce1..5f89570 100644
--- a/src/components/SubsidiaryDetail/SubsidiaryDetail.tsx
+++ b/src/components/SubsidiaryDetail/SubsidiaryDetail.tsx
@@ -75,8 +75,7 @@ const SubsidiaryDetail: React.FC = ({
const subsidiary = await fetchSubsidiary(parseInt(id))
- if (subsidiary !== null && Object.keys(subsidiary).length > 0) {
- console.log(subsidiary)
+ if (subsidiary !== null && Object.keys(subsidiary).length > 0 && !Object.values(subsidiary.subsidiary).every(x => x === null)) {
const tempSubsidiaryInput = {
id: subsidiary.subsidiary.id,
code: subsidiary.subsidiary.code ?? "",
@@ -158,7 +157,7 @@ const SubsidiaryDetail: React.FC = ({
formProps.setError("addContacts", { message: "Contact info includes empty fields", type: "required" })
}
- if (data.addContacts.length > 0 && data.addContacts.filter(row => !/^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/.test(String(row.email))).length > 0) {
+ if (data.addContacts.length > 0 && data.addContacts.filter(row => !/^([a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})$/.test(String(row.email))).length > 0) {
haveError = true
formProps.setError("addContacts", { message: "Contact info includes invalid email", type: "email_format" })
}
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;
+};
diff --git a/src/i18n/en/breadcrumb.json b/src/i18n/en/breadcrumb.json
index 72bdcc8..5361966 100644
--- a/src/i18n/en/breadcrumb.json
+++ b/src/i18n/en/breadcrumb.json
@@ -1,6 +1,6 @@
{
"Overview": "Overview",
- "customer": "Customer",
- "Create Customer": "Create Customer"
+ "customer": "Client",
+ "Create Customer": "Create Client"
}
\ No newline at end of file
diff --git a/src/i18n/en/subsidiary.json b/src/i18n/en/subsidiary.json
index d26d4e1..3c06c1c 100644
--- a/src/i18n/en/subsidiary.json
+++ b/src/i18n/en/subsidiary.json
@@ -15,8 +15,8 @@
"Customer Type": "Client Type",
"Customer Allocation": "Client Allocation",
"Search by customer code, name or br no.": "Search by client code, name or br no.",
- "Client Pool": "Client Pool",
- "Allocated Client": "Allocated Client",
+ "Customer Pool": "Client Pool",
+ "Allocated Customer": "Allocated Client",
"Please input correct subsidiary code": "Please input correct client code",
"Please input correct subsidiary name": "Please input correct client name",