@@ -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": "備註" | |||||
} |