| @@ -30,6 +30,17 @@ export type IndivStaff = { | |||
| data: IndividualStaff | |||
| } | |||
| export type projects = { | |||
| id: number, | |||
| code: string, | |||
| name: string, | |||
| status: string | |||
| } | |||
| // export type InvolvedProject = { | |||
| // records: projects[] | |||
| // } | |||
| export type IndividualStaff = { | |||
| id: number | |||
| staffId: string | |||
| @@ -110,6 +121,12 @@ export const fetchIndivStaff = cache(async (id: number) => { | |||
| }); | |||
| }); | |||
| export const fetchStaffInvolvedProjects = cache(async (id: number) => { | |||
| return serverFetchJson<projects[]>(`${BASE_API_URL}/staffs/staff-projects/${id}`, { | |||
| next: { tags: ["staffs"] }, | |||
| }); | |||
| }); | |||
| export const fetchStaffWithoutTeam = cache(async () => { | |||
| return serverFetchJson<StaffResult[]>(`${BASE_API_URL}/staffs/noteam`, { | |||
| next: { tags: ["staffs"] }, | |||
| @@ -11,14 +11,15 @@ import { | |||
| useForm, | |||
| } from "react-hook-form"; | |||
| import { CreateStaffInputs, saveStaff } from "@/app/api/staff/actions"; | |||
| import { Button, Stack, Typography } from "@mui/material"; | |||
| import { Button, Stack, Tab, Tabs, TabsProps, Typography } from "@mui/material"; | |||
| // import CreateStaffForm from "../CreateStaffForm"; | |||
| import { comboProp } from "@/app/api/companys/actions"; | |||
| // import StaffInfo from "./StaffInfo"; | |||
| import { Check, Close, RestartAlt } from "@mui/icons-material"; | |||
| import StaffInfo from "./StaffInfo"; | |||
| import { IndividualStaff, SalaryEffectiveInfo } from "@/app/api/staff"; | |||
| import { IndividualStaff, projects, SalaryEffectiveInfo } from "@/app/api/staff"; | |||
| import dayjs from "dayjs"; | |||
| import ProjectHistory from "./ProjectHistory"; | |||
| // import { useGridApiContext } from '@mui/x-data-grid'; | |||
| export interface comboItem { | |||
| @@ -35,14 +36,16 @@ interface formProps { | |||
| Staff: IndividualStaff | |||
| combos: comboItem; | |||
| SalaryEffectiveInfo: SalaryEffectiveInfo[]; | |||
| InvolvedProject?: projects[] | |||
| } | |||
| const EditStaff: React.FC<formProps> = ({ Staff, combos, SalaryEffectiveInfo }) => { | |||
| // console.log(Staff.joinDate) | |||
| const EditStaff: React.FC<formProps> = ({ Staff, combos, SalaryEffectiveInfo, InvolvedProject }) => { | |||
| console.log(InvolvedProject) | |||
| const defaultSkillset = Staff.skillset.map((s: any) => s.skill.id) | |||
| const { t } = useTranslation(); | |||
| const searchParams = useSearchParams() | |||
| const [tabIndex, setTabIndex] = useState(0); | |||
| const id = parseInt(searchParams.get("id") || "0"); | |||
| const formProps = useForm<CreateStaffInputs & { salaryEffectiveInfo: SalaryEffectiveInfo[] } & { delSalaryEffectiveInfo: number[] }>({ | |||
| defaultValues: { | |||
| @@ -190,6 +193,13 @@ const EditStaff: React.FC<formProps> = ({ Staff, combos, SalaryEffectiveInfo }) | |||
| return null; | |||
| } | |||
| const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>( | |||
| (_e, newValue) => { | |||
| setTabIndex(newValue); | |||
| }, | |||
| [] | |||
| ); | |||
| // const resetStaff = useCallback(() => { | |||
| // window.location.reload() | |||
| // console.log(dayjs(Staff.joinDate).format(INPUT_DATE_FORMAT)) | |||
| @@ -258,7 +268,24 @@ const EditStaff: React.FC<formProps> = ({ Staff, combos, SalaryEffectiveInfo }) | |||
| {serverError} | |||
| </Typography> | |||
| )} | |||
| {Staff && <StaffInfo combos={combos}/>} | |||
| <Stack | |||
| direction="row" | |||
| justifyContent="space-between" | |||
| flexWrap="wrap" | |||
| rowGap={2} | |||
| > | |||
| <Tabs | |||
| value={tabIndex} | |||
| onChange={handleTabChange} | |||
| variant="scrollable" | |||
| > | |||
| <Tab label={t("Info")}/> | |||
| <Tab label={t("Involved Project History")} /> | |||
| </Tabs> | |||
| </Stack> | |||
| {tabIndex == 0 && Staff && <StaffInfo combos={combos} />} | |||
| {tabIndex == 1 && <ProjectHistory InvolvedProject={InvolvedProject}/>} | |||
| {tabIndex == 0 && | |||
| <Stack direction="row" justifyContent="flex-end" gap={1}> | |||
| <Button | |||
| variant="text" | |||
| @@ -283,6 +310,7 @@ const EditStaff: React.FC<formProps> = ({ Staff, combos, SalaryEffectiveInfo }) | |||
| {t("Confirm")} | |||
| </Button> | |||
| </Stack> | |||
| } | |||
| </Stack> | |||
| </FormProvider> | |||
| </> | |||
| @@ -1,8 +1,7 @@ | |||
| import React from "react"; | |||
| import EditStaff, { comboItem } from "./EditStaff"; | |||
| import EditStaffLoading from "./EditStaffLoading"; | |||
| import { StaffResult, fetchIndivStaff, fetchStaff, fetchStaffSalaryEffectiveInfo, fetchTeamLeads, preloadStaff } from "@/app/api/staff"; | |||
| import { useSearchParams } from "next/navigation"; | |||
| import { fetchIndivStaff, fetchStaffInvolvedProjects, fetchStaffSalaryEffectiveInfo, preloadStaff } from "@/app/api/staff"; | |||
| import { fetchTeamCombo } from "@/app/api/team/actions"; | |||
| import { fetchDepartmentCombo } from "@/app/api/departments/actions"; | |||
| import { fetchPositionCombo } from "@/app/api/positions/actions"; | |||
| @@ -23,6 +22,7 @@ const EditStaffWrapper: React.FC<Props> & SubComponents = async ({ | |||
| id | |||
| }) => { | |||
| preloadStaff() | |||
| const [ | |||
| Staff, | |||
| CompanyCombo, | |||
| @@ -33,6 +33,7 @@ const EditStaffWrapper: React.FC<Props> & SubComponents = async ({ | |||
| SkillCombo, | |||
| SalaryCombo, | |||
| SalaryEffectiveInfo, | |||
| InvolvedProject | |||
| ] = await Promise.all([ | |||
| fetchIndivStaff(id), | |||
| fetchCompanyCombo(), | |||
| @@ -43,8 +44,10 @@ const EditStaffWrapper: React.FC<Props> & SubComponents = async ({ | |||
| fetchSkillCombo(), | |||
| fetchSalaryCombo(), | |||
| fetchStaffSalaryEffectiveInfo(id), | |||
| fetchStaffInvolvedProjects(id) | |||
| ]); | |||
| console.log(InvolvedProject) | |||
| console.log(SalaryCombo.records) | |||
| const combos: comboItem = { | |||
| company: CompanyCombo.records, | |||
| @@ -61,7 +64,7 @@ Staff.data.departDate = Staff.data.departDate && dateArrayToString(Staff.data.de | |||
| // [{id:0, salaryPoint: 1, date:"2021-05-05"}, {id:1, salaryPoint: 43, date:"2024-05-05"}] | |||
| console.log(Staff.data) | |||
| return <EditStaff Staff={Staff.data} combos={combos} SalaryEffectiveInfo={SalaryEffectiveInfo}/>; | |||
| return <EditStaff Staff={Staff.data} combos={combos} SalaryEffectiveInfo={SalaryEffectiveInfo} InvolvedProject={InvolvedProject}/>; | |||
| }; | |||
| EditStaffWrapper.Loading = EditStaffLoading; | |||
| @@ -0,0 +1,53 @@ | |||
| import { projects } from "@/app/api/staff"; | |||
| import { Box, Card, CardContent, Grid, Stack } from "@mui/material"; | |||
| import StyledDataGrid from "../StyledDataGrid"; | |||
| import { useMemo } from "react"; | |||
| import { useTranslation } from "react-i18next"; | |||
| interface Props { | |||
| InvolvedProject?: projects[] | |||
| } | |||
| const ProjectHistory: React.FC<Props> = async ({ InvolvedProject }) => { | |||
| const { t } = useTranslation(); | |||
| const projectCols = useMemo( | |||
| () => [ | |||
| { | |||
| field: 'code', | |||
| headerName: t("Code"), | |||
| flex: .4, | |||
| }, | |||
| { | |||
| field: 'name', | |||
| headerName: t("Name"), | |||
| flex: 1, | |||
| }, | |||
| ], [InvolvedProject]) | |||
| return ( | |||
| <Card sx={{ display: "block" }}> | |||
| <CardContent component={Stack} spacing={4}> | |||
| <Box> | |||
| <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}> | |||
| <Grid item xs={6}> | |||
| <StyledDataGrid | |||
| rows={InvolvedProject?.filter(item => item.status === "On-going") ?? []} | |||
| columns={projectCols} | |||
| /> | |||
| </Grid> | |||
| <Grid item xs={6}> | |||
| <StyledDataGrid | |||
| rows={InvolvedProject?.filter(item => item.status === "Completed") ?? []} | |||
| columns={projectCols} | |||
| /> | |||
| </Grid> | |||
| </Grid> | |||
| </Box> | |||
| </CardContent> | |||
| </Card> | |||
| ) | |||
| } | |||
| export default ProjectHistory; | |||
| @@ -16,6 +16,8 @@ import { | |||
| Checkbox, | |||
| FormControl, | |||
| InputLabel, | |||
| List, | |||
| ListItem, | |||
| ListItemText, | |||
| MenuItem, | |||
| Select, | |||
| @@ -27,10 +29,11 @@ import { DemoItem } from "@mui/x-date-pickers/internals/demo"; | |||
| import dayjs from "dayjs"; | |||
| import { INPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; | |||
| import SalaryEffectiveModel from "./SalaryEffectiveModel"; | |||
| import { SalaryEffectiveInfo } from "@/app/api/staff"; | |||
| import { SalaryEffectiveInfo, projects } from "@/app/api/staff"; | |||
| interface Props { | |||
| combos: comboItem; | |||
| // InvolvedProject?: projects[] | |||
| } | |||
| const StaffInfo: React.FC<Props> = ({ combos }) => { | |||
| @@ -411,8 +414,40 @@ const StaffInfo: React.FC<Props> = ({ combos }) => { | |||
| /> | |||
| </Grid> | |||
| </Grid> | |||
| {/* <Grid container spacing={2} columns={{ xs: 6, sm: 12 }} marginTop={3}> */} | |||
| {/* <Grid item xs={6} md={3}> | |||
| <Typography sx={{ ml: 1 }} variant="h6" component="div"> | |||
| {t("on-going")} | |||
| </Typography> | |||
| <List> | |||
| {InvolvedProject.filter((item: projects) => item.status === "On-going") | |||
| .map((item: projects) => ( | |||
| <ListItem key={item.code}> | |||
| <ListItemText | |||
| primary={item.name} | |||
| secondary={item.code} | |||
| /> | |||
| </ListItem> | |||
| )) | |||
| } | |||
| </List> | |||
| </Grid> | |||
| <Grid item xs={6} md={3}> | |||
| <Typography sx={{ ml: 1 }} variant="h6" component="div"> | |||
| {t("completed")} | |||
| </Typography> | |||
| <List> | |||
| <ListItem> | |||
| <ListItemText | |||
| primary="Single-line item" | |||
| secondary={'Secondary text'} | |||
| /> | |||
| </ListItem> | |||
| </List> | |||
| </Grid> | |||
| </Grid> */} | |||
| <Grid container spacing={2} columns={{ xs: 6, sm: 12 }} marginTop={3}> | |||
| <Grid item xs={6}> | |||
| {/* <Grid item xs={6}> | |||
| <TextField | |||
| label={t("Emergency Contact Name")} | |||
| fullWidth | |||
| @@ -447,7 +482,7 @@ const StaffInfo: React.FC<Props> = ({ combos }) => { | |||
| : t("Please input correct Emergency Contact Phone")) | |||
| } | |||
| /> | |||
| </Grid> | |||
| </Grid> */} | |||
| <Grid item xs={6}> | |||
| <LocalizationProvider | |||
| dateAdapter={AdapterDayjs} | |||
| @@ -65,8 +65,8 @@ const TimesheetMailDetails: React.FC<Props> = ({ isActive }) => { | |||
| label={t("Required Params")} | |||
| fullWidth | |||
| value={"${date}"} | |||
| disabled | |||
| error={Boolean(errors.template?.template)} | |||
| // disabled | |||
| // error={Boolean(errors.template?.template)} | |||
| /> | |||
| </Grid> | |||
| <Grid item xs={12}> | |||