From e21e2cbda22159502b78d04f01fd8f31df60f9a1 Mon Sep 17 00:00:00 2001 From: "MSI\\derek" Date: Mon, 13 May 2024 11:05:35 +0800 Subject: [PATCH] update --- .../page.tsx | 28 ++++++++++ src/app/(main)/settings/skill/edit/page.tsx | 27 +++++---- src/app/(main)/settings/staff/user/page.tsx | 10 +++- src/app/(main)/settings/user/edit/page.tsx | 14 ++++- src/app/api/group/actions.ts | 2 +- src/app/api/reports/actions.ts | 15 ++++- src/app/api/reports/index.ts | 16 +++++- src/app/api/skill/index.ts | 6 ++ src/app/api/staff/actions.ts | 8 +-- src/app/api/user/actions.ts | 1 - src/app/api/user/index.ts | 10 ++++ .../CreateGroup/CreateGroupWrapper.tsx | 2 +- src/components/CreateTeam/StaffAllocation.tsx | 1 - src/components/EditSkill/EditSkillWrapper.tsx | 11 ++-- src/components/EditTeam/Allocation.tsx | 1 - src/components/EditUser/EditUserWrapper.tsx | 13 +++-- .../GenerateMonthlyWorkHoursReport.tsx | 55 +++++++++++++++++++ .../GenerateMonthlyWorkHoursReportLoading.tsx | 38 +++++++++++++ .../GenerateMonthlyWorkHoursReportWrapper.tsx | 19 +++++++ .../GenerateMonthlyWorkHoursReport/index.ts | 1 + .../NavigationContent/NavigationContent.tsx | 3 +- 21 files changed, 246 insertions(+), 35 deletions(-) create mode 100644 src/app/(main)/analytics/StaffMonthlyWorkHoursAnalysisReport/page.tsx create mode 100644 src/components/GenerateMonthlyWorkHoursReport/GenerateMonthlyWorkHoursReport.tsx create mode 100644 src/components/GenerateMonthlyWorkHoursReport/GenerateMonthlyWorkHoursReportLoading.tsx create mode 100644 src/components/GenerateMonthlyWorkHoursReport/GenerateMonthlyWorkHoursReportWrapper.tsx create mode 100644 src/components/GenerateMonthlyWorkHoursReport/index.ts diff --git a/src/app/(main)/analytics/StaffMonthlyWorkHoursAnalysisReport/page.tsx b/src/app/(main)/analytics/StaffMonthlyWorkHoursAnalysisReport/page.tsx new file mode 100644 index 0000000..49ba399 --- /dev/null +++ b/src/app/(main)/analytics/StaffMonthlyWorkHoursAnalysisReport/page.tsx @@ -0,0 +1,28 @@ +import { Metadata } from "next"; +import { Suspense } from "react"; +import { I18nProvider, getServerI18n } from "@/i18n"; +import GenerateMonthlyWorkHoursReport from "@/components/GenerateMonthlyWorkHoursReport"; +import { Typography } from "@mui/material"; + +export const metadata: Metadata = { + title: "Staff Monthly Work Hours Analysis Report", +}; + +const StaffMonthlyWorkHoursAnalysisReport: React.FC = async () => { + const { t } = await getServerI18n("User Group"); + + return ( + <> + + {t("Staff Monthly Work Hours Analysis Report")} + + + }> + + + + + ); +}; + +export default StaffMonthlyWorkHoursAnalysisReport; diff --git a/src/app/(main)/settings/skill/edit/page.tsx b/src/app/(main)/settings/skill/edit/page.tsx index a2d9863..05ff61f 100644 --- a/src/app/(main)/settings/skill/edit/page.tsx +++ b/src/app/(main)/settings/skill/edit/page.tsx @@ -7,21 +7,28 @@ import { I18nProvider, getServerI18n } from "@/i18n"; import { Metadata } from "next"; import EditSkill from "@/components/EditSkill"; import { Typography } from "@mui/material"; +import { fetchSkill } from "@/app/api/skill"; +export interface searchParamsProps { + searchParams: { [key: string]: string | string[] | undefined }; +} -const EditSkillPage: React.FC = async () => { +const EditSkillPage: React.FC = async ({ + searchParams, +}) => { + console.log(searchParams.id) const { t } = await getServerI18n("staff"); return ( - <> - {t("Edit Skill")} - - }> - - - - {/* */} - + + <> + {t("Edit Skill")} + + }> + + + + ); }; diff --git a/src/app/(main)/settings/staff/user/page.tsx b/src/app/(main)/settings/staff/user/page.tsx index 11ef8f9..544140c 100644 --- a/src/app/(main)/settings/staff/user/page.tsx +++ b/src/app/(main)/settings/staff/user/page.tsx @@ -4,16 +4,20 @@ import { I18nProvider, getServerI18n } from "@/i18n"; import EditUser from "@/components/EditUser"; import { Typography } from "@mui/material"; import { Suspense } from "react"; +import { searchParamsProps } from "../../skill/edit/page"; +import { preloadUser } from "@/app/api/user"; -const User: React.FC = async () => { +const User: React.FC = async ({ + searchParams +}) => { const { t } = await getServerI18n("user"); - + preloadUser() return ( <> {t("Edit User")} }> - + diff --git a/src/app/(main)/settings/user/edit/page.tsx b/src/app/(main)/settings/user/edit/page.tsx index 659347b..a789ea9 100644 --- a/src/app/(main)/settings/user/edit/page.tsx +++ b/src/app/(main)/settings/user/edit/page.tsx @@ -6,15 +6,25 @@ import { I18nProvider } from "@/i18n"; // import EditStaffWrapper from "@/components/EditStaff/EditStaffWrapper"; import { Metadata } from "next"; import EditUser from "@/components/EditUser"; +import { useRouter } from 'next/router'; +import { getServerSideProps } from "next/dist/build/templates/pages"; +import { searchParamsProps } from "../../skill/edit/page"; +import { preloadUserDetail } from "@/app/api/user"; -const EditUserPage: React.FC = () => { +const EditUserPage: React.FC = async ({ + searchParams +}) => { + const id = parseInt(searchParams.id as string) + preloadUserDetail(id) return ( <> }> - + diff --git a/src/app/api/group/actions.ts b/src/app/api/group/actions.ts index 6800b29..1122c16 100644 --- a/src/app/api/group/actions.ts +++ b/src/app/api/group/actions.ts @@ -29,7 +29,7 @@ export interface record { records: auth[]; } -export const fetchAuth = cache(async (target: string, id?: number) => { +export const fetchAuth = cache(async (target: string, id?: number ) => { return serverFetchJson(`${BASE_API_URL}/group/auth/${target}/${id ?? 0}`, { next: { tags: ["auth"] }, }); diff --git a/src/app/api/reports/actions.ts b/src/app/api/reports/actions.ts index 8f38af3..1c4a175 100644 --- a/src/app/api/reports/actions.ts +++ b/src/app/api/reports/actions.ts @@ -1,7 +1,7 @@ "use server"; import { serverFetchBlob, serverFetchJson } from "@/app/utils/fetchUtil"; -import { ProjectCashFlowReportRequest } from "."; +import { MonthlyWorkHoursReportRequest, ProjectCashFlowReportRequest } from "."; import { BASE_API_URL } from "@/config/api"; export interface FileResponse { @@ -19,5 +19,18 @@ export const fetchProjectCashFlowReport = async (data: ProjectCashFlowReportRequ }, ); + return reportBlob +}; + +export const fetchMonthlyWorkHoursReport = async (data: MonthlyWorkHoursReportRequest) => { + const reportBlob = await serverFetchBlob( + `${BASE_API_URL}/reports/StaffMonthlyWorkHourAnalysisReport`, + { + method: "POST", + body: JSON.stringify(data), + headers: { "Content-Type": "application/json" }, + }, + ); + return reportBlob }; \ No newline at end of file diff --git a/src/app/api/reports/index.ts b/src/app/api/reports/index.ts index 9ab8f36..bdc5cbe 100644 --- a/src/app/api/reports/index.ts +++ b/src/app/api/reports/index.ts @@ -1,3 +1,5 @@ +import { records } from "../staff/actions"; + // - Project Cash Flow Report export interface ProjectCashFlowReportFilter { project: string[]; @@ -5,4 +7,16 @@ export interface ProjectCashFlowReportFilter { export interface ProjectCashFlowReportRequest { projectId: number; -} \ No newline at end of file +} + +// - Monthly Work Hours Report +export interface MonthlyWorkHoursReportFilter { + staff: string[]; + date: any; +} + +export interface MonthlyWorkHoursReportRequest { + id: number; + yearMonth: string; +} + diff --git a/src/app/api/skill/index.ts b/src/app/api/skill/index.ts index cf6ebec..a235426 100644 --- a/src/app/api/skill/index.ts +++ b/src/app/api/skill/index.ts @@ -19,4 +19,10 @@ export interface SkillResult { return serverFetchJson(`${BASE_API_URL}/skill`, { next: { tags: ["sill"] }, }); + }); + + export const fetchSkillDetail = cache(async (id: number) => { + return serverFetchJson(`${BASE_API_URL}/skill/${id}`, { + next: { tags: ["sill"] }, + }); }); \ No newline at end of file diff --git a/src/app/api/staff/actions.ts b/src/app/api/staff/actions.ts index a2235d6..b1559de 100644 --- a/src/app/api/staff/actions.ts +++ b/src/app/api/staff/actions.ts @@ -39,9 +39,9 @@ export interface CreateStaffInputs { name: string; // team: Team[]; } - export interface Staff4TransferList { - records: records[]; - } + // export interface Staff4TransferList { + // records: records[]; + // } export const saveStaff = async (data: CreateStaffInputs) => { return serverFetchJson(`${BASE_API_URL}/staffs/save`, { @@ -79,7 +79,7 @@ export const fetchStaffEdit = cache(async (id: number) => { // }; export const fetchStaffCombo = cache(async () => { - return serverFetchJson(`${BASE_API_URL}/staffs/combo`, { + return serverFetchJson(`${BASE_API_URL}/staffs/combo`, { next: { tags: ["staffs"] }, }); }); diff --git a/src/app/api/user/actions.ts b/src/app/api/user/actions.ts index 77b58b5..919634c 100644 --- a/src/app/api/user/actions.ts +++ b/src/app/api/user/actions.ts @@ -19,7 +19,6 @@ export interface PasswordInputs { newPasswordCheck: string; } - export const fetchUserDetails = cache(async (id: number) => { return serverFetchJson(`${BASE_API_URL}/user/${id}`, { next: { tags: ["user"] }, diff --git a/src/app/api/user/index.ts b/src/app/api/user/index.ts index f34292f..369c62d 100644 --- a/src/app/api/user/index.ts +++ b/src/app/api/user/index.ts @@ -38,8 +38,18 @@ export interface UserDetail { fetchUser(); }; + export const preloadUserDetail = (id: number) => { + fetchUserDetail(id); + }; + export const fetchUser = cache(async () => { return serverFetchJson(`${BASE_API_URL}/user`, { next: { tags: ["user"] }, }); + }); + + export const fetchUserDetail = cache(async (id: number) => { + return serverFetchJson(`${BASE_API_URL}/user/${id}`, { + next: { tags: ["user"] }, + }); }); \ No newline at end of file diff --git a/src/components/CreateGroup/CreateGroupWrapper.tsx b/src/components/CreateGroup/CreateGroupWrapper.tsx index e4bd018..b971f7e 100644 --- a/src/components/CreateGroup/CreateGroupWrapper.tsx +++ b/src/components/CreateGroup/CreateGroupWrapper.tsx @@ -11,7 +11,7 @@ interface SubComponents { } const CreateGroupWrapper: React.FC & SubComponents = async () => { - const records = await fetchAuth() + const records = await fetchAuth("group") const users = await fetchUser() console.log(users) const auth = records.records as auth[] diff --git a/src/components/CreateTeam/StaffAllocation.tsx b/src/components/CreateTeam/StaffAllocation.tsx index bbd768c..6326561 100644 --- a/src/components/CreateTeam/StaffAllocation.tsx +++ b/src/components/CreateTeam/StaffAllocation.tsx @@ -13,7 +13,6 @@ import { } from "react-hook-form"; // import CreateTeamForm from "../CreateTeamForm"; import { CreateTeamInputs } from "@/app/api/team/actions"; -// import { Staff4TransferList, fetchStaffCombo } from "@/app/api/staff/actions"; import { StaffResult } from "@/app/api/staff"; import SearchResults, { Column } from "../SearchResults"; import { Clear, PersonAdd, PersonRemove, Search } from "@mui/icons-material"; diff --git a/src/components/EditSkill/EditSkillWrapper.tsx b/src/components/EditSkill/EditSkillWrapper.tsx index 12d7a12..dabb621 100644 --- a/src/components/EditSkill/EditSkillWrapper.tsx +++ b/src/components/EditSkill/EditSkillWrapper.tsx @@ -3,15 +3,18 @@ import EditSkill from "./EditSkill"; import EditSkillLoading from "./EditSkillLoading"; import { fetchStaff, fetchTeamLeads } from "@/app/api/staff"; import { useSearchParams } from "next/navigation"; -import { fetchSkill } from "@/app/api/skill"; +import { fetchSkill, fetchSkillDetail } from "@/app/api/skill"; interface SubComponents { Loading: typeof EditSkillLoading; } -const EditSkillWrapper: React.FC & SubComponents = async () => { - const skills = await fetchSkill() - console.log(skills) +interface Props { + id: number +} + +const EditSkillWrapper: React.FC & SubComponents = async ({ id }) => { + const skills = await fetchSkillDetail(id) return ; }; diff --git a/src/components/EditTeam/Allocation.tsx b/src/components/EditTeam/Allocation.tsx index f1386fe..b732168 100644 --- a/src/components/EditTeam/Allocation.tsx +++ b/src/components/EditTeam/Allocation.tsx @@ -12,7 +12,6 @@ import { useFormContext, } from "react-hook-form"; import { CreateTeamInputs } from "@/app/api/team/actions"; -import { Staff4TransferList, fetchStaffCombo } from "@/app/api/staff/actions"; import { StaffResult, StaffTeamTable } from "@/app/api/staff"; import SearchResults, { Column } from "../SearchResults"; import { Clear, PersonAdd, PersonRemove, Search } from "@mui/icons-material"; diff --git a/src/components/EditUser/EditUserWrapper.tsx b/src/components/EditUser/EditUserWrapper.tsx index 10be6f2..6d41a92 100644 --- a/src/components/EditUser/EditUserWrapper.tsx +++ b/src/components/EditUser/EditUserWrapper.tsx @@ -5,15 +5,20 @@ import EditUserLoading from "./EditUserLoading"; import { useSearchParams } from "next/navigation"; import { fetchTeam, fetchTeamDetail } from "@/app/api/team"; import { fetchStaff } from "@/app/api/staff"; -import { fetchUser } from "@/app/api/user"; +import { fetchUser, fetchUserDetail } from "@/app/api/user"; interface SubComponents { Loading: typeof EditUserLoading; } -const EditUserWrapper: React.FC & SubComponents = async () => { - const users = await fetchUser() - console.log(users) +interface Props { + // id: number +} +const EditUserWrapper: React.FC & SubComponents = async ({ + // id +}) => { + // const users = await fetchUser() + // const userDetail = await fetchUserDetail(id) return }; diff --git a/src/components/GenerateMonthlyWorkHoursReport/GenerateMonthlyWorkHoursReport.tsx b/src/components/GenerateMonthlyWorkHoursReport/GenerateMonthlyWorkHoursReport.tsx new file mode 100644 index 0000000..417a182 --- /dev/null +++ b/src/components/GenerateMonthlyWorkHoursReport/GenerateMonthlyWorkHoursReport.tsx @@ -0,0 +1,55 @@ +"use client"; +import React, { useMemo } from "react"; +import SearchBox, { Criterion } from "../SearchBox"; +import { useTranslation } from "react-i18next"; +import { ProjectResult } from "@/app/api/projects"; +import { fetchMonthlyWorkHoursReport, fetchProjectCashFlowReport } from "@/app/api/reports/actions"; +import { downloadFile } from "@/app/utils/commonUtil"; +import { BASE_API_URL } from "@/config/api"; +import { MonthlyWorkHoursReportFilter } from "@/app/api/reports"; +import { records } from "@/app/api/staff/actions"; +import { StaffResult } from "@/app/api/staff"; + +interface Props { + staffs: StaffResult[] +} + +type SearchQuery = Partial>; +type SearchParamNames = keyof SearchQuery; + +const GenerateMonthlyWorkHoursReport: React.FC = ({ staffs }) => { + const { t } = useTranslation(); + const staffCombo = staffs.map(staff => `${staff.name} - ${staff.staffId}`) + console.log(staffs) + + const searchCriteria: Criterion[] = useMemo( + () => [ + { label: t("Staff"), + paramName: "staff", + type: "select", + options: staffCombo, + needAll: false}, + ], + [t], + ); + +return ( + <> + { + if (query.staff.length > 0 && query.staff.toLocaleLowerCase() !== "all") { + const index = staffCombo.findIndex(staff => staff === query.staff) + const response = await fetchMonthlyWorkHoursReport({ id: staffs[index].id, yearMonth: "2023-03" }) + if (response) { + downloadFile(new Uint8Array(response.blobValue), response.filename!!) + } + } + } + } + /> + + ) +} + +export default GenerateMonthlyWorkHoursReport \ No newline at end of file diff --git a/src/components/GenerateMonthlyWorkHoursReport/GenerateMonthlyWorkHoursReportLoading.tsx b/src/components/GenerateMonthlyWorkHoursReport/GenerateMonthlyWorkHoursReportLoading.tsx new file mode 100644 index 0000000..9ac92ea --- /dev/null +++ b/src/components/GenerateMonthlyWorkHoursReport/GenerateMonthlyWorkHoursReportLoading.tsx @@ -0,0 +1,38 @@ +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 GenerateMonthlyWorkHoursReportLoading: React.FC = () => { + return ( + <> + + + + + + + + + + + + + + + + + + + + ); +}; + +export default GenerateMonthlyWorkHoursReportLoading; \ No newline at end of file diff --git a/src/components/GenerateMonthlyWorkHoursReport/GenerateMonthlyWorkHoursReportWrapper.tsx b/src/components/GenerateMonthlyWorkHoursReport/GenerateMonthlyWorkHoursReportWrapper.tsx new file mode 100644 index 0000000..4ce7ac9 --- /dev/null +++ b/src/components/GenerateMonthlyWorkHoursReport/GenerateMonthlyWorkHoursReportWrapper.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import GenerateMonthlyWorkHoursReportLoading from "./GenerateMonthlyWorkHoursReportLoading"; +import { fetchProjects } from "@/app/api/projects"; +import GenerateMonthlyWorkHoursReport from "./GenerateMonthlyWorkHoursReport"; +import { fetchStaff } from "@/app/api/staff"; + +interface SubComponents { + Loading: typeof GenerateMonthlyWorkHoursReportLoading; +} + +const GenerateMonthlyWorkHoursReportWrapper: React.FC & SubComponents = async () => { + const staffs = await fetchStaff(); + + return ; +}; + +GenerateMonthlyWorkHoursReportWrapper.Loading = GenerateMonthlyWorkHoursReportLoading; + +export default GenerateMonthlyWorkHoursReportWrapper; \ No newline at end of file diff --git a/src/components/GenerateMonthlyWorkHoursReport/index.ts b/src/components/GenerateMonthlyWorkHoursReport/index.ts new file mode 100644 index 0000000..7c5871e --- /dev/null +++ b/src/components/GenerateMonthlyWorkHoursReport/index.ts @@ -0,0 +1 @@ +export { default } from "./GenerateMonthlyWorkHoursReportWrapper"; \ No newline at end of file diff --git a/src/components/NavigationContent/NavigationContent.tsx b/src/components/NavigationContent/NavigationContent.tsx index 36beaed..4b47149 100644 --- a/src/components/NavigationContent/NavigationContent.tsx +++ b/src/components/NavigationContent/NavigationContent.tsx @@ -128,6 +128,7 @@ const NavigationContent: React.FC = ({ abilities }) => { {icon: , label:"Project P&L Report", path: "/analytics/ProjectPLReport"}, {icon: , label:"Financial Status Report", path: "/analytics/FinancialStatusReport"}, {icon: , label:"Project Cash Flow Report", path: "/analytics/ProjectCashFlowReport"}, + {icon: , label:"Staff Monthly Work Hours Analysis Report", path: "/analytics/StaffMonthlyWorkHoursAnalysisReport"}, ], }, { @@ -142,7 +143,7 @@ const NavigationContent: React.FC = ({ abilities }) => { { icon: , label: "Position", path: "/settings/position" }, { icon: , label: "Salary", path: "/settings/salary" }, { icon: , label: "Team", path: "/settings/team" }, - { icon: , label: "User", path: "/settings/user" }, + // { icon: , label: "User", path: "/settings/user" }, { icon: , label: "User Group", path: "/settings/group" }, { icon: , label: "Holiday", path: "/settings/holiday" }, ],