Browse Source

update

tags/Baseline_30082024_FRONTEND_UAT
MSI\derek 1 year ago
parent
commit
e21e2cbda2
21 changed files with 246 additions and 35 deletions
  1. +28
    -0
      src/app/(main)/analytics/StaffMonthlyWorkHoursAnalysisReport/page.tsx
  2. +17
    -10
      src/app/(main)/settings/skill/edit/page.tsx
  3. +7
    -3
      src/app/(main)/settings/staff/user/page.tsx
  4. +12
    -2
      src/app/(main)/settings/user/edit/page.tsx
  5. +1
    -1
      src/app/api/group/actions.ts
  6. +14
    -1
      src/app/api/reports/actions.ts
  7. +15
    -1
      src/app/api/reports/index.ts
  8. +6
    -0
      src/app/api/skill/index.ts
  9. +4
    -4
      src/app/api/staff/actions.ts
  10. +0
    -1
      src/app/api/user/actions.ts
  11. +10
    -0
      src/app/api/user/index.ts
  12. +1
    -1
      src/components/CreateGroup/CreateGroupWrapper.tsx
  13. +0
    -1
      src/components/CreateTeam/StaffAllocation.tsx
  14. +7
    -4
      src/components/EditSkill/EditSkillWrapper.tsx
  15. +0
    -1
      src/components/EditTeam/Allocation.tsx
  16. +9
    -4
      src/components/EditUser/EditUserWrapper.tsx
  17. +55
    -0
      src/components/GenerateMonthlyWorkHoursReport/GenerateMonthlyWorkHoursReport.tsx
  18. +38
    -0
      src/components/GenerateMonthlyWorkHoursReport/GenerateMonthlyWorkHoursReportLoading.tsx
  19. +19
    -0
      src/components/GenerateMonthlyWorkHoursReport/GenerateMonthlyWorkHoursReportWrapper.tsx
  20. +1
    -0
      src/components/GenerateMonthlyWorkHoursReport/index.ts
  21. +2
    -1
      src/components/NavigationContent/NavigationContent.tsx

+ 28
- 0
src/app/(main)/analytics/StaffMonthlyWorkHoursAnalysisReport/page.tsx View File

@@ -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 (
<>
<Typography variant="h4" marginInlineEnd={2}>
{t("Staff Monthly Work Hours Analysis Report")}
</Typography>
<I18nProvider namespaces={["report", "common"]}>
<Suspense fallback={<GenerateMonthlyWorkHoursReport.Loading />}>
<GenerateMonthlyWorkHoursReport />
</Suspense>
</I18nProvider>
</>
);
};

export default StaffMonthlyWorkHoursAnalysisReport;

+ 17
- 10
src/app/(main)/settings/skill/edit/page.tsx View File

@@ -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<searchParamsProps> = async ({
searchParams,
}) => {
console.log(searchParams.id)
const { t } = await getServerI18n("staff");

return (
<>
<Typography variant="h4">{t("Edit Skill")}</Typography>
<I18nProvider namespaces={["team", "common"]}>
<Suspense fallback={<EditSkill.Loading />}>
<EditSkill />
</Suspense>
</I18nProvider>
{/* <EditStaff /> */}
</>
<>
<Typography variant="h4">{t("Edit Skill")}</Typography>
<I18nProvider namespaces={["team", "common"]}>
<Suspense fallback={<EditSkill.Loading />}>
<EditSkill id={parseInt(searchParams.id as string)}/>
</Suspense>
</I18nProvider>
</>
);
};



+ 7
- 3
src/app/(main)/settings/staff/user/page.tsx View File

@@ -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<searchParamsProps> = async ({
searchParams
}) => {
const { t } = await getServerI18n("user");

preloadUser()
return (
<>
<Typography variant="h4">{t("Edit User")}</Typography>
<I18nProvider namespaces={["user", "common"]}>
<Suspense fallback={<EditUser.Loading />}>
<EditUser />
<EditUser id={parseInt(searchParams.id as string)}/>
</Suspense>
</I18nProvider>
</>


+ 12
- 2
src/app/(main)/settings/user/edit/page.tsx View File

@@ -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<searchParamsProps> = async ({
searchParams
}) => {
const id = parseInt(searchParams.id as string)
preloadUserDetail(id)

return (
<>
<I18nProvider namespaces={["team", "common"]}>
<Suspense fallback={<EditUser.Loading />}>
<EditUser />
<EditUser
// id={id}
/>
</Suspense>
</I18nProvider>
</>


+ 1
- 1
src/app/api/group/actions.ts View File

@@ -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<record>(`${BASE_API_URL}/group/auth/${target}/${id ?? 0}`, {
next: { tags: ["auth"] },
});


+ 14
- 1
src/app/api/reports/actions.ts View File

@@ -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<FileResponse>(
`${BASE_API_URL}/reports/StaffMonthlyWorkHourAnalysisReport`,
{
method: "POST",
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
},
);

return reportBlob
};

+ 15
- 1
src/app/api/reports/index.ts View File

@@ -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;
}
}

// - Monthly Work Hours Report
export interface MonthlyWorkHoursReportFilter {
staff: string[];
date: any;
}

export interface MonthlyWorkHoursReportRequest {
id: number;
yearMonth: string;
}


+ 6
- 0
src/app/api/skill/index.ts View File

@@ -19,4 +19,10 @@ export interface SkillResult {
return serverFetchJson<SkillResult[]>(`${BASE_API_URL}/skill`, {
next: { tags: ["sill"] },
});
});

export const fetchSkillDetail = cache(async (id: number) => {
return serverFetchJson<SkillResult[]>(`${BASE_API_URL}/skill/${id}`, {
next: { tags: ["sill"] },
});
});

