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