| @@ -28,18 +28,6 @@ import { ProjectCategory } from "@/app/api/projects"; | |||||
| import { Grid, Typography } from "@mui/material"; | import { Grid, Typography } from "@mui/material"; | ||||
| import CreateStaffForm from "@/components/CreateStaff/CreateStaffForm"; | import CreateStaffForm from "@/components/CreateStaff/CreateStaffForm"; | ||||
| // import { Metadata } from "next"; | |||||
| // export const metadata: Metadata = { | |||||
| // title: "staffCreate", | |||||
| // }; | |||||
| // export interface Props { | |||||
| // allTasks: Task[]; | |||||
| // projectCategories: ProjectCategory[]; | |||||
| // taskTemplates: TaskTemplate[]; | |||||
| // teamLeads: Staff[]; | |||||
| // } | |||||
| interface CreateCustomInputs { | interface CreateCustomInputs { | ||||
| projectCode: string; | projectCode: string; | ||||
| projectName: string; | projectName: string; | ||||
| @@ -52,65 +40,84 @@ const createCustomInputs: CreateCustomInputs = { | |||||
| // const Title = ["title1", "title2"]; | // const Title = ["title1", "title2"]; | ||||
| const CreateStaff: React.FC = async () => { | const CreateStaff: React.FC = async () => { | ||||
| const { t } = await getServerI18n("createStaff"); | |||||
| const title = ['', 'Additional Info'] | |||||
| const { t } = await getServerI18n("staff"); | |||||
| const title = ['', t('Additional Info')] | |||||
| // const regex = new RegExp("^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$") | |||||
| // console.log(regex) | |||||
| const fieldLists = [ | const fieldLists = [ | ||||
| [ | [ | ||||
| { | { | ||||
| id: "name", | |||||
| label: t("Staff Id"), | |||||
| id: "staffId", | |||||
| label: t("Staff ID"), | |||||
| type: "text", | type: "text", | ||||
| value: "asdasd", | |||||
| // required: "asdasd", | |||||
| // option: "asdasd", | |||||
| value: "", | |||||
| pattern: "^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$", | |||||
| message: t("input matching format"), | |||||
| required: true, | |||||
| }, | }, | ||||
| { | { | ||||
| id: "name", | id: "name", | ||||
| label: t("Staff Name"), | label: t("Staff Name"), | ||||
| type: "text", | type: "text", | ||||
| value: "asdasd", | value: "asdasd", | ||||
| // required: "asdasd", | |||||
| // option: "asdasd", | |||||
| required: true, | |||||
| }, | }, | ||||
| { | { | ||||
| id: "companyId", | id: "companyId", | ||||
| label: t("Company"), | label: t("Company"), | ||||
| type: "combo-Obj", | type: "combo-Obj", | ||||
| // value: "asdasd", | |||||
| // required: "asdasd", | |||||
| options: [{id: 1, key: 1, value: 1, label: "Company A"}, {id: 2, key: 2, value: 2, label: "Company B"}], | options: [{id: 1, key: 1, value: 1, label: "Company A"}, {id: 2, key: 2, value: 2, label: "Company B"}], | ||||
| required: true, | |||||
| }, | }, | ||||
| { | { | ||||
| id: "teamId", | id: "teamId", | ||||
| label: t("Team"), | label: t("Team"), | ||||
| type: "combo-Obj", | type: "combo-Obj", | ||||
| options: [{id: 1, key: 1, value: 1, label: "A"}, {id: 2, key: 2, value: 2, label: "B"}], | options: [{id: 1, key: 1, value: 1, label: "A"}, {id: 2, key: 2, value: 2, label: "B"}], | ||||
| required: true, | |||||
| }, | |||||
| { | |||||
| id: "departmentId", | |||||
| label: t("Department"), | |||||
| type: "combo-Obj", | |||||
| options: [{id: 1, key: 1, value: 1, label: "Department A"}, {id: 2, key: 2, value: 2, label: "Department B"}], | |||||
| required: true, | |||||
| }, | }, | ||||
| { | { | ||||
| id: "gradeId", | id: "gradeId", | ||||
| label: t("Grade"), | label: t("Grade"), | ||||
| type: "combo-Obj", | type: "combo-Obj", | ||||
| options: [{id: 1, key: 1, value: 1, label: "A"}, {id: 2, key: 2, value: 2, label: "B"}], | options: [{id: 1, key: 1, value: 1, label: "A"}, {id: 2, key: 2, value: 2, label: "B"}], | ||||
| required: true, | |||||
| }, | |||||
| { | |||||
| id: "skillSetId", | |||||
| label: t("Skillset"), | |||||
| type: "combo-Obj", | |||||
| options: [{id: 1, key: 1, value: 1, label: "excel"}, {id: 2, key: 2, value: 2, label: "word"}], | |||||
| required: true, | |||||
| }, | }, | ||||
| { | { | ||||
| id: "currentPosition", | |||||
| id: "currentPositionId", | |||||
| label: t("Current Position"), | label: t("Current Position"), | ||||
| type: "combo-Obj", | type: "combo-Obj", | ||||
| options: [{id: 1, key: 1, value: 1, label: "pos1"}, {id: 2, key: 2, value: 2, label: "pos2"}], | options: [{id: 1, key: 1, value: 1, label: "pos1"}, {id: 2, key: 2, value: 2, label: "pos2"}], | ||||
| required: true, | |||||
| }, | }, | ||||
| { | { | ||||
| id: "salaryEffId", | id: "salaryEffId", | ||||
| label: t("Salary point ID with effective date"), | |||||
| label: t("Salary Point"), | |||||
| type: "combo-Obj", | type: "combo-Obj", | ||||
| options: [{id: 1, key: 1, value: 1, label: t("first")}, {id: 2, key: 2, value: 2, label: t("second")}], | |||||
| options: [{id: 1, key: 1, value: 1, label: t("15")}, {id: 2, key: 2, value: 2, label: t("20")}], | |||||
| required: true, | |||||
| }, | }, | ||||
| { | { | ||||
| id: "hourlyRate", | id: "hourlyRate", | ||||
| label: t("Hourly Rate"), | label: t("Hourly Rate"), | ||||
| type: "numeric", | type: "numeric", | ||||
| value: "", | value: "", | ||||
| required: true, | |||||
| }, | }, | ||||
| { | { | ||||
| id: "employType", | id: "employType", | ||||
| @@ -118,25 +125,29 @@ const CreateStaff: React.FC = async () => { | |||||
| type: "combo-Obj", | type: "combo-Obj", | ||||
| options: [{id: 1, key: "FT", value: "FT", label: t("FT")}, {id: 2, key: "PT", value: "PT", label: t("PT")}], | options: [{id: 1, key: "FT", value: "FT", label: t("FT")}, {id: 2, key: "PT", value: "PT", label: t("PT")}], | ||||
| value: "", | value: "", | ||||
| required: true, | |||||
| }, | }, | ||||
| { | { | ||||
| id: "email", | id: "email", | ||||
| label: t("Email"), | label: t("Email"), | ||||
| type: "text", | |||||
| type: "email", | |||||
| value: "", | value: "", | ||||
| required: true, | |||||
| }, | }, | ||||
| { | { | ||||
| id: "phone1", | id: "phone1", | ||||
| label: t("Phone1"), | label: t("Phone1"), | ||||
| type: "numeric", | type: "numeric", | ||||
| value: "", | value: "", | ||||
| required: true, | |||||
| }, | }, | ||||
| { | { | ||||
| id: "phone2", | id: "phone2", | ||||
| label: t("Phone2"), | label: t("Phone2"), | ||||
| type: "numeric", | type: "numeric", | ||||
| value: "", | value: "", | ||||
| }, | |||||
| required: true, | |||||
| }, | |||||
| ], | ], | ||||
| [ | [ | ||||
| { | { | ||||
| @@ -144,24 +155,28 @@ const CreateStaff: React.FC = async () => { | |||||
| label: t("Emergency Contact Name"), | label: t("Emergency Contact Name"), | ||||
| type: "text", | type: "text", | ||||
| value: "", | value: "", | ||||
| required: true, | |||||
| }, | }, | ||||
| { | { | ||||
| id: "emergContactPhone", | id: "emergContactPhone", | ||||
| label: t("Emergency Contact Phone"), | label: t("Emergency Contact Phone"), | ||||
| type: "numeric", | type: "numeric", | ||||
| value: "", | value: "", | ||||
| required: true, | |||||
| }, | }, | ||||
| { | { | ||||
| id: "joinDate", | id: "joinDate", | ||||
| label: t("Join Date"), | label: t("Join Date"), | ||||
| type: "multiDate", | type: "multiDate", | ||||
| value: "", | value: "", | ||||
| required: true, | |||||
| }, | }, | ||||
| { | { | ||||
| id: "joinPosition", | |||||
| id: "joinPositionId", | |||||
| label: t("Join Position"), | label: t("Join Position"), | ||||
| type: "combo-Obj", | type: "combo-Obj", | ||||
| options: [{id: 1, key: 1, value: 1, label: "pos1"}, {id: 2, key: 2, value: 2, label: "pos2"}], | options: [{id: 1, key: 1, value: 1, label: "pos1"}, {id: 2, key: 2, value: 2, label: "pos2"}], | ||||
| required: true, | |||||
| }, | }, | ||||
| { | { | ||||
| id: "departDate", | id: "departDate", | ||||
| @@ -171,7 +186,7 @@ const CreateStaff: React.FC = async () => { | |||||
| }, | }, | ||||
| { | { | ||||
| id: "departReason", | id: "departReason", | ||||
| label: t("Reason"), | |||||
| label: t("Depart Reason"), | |||||
| type: "text", | type: "text", | ||||
| value: "", | value: "", | ||||
| }, | }, | ||||
| @@ -187,7 +202,7 @@ const CreateStaff: React.FC = async () => { | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| <Typography variant="h4">{t("Create Staff")}</Typography> | <Typography variant="h4">{t("Create Staff")}</Typography> | ||||
| <I18nProvider namespaces={["createStaff"]}> | |||||
| <I18nProvider namespaces={["staff"]}> | |||||
| <CreateStaffForm | <CreateStaffForm | ||||
| Title={title} | Title={title} | ||||
| fieldLists={fieldLists} | fieldLists={fieldLists} | ||||
| @@ -2,7 +2,7 @@ | |||||
| import { preloadClaims } from "@/app/api/claims"; | import { preloadClaims } from "@/app/api/claims"; | ||||
| import { preloadStaff, preloadTeamLeads } from "@/app/api/staff"; | import { preloadStaff, preloadTeamLeads } from "@/app/api/staff"; | ||||
| import StaffSearch from "@/components/StaffSearch"; | import StaffSearch from "@/components/StaffSearch"; | ||||
| import { getServerI18n } from "@/i18n"; | |||||
| import { I18nProvider, getServerI18n } from "@/i18n"; | |||||
| import Add from "@mui/icons-material/Add"; | import Add from "@mui/icons-material/Add"; | ||||
| import Button from "@mui/material/Button"; | import Button from "@mui/material/Button"; | ||||
| import Stack from "@mui/material/Stack"; | import Stack from "@mui/material/Stack"; | ||||
| @@ -16,9 +16,9 @@ export const metadata: Metadata = { | |||||
| }; | }; | ||||
| const Staff: React.FC = async () => { | const Staff: React.FC = async () => { | ||||
| const { t } = await getServerI18n("projects"); | |||||
| preloadTeamLeads() | |||||
| preloadStaff() | |||||
| const { t } = await getServerI18n("staff"); | |||||
| preloadTeamLeads(); | |||||
| preloadStaff(); | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| <Stack | <Stack | ||||
| @@ -39,9 +39,11 @@ const Staff: React.FC = async () => { | |||||
| {t("Create Staff")} | {t("Create Staff")} | ||||
| </Button> | </Button> | ||||
| </Stack> | </Stack> | ||||
| <Suspense fallback={<StaffSearch.Loading />}> | |||||
| <StaffSearch /> | |||||
| </Suspense> | |||||
| <I18nProvider namespaces={["staff", "common"]}> | |||||
| <Suspense fallback={<StaffSearch.Loading />}> | |||||
| <StaffSearch /> | |||||
| </Suspense> | |||||
| </I18nProvider> | |||||
| </> | </> | ||||
| ); | ); | ||||
| }; | }; | ||||
| @@ -25,12 +25,13 @@ export interface CreateStaffInputs { | |||||
| emergContactName: string; | emergContactName: string; | ||||
| emergContactPhone: string; | emergContactPhone: string; | ||||
| employType: string; | employType: string; | ||||
| departDate: string; | |||||
| departReason: string; | |||||
| remark: string; | |||||
| departDate: string | null; | |||||
| departReason: string | null; | |||||
| remark: string | null; | |||||
| } | } | ||||
| export const saveStaff = async (data: CreateStaffInputs) => { | export const saveStaff = async (data: CreateStaffInputs) => { | ||||
| console.log(`${BASE_API_URL}/staffs/new`) | |||||
| return serverFetchJson(`${BASE_API_URL}/staffs/new`, { | return serverFetchJson(`${BASE_API_URL}/staffs/new`, { | ||||
| method: "POST", | method: "POST", | ||||
| body: JSON.stringify(data), | body: JSON.stringify(data), | ||||
| @@ -4,6 +4,7 @@ import { cache } from "react"; | |||||
| import "server-only"; | import "server-only"; | ||||
| export interface StaffResult { | export interface StaffResult { | ||||
| action: any; | |||||
| id: number; | id: number; | ||||
| name: string; | name: string; | ||||
| team: string; | team: string; | ||||
| @@ -37,16 +37,10 @@ const CreateStaffForm: React.FC<formProps> = ({ | |||||
| Title, | Title, | ||||
| fieldLists | fieldLists | ||||
| }) => { | }) => { | ||||
| // const [formData, setFormData] = useState<any>(null); | |||||
| const router = useRouter(); | const router = useRouter(); | ||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const [serverError, setServerError] = useState(""); | const [serverError, setServerError] = useState(""); | ||||
| // const handleSubmit = (data: any) => { | |||||
| // console.log(data); | |||||
| // // Handle the form submission logic here | |||||
| // // setFormData(data); | |||||
| // }; | |||||
| const handleCancel = () => { | const handleCancel = () => { | ||||
| router.back(); | router.back(); | ||||
| }; | }; | ||||
| @@ -54,8 +48,16 @@ const CreateStaffForm: React.FC<formProps> = ({ | |||||
| async (data) => { | async (data) => { | ||||
| try { | try { | ||||
| console.log(data); | console.log(data); | ||||
| const postData = { | |||||
| ...data, | |||||
| emergContactPhone: data.emergContactPhone.toString(), | |||||
| phone1: data.phone1.toString(), | |||||
| phone2: data.phone2.toString(), | |||||
| } | |||||
| console.log(postData); | |||||
| setServerError(""); | setServerError(""); | ||||
| await saveStaff(data); | |||||
| await saveStaff(postData); | |||||
| router.replace("/staff"); | |||||
| } catch (e) { | } catch (e) { | ||||
| setServerError(t("An error has occurred. Please try again later.")); | setServerError(t("An error has occurred. Please try again later.")); | ||||
| } | } | ||||
| @@ -63,6 +65,13 @@ const CreateStaffForm: React.FC<formProps> = ({ | |||||
| [router, t] | [router, t] | ||||
| ); | ); | ||||
| const onSubmitError = useCallback<SubmitErrorHandler<CreateStaffInputs>>( | |||||
| (errors) => { | |||||
| console.log(errors) | |||||
| }, | |||||
| [], | |||||
| ); | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| {serverError && ( | {serverError && ( | ||||
| @@ -75,6 +84,7 @@ const CreateStaffForm: React.FC<formProps> = ({ | |||||
| fieldLists={fieldLists} | fieldLists={fieldLists} | ||||
| isActive={true} | isActive={true} | ||||
| onSubmit={onSubmit} | onSubmit={onSubmit} | ||||
| onSubmitError={onSubmitError} | |||||
| onCancel={handleCancel} | onCancel={handleCancel} | ||||
| /> | /> | ||||
| </> | </> | ||||
| @@ -27,7 +27,7 @@ import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; | |||||
| import { DemoItem } from "@mui/x-date-pickers/internals/demo"; | import { DemoItem } from "@mui/x-date-pickers/internals/demo"; | ||||
| import { DatePicker } from "@mui/x-date-pickers/DatePicker"; | import { DatePicker } from "@mui/x-date-pickers/DatePicker"; | ||||
| import dayjs from "dayjs"; | import dayjs from "dayjs"; | ||||
| import { useEffect, useState } from "react"; | |||||
| import { useCallback, useEffect, useState } from "react"; | |||||
| import { Check, Close } from "@mui/icons-material"; | import { Check, Close } from "@mui/icons-material"; | ||||
| // interface Option { | // interface Option { | ||||
| @@ -45,6 +45,8 @@ interface Field { | |||||
| type: string; | type: string; | ||||
| value?: any; | value?: any; | ||||
| required?: boolean; | required?: boolean; | ||||
| pattern?: string; | |||||
| message?: string; | |||||
| options?: any[]; | options?: any[]; | ||||
| readOnly?: boolean; | readOnly?: boolean; | ||||
| size?: number; | size?: number; | ||||
| @@ -53,6 +55,7 @@ interface Field { | |||||
| interface CustomInputFormProps { | interface CustomInputFormProps { | ||||
| onSubmit: (data: any) => void; | onSubmit: (data: any) => void; | ||||
| onSubmitError?: (data: any) => void; | |||||
| onCancel: () => void; | onCancel: () => void; | ||||
| // resetForm: () => void; | // resetForm: () => void; | ||||
| Title?: string[]; | Title?: string[]; | ||||
| @@ -60,20 +63,24 @@ interface CustomInputFormProps { | |||||
| fieldLists: Field[][]; | fieldLists: Field[][]; | ||||
| } | } | ||||
| // interface SubComponents { | |||||
| // Loading: typeof CustomerSearchLoading; | |||||
| // } | |||||
| const CustomInputForm: React.FC<CustomInputFormProps> = ({ | const CustomInputForm: React.FC<CustomInputFormProps> = ({ | ||||
| Title, | Title, | ||||
| isActive, | isActive, | ||||
| fieldLists, | fieldLists, | ||||
| onSubmit, | onSubmit, | ||||
| onSubmitError, | |||||
| onCancel, | onCancel, | ||||
| // resetForm, | // resetForm, | ||||
| }) => { | }) => { | ||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const { reset, register, handleSubmit, control } = useForm(); | |||||
| const { reset, register, handleSubmit, control, formState: { errors } } = useForm(); | |||||
| const [dateObj, setDateObj] = useState<any>(null); | const [dateObj, setDateObj] = useState<any>(null); | ||||
| const [value, setValue] = useState<any>({}); | const [value, setValue] = useState<any>({}); | ||||
| const [checkboxValue, setCheckboxValue] = useState({}); | const [checkboxValue, setCheckboxValue] = useState({}); | ||||
| // const [dateObj, setDateObj] = useState({}); | |||||
| interface DateObj { | interface DateObj { | ||||
| [key: string]: string; | [key: string]: string; | ||||
| @@ -98,10 +105,7 @@ const CustomInputForm: React.FC<CustomInputFormProps> = ({ | |||||
| if (checkboxValue !== null) { | if (checkboxValue !== null) { | ||||
| data = { ...data, ...checkboxValue }; | data = { ...data, ...checkboxValue }; | ||||
| } | } | ||||
| // if (value !== null) { | |||||
| // data.dropdownCombo = value; | |||||
| // } | |||||
| const finalData = { | const finalData = { | ||||
| ...value, | ...value, | ||||
| ...data, | ...data, | ||||
| @@ -172,14 +176,8 @@ const CustomInputForm: React.FC<CustomInputFormProps> = ({ | |||||
| }); | }); | ||||
| }); | }); | ||||
| // useEffect(() => { | |||||
| // if (dateObj) { | |||||
| // console.log(dateObj); | |||||
| // } | |||||
| // }, [dateObj]); | |||||
| return ( | return ( | ||||
| <form onSubmit={handleSubmit(handleFormSubmit)}> | |||||
| <form onSubmit={handleSubmit(handleFormSubmit, onSubmitError)}> | |||||
| <Card sx={{ display: isActive ? "block" : "none" }}> | <Card sx={{ display: isActive ? "block" : "none" }}> | ||||
| <CardContent component={Stack} spacing={4}> | <CardContent component={Stack} spacing={4}> | ||||
| <> | <> | ||||
| @@ -200,17 +198,35 @@ const CustomInputForm: React.FC<CustomInputFormProps> = ({ | |||||
| return ( | return ( | ||||
| <Grid item xs={field.size ?? 6} key={field.id}> | <Grid item xs={field.size ?? 6} key={field.id}> | ||||
| <TextField | <TextField | ||||
| label={t(`${field.label}`)} | |||||
| label={field.label} | |||||
| fullWidth | |||||
| {...register(field.id, { | |||||
| pattern: field.pattern ? new RegExp(field.pattern) : /.*/, | |||||
| })} | |||||
| defaultValue={!field.value ? `${field.value}` : ""} | |||||
| required={field.required ?? false} | |||||
| error={Boolean(errors[field.id])} | |||||
| helperText={Boolean(errors[field.id]) && field.message} | |||||
| /> | |||||
| </Grid> | |||||
| ); | |||||
| } else if (field.type === "email") { | |||||
| return ( | |||||
| <Grid item xs={field.size ?? 6} key={field.id}> | |||||
| <TextField | |||||
| label={field.label} | |||||
| fullWidth | fullWidth | ||||
| // {...register(`asdasd`, { | |||||
| {...register(`${field.id}`, { | |||||
| required: field.required ?? false, | |||||
| {...register(field.id, { | |||||
| pattern: /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$/, | |||||
| })} | })} | ||||
| // error={Boolean(errors.projectCode)} | |||||
| defaultValue={!field.value ? `${field.value}` : ""} | |||||
| required={field.required ?? false} | |||||
| error={Boolean(errors[field.id])} | |||||
| helperText={Boolean(errors[field.id]) && field.message} | |||||
| /> | /> | ||||
| </Grid> | </Grid> | ||||
| ); | ); | ||||
| } else if (field.type === "multiDate") { | |||||
| }else if (field.type === "multiDate") { | |||||
| return ( | return ( | ||||
| <Grid item xs={field.size ?? 6} key={field.id}> | <Grid item xs={field.size ?? 6} key={field.id}> | ||||
| <LocalizationProvider dateAdapter={AdapterDayjs}> | <LocalizationProvider dateAdapter={AdapterDayjs}> | ||||
| @@ -408,4 +424,6 @@ const CustomInputForm: React.FC<CustomInputFormProps> = ({ | |||||
| ); | ); | ||||
| }; | }; | ||||
| // CustomInputForm.Loading = CustomerSearchLoading; | |||||
| export default CustomInputForm; | export default CustomInputForm; | ||||
| @@ -15,7 +15,7 @@ type SearchQuery = Partial<Omit<StaffResult, "id">>; | |||||
| type SearchParamNames = keyof SearchQuery; | type SearchParamNames = keyof SearchQuery; | ||||
| const StaffSearch: React.FC<Props> = ({ staff }) => { | const StaffSearch: React.FC<Props> = ({ staff }) => { | ||||
| const { t } = useTranslation("staff"); | |||||
| const { t } = useTranslation(); | |||||
| // If claim searching is done on the server-side, then no need for this. | // If claim searching is done on the server-side, then no need for this. | ||||
| const [filteredStaff, setFilteredStaff] = useState(staff); | const [filteredStaff, setFilteredStaff] = useState(staff); | ||||
| @@ -61,12 +61,12 @@ const StaffSearch: React.FC<Props> = ({ staff }) => { | |||||
| const columns = useMemo<Column<StaffResult>[]>( | const columns = useMemo<Column<StaffResult>[]>( | ||||
| () => [ | () => [ | ||||
| // { | |||||
| // name: "action", | |||||
| // label: t("Actions"), | |||||
| // onClick: onClaimClick, | |||||
| // buttonIcon: <EditNote />, | |||||
| // }, | |||||
| { | |||||
| name: "action", | |||||
| label: t("Actions"), | |||||
| onClick: onStaffClick, | |||||
| buttonIcon: <EditNote />, | |||||
| }, | |||||
| { name: "team", label: t("Team") }, | { name: "team", label: t("Team") }, | ||||
| { name: "name", label: t("Staff Name") }, | { name: "name", label: t("Staff Name") }, | ||||
| { name: "staffId", label: t("Staff ID") }, | { name: "staffId", label: t("Staff ID") }, | ||||
| @@ -0,0 +1,27 @@ | |||||
| { | |||||
| "Staff": "員工", | |||||
| "Team": "隊伍", | |||||
| "Staff Name": "員工姓名", | |||||
| "Staff ID": "員工編號", | |||||
| "Grade": "級別", | |||||
| "Current Position": "現職", | |||||
| "Actions": "編輯", | |||||
| "Create Staff": "新增員工", | |||||
| "Company": "公司", | |||||
| "Department": "部門", | |||||
| "Skillset": "技能", | |||||
| "Salary Point": "薪金點", | |||||
| "Employ Type": "職位類別", | |||||
| "Hourly Rate": "時薪", | |||||
| "Email": "時薪", | |||||
| "Phone1": "聯絡電話", | |||||
| "Phone2": "次要聯絡電話", | |||||
| "Additional Info": "更多資料", | |||||
| "Emergency Contact Name": "緊急聯絡人", | |||||
| "Emergency Contact Phone": "緊急聯絡人電話", | |||||
| "Join Date": "入職日期", | |||||
| "Join Position": "入職職位", | |||||
| "Depart Date": "離職日期", | |||||
| "Depart Reason": "離職原因", | |||||
| "Remark": "備註" | |||||
| } | |||||