+ 4
- 4
src/app/api/staff/actions.ts View File

@@ -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<Staff4TransferList>(`${BASE_API_URL}/staffs/combo`, {
return serverFetchJson<records[]>(`${BASE_API_URL}/staffs/combo`, {
next: { tags: ["staffs"] },
});
});


+ 0
- 1
src/app/api/user/actions.ts View File

@@ -19,7 +19,6 @@ export interface PasswordInputs {
newPasswordCheck: string;
}


export const fetchUserDetails = cache(async (id: number) => {
return serverFetchJson<UserDetail>(`${BASE_API_URL}/user/${id}`, {
next: { tags: ["user"] },


+ 10
- 0
src/app/api/user/index.ts View File

@@ -38,8 +38,18 @@ export interface UserDetail {
fetchUser();
};

export const preloadUserDetail = (id: number) => {
fetchUserDetail(id);
};

export const fetchUser = cache(async () => {
return serverFetchJson<UserResult[]>(`${BASE_API_URL}/user`, {
next: { tags: ["user"] },
});
});

export const fetchUserDetail = cache(async (id: number) => {
return serverFetchJson<UserResult[]>(`${BASE_API_URL}/user/${id}`, {
next: { tags: ["user"] },
});
});

+ 1
- 1
src/components/CreateGroup/CreateGroupWrapper.tsx View File

@@ -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[]


+ 0
- 1
src/components/CreateTeam/StaffAllocation.tsx View File

@@ -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";


+ 7
- 4
src/components/EditSkill/EditSkillWrapper.tsx View File

@@ -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<Props> & SubComponents = async ({ id }) => {
const skills = await fetchSkillDetail(id)

return <EditSkill skills={skills}/>;
};


+ 0
- 1
src/components/EditTeam/Allocation.tsx View File

@@ -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";


+ 9
- 4
src/components/EditUser/EditUserWrapper.tsx View File

@@ -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<Props> & SubComponents = async ({
// id
}) => {
// const users = await fetchUser()
// const userDetail = await fetchUserDetail(id)

return <EditUser />
};


+ 55
- 0
src/components/GenerateMonthlyWorkHoursReport/GenerateMonthlyWorkHoursReport.tsx View File

@@ -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<Omit<MonthlyWorkHoursReportFilter, "id">>;
type SearchParamNames = keyof SearchQuery;

const GenerateMonthlyWorkHoursReport: React.FC<Props> = ({ staffs }) => {
const { t } = useTranslation();
const staffCombo = staffs.map(staff => `${staff.name} - ${staff.staffId}`)
console.log(staffs)

const searchCriteria: Criterion<SearchParamNames>[] = useMemo(
() => [
{ label: t("Staff"),
paramName: "staff",
type: "select",
options: staffCombo,
needAll: false},
],
[t],
);

return (
<>
<SearchBox
criteria={searchCriteria}
onSearch={async (query: any) => {
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

+ 38
- 0
src/components/GenerateMonthlyWorkHoursReport/GenerateMonthlyWorkHoursReportLoading.tsx View File

@@ -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 (
<>
<Card>
<CardContent>
<Stack spacing={2}>
<Skeleton variant="rounded" height={60} />
<Skeleton
variant="rounded"
height={50}
width={100}
sx={{ alignSelf: "flex-end" }}
/>
</Stack>
</CardContent>
</Card>
<Card>
<CardContent>
<Stack spacing={2}>
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
</Stack>
</CardContent>
</Card>
</>
);
};

export default GenerateMonthlyWorkHoursReportLoading;

+ 19
- 0
src/components/GenerateMonthlyWorkHoursReport/GenerateMonthlyWorkHoursReportWrapper.tsx View File

@@ -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 <GenerateMonthlyWorkHoursReport staffs={staffs}/>;
};

GenerateMonthlyWorkHoursReportWrapper.Loading = GenerateMonthlyWorkHoursReportLoading;

export default GenerateMonthlyWorkHoursReportWrapper;

+ 1
- 0
src/components/GenerateMonthlyWorkHoursReport/index.ts View File

@@ -0,0 +1 @@
export { default } from "./GenerateMonthlyWorkHoursReportWrapper";

+ 2
- 1
src/components/NavigationContent/NavigationContent.tsx View File

@@ -128,6 +128,7 @@ const NavigationContent: React.FC<Props> = ({ abilities }) => {
{icon: <Analytics />, label:"Project P&L Report", path: "/analytics/ProjectPLReport"},
{icon: <Analytics />, label:"Financial Status Report", path: "/analytics/FinancialStatusReport"},
{icon: <Analytics />, label:"Project Cash Flow Report", path: "/analytics/ProjectCashFlowReport"},
{icon: <Analytics />, label:"Staff Monthly Work Hours Analysis Report", path: "/analytics/StaffMonthlyWorkHoursAnalysisReport"},
],
},
{
@@ -142,7 +143,7 @@ const NavigationContent: React.FC<Props> = ({ abilities }) => {
{ icon: <Position />, label: "Position", path: "/settings/position" },
{ icon: <Salary />, label: "Salary", path: "/settings/salary" },
{ icon: <Team />, label: "Team", path: "/settings/team" },
{ icon: <ManageAccountsIcon />, label: "User", path: "/settings/user" },
// { icon: <ManageAccountsIcon />, label: "User", path: "/settings/user" },
{ icon: <ManageAccountsIcon />, label: "User Group", path: "/settings/group" },
{ icon: <Holiday />, label: "Holiday", path: "/settings/holiday" },
],


Loading…
Cancel
Save