| @@ -9,7 +9,7 @@ export const metadata: Metadata = { | |||
| }; | |||
| const StaffMonthlyWorkHoursAnalysisReport: React.FC = async () => { | |||
| const { t } = await getServerI18n("User Group"); | |||
| const { t } = await getServerI18n("report"); | |||
| return ( | |||
| <> | |||
| @@ -9,7 +9,7 @@ export const metadata: Metadata = { | |||
| }; | |||
| const StaffMonthlyWorkHoursAnalysisReport: React.FC = async () => { | |||
| const { t } = await getServerI18n("User Group"); | |||
| const { t } = await getServerI18n("report"); | |||
| return ( | |||
| <> | |||
| @@ -19,7 +19,7 @@ export const metadata: Metadata = { | |||
| }; | |||
| const ChangePasswordPage: React.FC = async () => { | |||
| const { t } = await getServerI18n("User Group"); | |||
| const { t } = await getServerI18n("changePassword"); | |||
| // preloadTeamLeads(); | |||
| // preloadStaff(); | |||
| return ( | |||
| @@ -34,7 +34,7 @@ const ChangePasswordPage: React.FC = async () => { | |||
| {t("Change Password")} | |||
| </Typography> | |||
| </Stack> | |||
| <I18nProvider namespaces={["User Group", "common"]}> | |||
| <I18nProvider namespaces={["common", "changePassword"]}> | |||
| <Suspense fallback={<ChangePassword.Loading />}> | |||
| <ChangePassword /> | |||
| </Suspense> | |||
| @@ -11,7 +11,7 @@ const CreateStaff: React.FC = async () => { | |||
| return ( | |||
| <> | |||
| <Typography variant="h4">{t("Create Group")}</Typography> | |||
| <Typography variant="h4">{t("Create User Group")}</Typography> | |||
| <I18nProvider namespaces={["group"]}> | |||
| <CreateGroup /> | |||
| </I18nProvider> | |||
| @@ -20,7 +20,7 @@ export const metadata: Metadata = { | |||
| const UserGroup: React.FC = async () => { | |||
| const { t } = await getServerI18n("User Group"); | |||
| const { t } = await getServerI18n("group"); | |||
| // preloadTeamLeads(); | |||
| // preloadStaff(); | |||
| return ( | |||
| @@ -43,7 +43,7 @@ export const metadata: Metadata = { | |||
| {t("Create User Group")} | |||
| </Button> | |||
| </Stack> | |||
| <I18nProvider namespaces={["User Group", "common"]}> | |||
| <I18nProvider namespaces={["group", "common"]}> | |||
| <Suspense fallback={<UserGroupSearch.Loading />}> | |||
| <UserGroupSearch /> | |||
| </Suspense> | |||
| @@ -14,7 +14,7 @@ import { Visibility, VisibilityOff } from "@mui/icons-material"; | |||
| import { IconButton, InputAdornment } from "@mui/material"; | |||
| const ChagnePasswordForm: React.FC = () => { | |||
| const { t } = useTranslation(); | |||
| const { t } = useTranslation("changePassword"); | |||
| const [showNewPassword, setShowNewPassword] = useState(false); | |||
| const handleClickShowNewPassword = () => setShowNewPassword(!showNewPassword); | |||
| @@ -38,7 +38,7 @@ const ChagnePasswordForm: React.FC = () => { | |||
| <CardContent component={Stack} spacing={4}> | |||
| <Box> | |||
| <Typography variant="overline" display="block" marginBlockEnd={1}> | |||
| {t("Please Fill in all the Fields")} | |||
| {t("Please Fill in All Fields")} | |||
| </Typography> | |||
| <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}> | |||
| <Grid item xs={6}> | |||
| @@ -84,8 +84,8 @@ const AuthorityAllocation: React.FC<Props> = ({ auth }) => { | |||
| onClick: addAuth, | |||
| buttonIcon: <Add />, | |||
| }, | |||
| { label: t("authority"), name: "authority" }, | |||
| { label: t("Auth Name"), name: "name" }, | |||
| { label: t("Authority"), name: "authority" }, | |||
| { label: t("Description"), name: "name" }, | |||
| // { label: t("Current Position"), name: "currentPosition" }, | |||
| ], | |||
| [addAuth, t] | |||
| @@ -97,10 +97,10 @@ const AuthorityAllocation: React.FC<Props> = ({ auth }) => { | |||
| label: t("Remove"), | |||
| name: "id", | |||
| onClick: removeAuth, | |||
| buttonIcon: <Remove color="warning"/>, | |||
| buttonIcon: <Remove color="warning" />, | |||
| }, | |||
| { label: t("authority"), name: "authority" }, | |||
| { label: t("Auth Name"), name: "name" }, | |||
| { label: t("Authority"), name: "authority" }, | |||
| { label: t("Description"), name: "name" }, | |||
| ], | |||
| [removeAuth, selectedAuths, t] | |||
| ); | |||
| @@ -115,16 +115,13 @@ const AuthorityAllocation: React.FC<Props> = ({ auth }) => { | |||
| }, []); | |||
| React.useEffect(() => { | |||
| // setFilteredStaff( | |||
| // initialStaffs.filter((s) => { | |||
| // const q = query.toLowerCase(); | |||
| // // s.staffId.toLowerCase().includes(q) | |||
| // // const q = query.toLowerCase(); | |||
| // // return s.name.toLowerCase().includes(q); | |||
| // // s.code.toString().includes(q) || | |||
| // // (s.brNo != null && s.brNo.toLowerCase().includes(q)) | |||
| // }) | |||
| // ); | |||
| setFilteredAuths( | |||
| initialAuths.filter( | |||
| (a) => | |||
| a.authority.toLowerCase().includes(query.toLowerCase()) || | |||
| a.name?.toLowerCase().includes(query.toLowerCase()) | |||
| ) | |||
| ); | |||
| }, [auth, query]); | |||
| const resetAuth = React.useCallback(() => { | |||
| @@ -152,7 +149,7 @@ const AuthorityAllocation: React.FC<Props> = ({ auth }) => { | |||
| > | |||
| <Stack gap={2}> | |||
| <Typography variant="overline" display="block"> | |||
| {t("Authority")} | |||
| {/* {t("Authority")} */} | |||
| </Typography> | |||
| <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}> | |||
| <Grid item xs={6} display="flex" alignItems="center"> | |||
| @@ -162,7 +159,7 @@ const AuthorityAllocation: React.FC<Props> = ({ auth }) => { | |||
| fullWidth | |||
| onChange={onQueryInputChange} | |||
| value={query} | |||
| placeholder={t("Search by staff ID, name or position.")} | |||
| placeholder={t("Search by ") + t("Authority") + " / " + t("Description")} | |||
| InputProps={{ | |||
| endAdornment: query && ( | |||
| <InputAdornment position="end"> | |||
| @@ -178,18 +175,20 @@ const AuthorityAllocation: React.FC<Props> = ({ auth }) => { | |||
| <Tabs value={tabIndex} onChange={handleTabChange}> | |||
| <Tab label={t("Authority Pool")} /> | |||
| <Tab | |||
| label={`${t("Allocated Authority")} (${selectedAuths.length})`} | |||
| label={`${t("Allocated Authority")} (${ | |||
| selectedAuths.length | |||
| })`} | |||
| /> | |||
| </Tabs> | |||
| <Box sx={{ marginInline: -3 }}> | |||
| {tabIndex === 0 && ( | |||
| {tabIndex === 0 && ( | |||
| <SearchResults | |||
| noWrapper | |||
| items={differenceBy(filteredAuths, selectedAuths, "id")} | |||
| columns={AuthPoolColumns} | |||
| /> | |||
| )} | |||
| {tabIndex === 1 && ( | |||
| {tabIndex === 1 && ( | |||
| <SearchResults | |||
| noWrapper | |||
| items={selectedAuths} | |||
| @@ -22,7 +22,7 @@ const CreateGroup: React.FC<Props> = ({ auth, users }) => { | |||
| const [serverError, setServerError] = useState(""); | |||
| const router = useRouter(); | |||
| const [tabIndex, setTabIndex] = useState(0); | |||
| const { t } = useTranslation(); | |||
| const { t } = useTranslation("group"); | |||
| const errors = formProps.formState.errors; | |||
| @@ -51,13 +51,13 @@ const GroupInfo: React.FC = () => { | |||
| Boolean(errors.name) && | |||
| (errors.name?.message | |||
| ? t(errors.name.message) | |||
| : t("Please input correct name")) | |||
| : t("Please input correct ") + t("Group Name")) | |||
| } | |||
| /> | |||
| </Grid> | |||
| <Grid item xs={12}> | |||
| <TextField | |||
| label={t("Group Description")} | |||
| label={t("Description")} | |||
| fullWidth | |||
| multiline | |||
| rows={4} | |||
| @@ -67,7 +67,7 @@ const GroupInfo: React.FC = () => { | |||
| Boolean(errors.description) && | |||
| (errors.description?.message | |||
| ? t(errors.description.message) | |||
| : t("Please input correct description")) | |||
| : t("Please input correct ") + t("Description")) | |||
| } | |||
| /> | |||
| </Grid> | |||
| @@ -85,8 +85,8 @@ const UserAllocation: React.FC<Props> = ({ users }) => { | |||
| onClick: addUser, | |||
| buttonIcon: <Add />, | |||
| }, | |||
| { label: t("User Name"), name: "username" }, | |||
| { label: t("name"), name: "name" }, | |||
| { label: t("Username"), name: "username" }, | |||
| { label: t("Staff Name"), name: "name" }, | |||
| ], | |||
| [addUser, t] | |||
| ); | |||
| @@ -99,8 +99,8 @@ const UserAllocation: React.FC<Props> = ({ users }) => { | |||
| onClick: removeUser, | |||
| buttonIcon: <Remove color="warning" />, | |||
| }, | |||
| { label: t("User Name"), name: "username" }, | |||
| { label: t("name"), name: "name" }, | |||
| { label: t("Username"), name: "username" }, | |||
| { label: t("Staff Name"), name: "name" }, | |||
| ], | |||
| [removeUser, selectedUsers, t] | |||
| ); | |||
| @@ -116,16 +116,13 @@ const UserAllocation: React.FC<Props> = ({ users }) => { | |||
| }, []); | |||
| React.useEffect(() => { | |||
| // setFilteredStaff( | |||
| // initialStaffs.filter((s) => { | |||
| // const q = query.toLowerCase(); | |||
| // // s.staffId.toLowerCase().includes(q) | |||
| // // const q = query.toLowerCase(); | |||
| // // return s.name.toLowerCase().includes(q); | |||
| // // s.code.toString().includes(q) || | |||
| // // (s.brNo != null && s.brNo.toLowerCase().includes(q)) | |||
| // }) | |||
| // ); | |||
| const q = query.toLowerCase(); | |||
| setFilteredUsers( | |||
| initialUsers.filter((u) => ( | |||
| u.username.toLowerCase().includes(q) || | |||
| u.name.toLowerCase().includes(q) | |||
| )) | |||
| ); | |||
| }, [users, query]); | |||
| const resetUser = React.useCallback(() => { | |||
| @@ -153,7 +150,7 @@ const UserAllocation: React.FC<Props> = ({ users }) => { | |||
| > | |||
| <Stack gap={2}> | |||
| <Typography variant="overline" display="block"> | |||
| {t("User")} | |||
| {/* {t("User")} */} | |||
| </Typography> | |||
| <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}> | |||
| <Grid item xs={6} display="flex" alignItems="center"> | |||
| @@ -163,7 +160,7 @@ const UserAllocation: React.FC<Props> = ({ users }) => { | |||
| fullWidth | |||
| onChange={onQueryInputChange} | |||
| value={query} | |||
| placeholder={t("Search by staff ID, name or position.")} | |||
| placeholder={t("Search by ") + t("Username") + " / " + t("Staff Name")} | |||
| InputProps={{ | |||
| endAdornment: query && ( | |||
| <InputAdornment position="end"> | |||
| @@ -48,7 +48,6 @@ const StaffAllocation: React.FC<Props> = ({ allStaffs: staff }) => { | |||
| } = useFormContext<CreateTeamInputs>(); | |||
| const initialStaffs = staff.map((s) => ({ ...s })); | |||
| // console.log(initialStaffs) | |||
| const [filteredStaff, setFilteredStaff] = useState(initialStaffs); | |||
| const [selectedStaff, setSelectedStaff] = useState<typeof filteredStaff>( | |||
| initialStaffs.filter((s) => getValues("addStaffIds")?.includes(s.id)) | |||
| @@ -158,15 +157,13 @@ const StaffAllocation: React.FC<Props> = ({ allStaffs: staff }) => { | |||
| }, []); | |||
| React.useEffect(() => { | |||
| const q = query.toLowerCase(); | |||
| setFilteredStaff( | |||
| initialStaffs.filter((i) => { | |||
| const q = query.toLowerCase(); | |||
| return ( | |||
| initialStaffs.filter((i) => ( | |||
| i.staffId.toLowerCase().includes(q) || | |||
| i.name.toLowerCase().includes(q) || | |||
| i.currentPosition.toLowerCase().includes(q) | |||
| ); | |||
| }) | |||
| )) | |||
| ); | |||
| }, [staff, query]); | |||
| @@ -50,7 +50,7 @@ interface Props { | |||
| } | |||
| const EditUser: React.FC<Props> = async ({ user, rules, auths }) => { | |||
| const { t } = useTranslation(); | |||
| const { t } = useTranslation("user"); | |||
| const formProps = useForm<UserInputs>(); | |||
| const searchParams = useSearchParams(); | |||
| const id = parseInt(searchParams.get("id") || "0"); | |||
| @@ -41,7 +41,6 @@ const AuthorityAllocation: React.FC<Props> = ({ auth }) => { | |||
| reset, | |||
| resetField, | |||
| } = useFormContext<CreateGroupInputs>(); | |||
| console.log(auth) | |||
| const initialAuths = auth.map((a) => ({ ...a })).sort((a, b) => a.id - b.id); | |||
| const [filteredAuths, setFilteredAuths] = useState(initialAuths); | |||
| const [selectedAuths, setSelectedAuths] = useState<typeof filteredAuths>( | |||
| @@ -86,8 +85,8 @@ const AuthorityAllocation: React.FC<Props> = ({ auth }) => { | |||
| onClick: addAuth, | |||
| buttonIcon: <Add />, | |||
| }, | |||
| { label: t("authority"), name: "authority" }, | |||
| { label: t("Auth Name"), name: "name" }, | |||
| { label: t("Authority"), name: "authority" }, | |||
| { label: t("Description"), name: "name" }, | |||
| // { label: t("Current Position"), name: "currentPosition" }, | |||
| ], | |||
| [addAuth, t] | |||
| @@ -101,8 +100,8 @@ const AuthorityAllocation: React.FC<Props> = ({ auth }) => { | |||
| onClick: removeAuth, | |||
| buttonIcon: <Remove color="warning"/>, | |||
| }, | |||
| { label: t("authority"), name: "authority" }, | |||
| { label: t("Auth Name"), name: "name" }, | |||
| { label: t("Authority"), name: "authority" }, | |||
| { label: t("Description"), name: "name" }, | |||
| ], | |||
| [removeAuth, selectedAuths, t] | |||
| ); | |||
| @@ -117,16 +116,13 @@ const AuthorityAllocation: React.FC<Props> = ({ auth }) => { | |||
| }, []); | |||
| React.useEffect(() => { | |||
| // setFilteredStaff( | |||
| // initialStaffs.filter((s) => { | |||
| // const q = query.toLowerCase(); | |||
| // // s.staffId.toLowerCase().includes(q) | |||
| // // const q = query.toLowerCase(); | |||
| // // return s.name.toLowerCase().includes(q); | |||
| // // s.code.toString().includes(q) || | |||
| // // (s.brNo != null && s.brNo.toLowerCase().includes(q)) | |||
| // }) | |||
| // ); | |||
| const q = query.toLowerCase(); | |||
| setFilteredAuths( | |||
| initialAuths.filter((a) => ( | |||
| a.authority.toLowerCase().includes(q) || | |||
| a.name.toLowerCase().includes(q) | |||
| )) | |||
| ); | |||
| }, [auth, query]); | |||
| const resetAuth = React.useCallback(() => { | |||
| @@ -164,7 +160,7 @@ const AuthorityAllocation: React.FC<Props> = ({ auth }) => { | |||
| fullWidth | |||
| onChange={onQueryInputChange} | |||
| value={query} | |||
| placeholder={t("Search by staff ID, name or position.")} | |||
| placeholder={t("Search by ")+ t("Authority") + " / " + t("Description")} | |||
| InputProps={{ | |||
| endAdornment: query && ( | |||
| <InputAdornment position="end"> | |||
| @@ -51,13 +51,13 @@ const GroupInfo: React.FC = () => { | |||
| Boolean(errors.name) && | |||
| (errors.name?.message | |||
| ? t(errors.name.message) | |||
| : t("Please input correct name")) | |||
| : t("Please input correct ") + t("name")) | |||
| } | |||
| /> | |||
| </Grid> | |||
| <Grid item xs={12}> | |||
| <TextField | |||
| label={t("Group Description")} | |||
| label={t("Description")} | |||
| fullWidth | |||
| multiline | |||
| rows={4} | |||
| @@ -67,7 +67,7 @@ const GroupInfo: React.FC = () => { | |||
| Boolean(errors.description) && | |||
| (errors.description?.message | |||
| ? t(errors.description.message) | |||
| : t("Please input correct description")) | |||
| : t("Please input correct ") + t("Description")) | |||
| } | |||
| /> | |||
| </Grid> | |||
| @@ -92,8 +92,8 @@ const UserAllocation: React.FC<Props> = ({ users }) => { | |||
| onClick: addUser, | |||
| buttonIcon: <Add />, | |||
| }, | |||
| { label: t("User Name"), name: "username" }, | |||
| { label: t("name"), name: "name" }, | |||
| { label: t("Username"), name: "username" }, | |||
| { label: t("Staff Name"), name: "name" }, | |||
| ], | |||
| [addUser, t] | |||
| ); | |||
| @@ -106,8 +106,8 @@ const UserAllocation: React.FC<Props> = ({ users }) => { | |||
| onClick: removeUser, | |||
| buttonIcon: <Remove color="warning" />, | |||
| }, | |||
| { label: t("User Name"), name: "username" }, | |||
| { label: t("name"), name: "name" }, | |||
| { label: t("Username"), name: "username" }, | |||
| { label: t("Staff Name"), name: "name" }, | |||
| ], | |||
| [removeUser, selectedUsers, t] | |||
| ); | |||
| @@ -123,16 +123,13 @@ const UserAllocation: React.FC<Props> = ({ users }) => { | |||
| }, []); | |||
| React.useEffect(() => { | |||
| // setFilteredStaff( | |||
| // initialStaffs.filter((s) => { | |||
| // const q = query.toLowerCase(); | |||
| // // s.staffId.toLowerCase().includes(q) | |||
| // // const q = query.toLowerCase(); | |||
| // // return s.name.toLowerCase().includes(q); | |||
| // // s.code.toString().includes(q) || | |||
| // // (s.brNo != null && s.brNo.toLowerCase().includes(q)) | |||
| // }) | |||
| // ); | |||
| const q = query.toLowerCase(); | |||
| setFilteredUsers( | |||
| initialUsers.filter((item) => ( | |||
| item.username.toLowerCase().includes(q) || | |||
| item.name.toLowerCase().includes(q) | |||
| )) | |||
| ); | |||
| }, [users, query]); | |||
| const resetUser = React.useCallback(() => { | |||
| @@ -170,7 +167,7 @@ const UserAllocation: React.FC<Props> = ({ users }) => { | |||
| fullWidth | |||
| onChange={onQueryInputChange} | |||
| value={query} | |||
| placeholder={t("Search by staff ID, name or position.")} | |||
| placeholder={t("Search by ") + t("Username") + " / " + t("Staff Name")} | |||
| InputProps={{ | |||
| endAdornment: query && ( | |||
| <InputAdornment position="end"> | |||
| @@ -3,61 +3,66 @@ 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 { | |||
| 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"; | |||
| import dayjs from "dayjs"; | |||
| interface Props { | |||
| staffs: StaffResult[] | |||
| 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 { t } = useTranslation("report"); | |||
| const staffCombo = staffs.map((staff) => `${staff.name} - ${staff.staffId}`); | |||
| const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | |||
| () => [ | |||
| { | |||
| label: t("Staff"), | |||
| paramName: "staff", | |||
| type: "select", | |||
| options: staffCombo, | |||
| needAll: false | |||
| }, | |||
| { | |||
| label: t("date"), | |||
| paramName: "date", | |||
| type: "monthYear", | |||
| }, | |||
| ], | |||
| [t], | |||
| ); | |||
| const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | |||
| () => [ | |||
| { | |||
| label: t("Staff"), | |||
| paramName: "staff", | |||
| type: "select", | |||
| options: staffCombo, | |||
| needAll: false, | |||
| }, | |||
| { | |||
| label: t("Date"), | |||
| paramName: "date", | |||
| type: "monthYear", | |||
| }, | |||
| ], | |||
| [t] | |||
| ); | |||
| return ( | |||
| return ( | |||
| <> | |||
| <SearchBox | |||
| <SearchBox | |||
| criteria={searchCriteria} | |||
| onSearch={async (query: any) => { | |||
| const index = staffCombo.findIndex(staff => staff === query.staff) | |||
| if (query.staff.length > 0 && query.staff.toLocaleLowerCase() !== "all" && query.date.length > 0) { | |||
| const index = staffCombo.findIndex(staff => staff === query.staff) | |||
| const response = await fetchMonthlyWorkHoursReport({ id: staffs[index].id, yearMonth: query.date }) | |||
| if (response) { | |||
| downloadFile(new Uint8Array(response.blobValue), response.filename!!) | |||
| } | |||
| } | |||
| } | |||
| } | |||
| /> | |||
| </> | |||
| ) | |||
| } | |||
| const index = staffCombo.findIndex((staff) => staff === query.staff); | |||
| const response = await fetchMonthlyWorkHoursReport({ | |||
| id: staffs[index].id, | |||
| yearMonth: query.date, | |||
| }); | |||
| if (response) { | |||
| downloadFile( | |||
| new Uint8Array(response.blobValue), | |||
| response.filename!! | |||
| ); | |||
| } | |||
| }} | |||
| /> | |||
| </> | |||
| ); | |||
| }; | |||
| export default GenerateMonthlyWorkHoursReport | |||
| export default GenerateMonthlyWorkHoursReport; | |||
| @@ -20,10 +20,10 @@ type SearchQuery = Partial<Omit<ProjectResourceOverconsumptionReportFilter, "id" | |||
| type SearchParamNames = keyof SearchQuery; | |||
| const ResourceOverconsumptionReport: React.FC<Props> = ({ team, customer }) => { | |||
| const { t } = useTranslation(); | |||
| const { t } = useTranslation("report"); | |||
| const teamCombo = team.map(t => `${t.name} - ${t.code}`) | |||
| const custCombo = customer.map(c => `${c.name} - ${c.code}`) | |||
| const statusCombo = ["Within Budget, Overconsumption", "Potential Overconsumption"] | |||
| const statusCombo = ["Overconsumption", "Potential Overconsumption"] | |||
| // const staffCombo = staffs.map(staff => `${staff.name} - ${staff.staffId}`) | |||
| // console.log(staffs) | |||
| @@ -226,19 +226,18 @@ function SearchBox<T extends string>({ | |||
| adapterLocale="zh-hk" | |||
| > | |||
| <Box display="flex"> | |||
| <InputLabel> | |||
| {c.label} | |||
| </InputLabel> | |||
| <DateCalendar | |||
| views={["month", "year"]} | |||
| openTo="month" | |||
| onChange={makeMonthYearChangeHandler(c.paramName)} | |||
| value={ | |||
| inputs[c.paramName] | |||
| ? dayjs(inputs[c.paramName]) | |||
| : null | |||
| } | |||
| /> | |||
| <FormControl fullWidth> | |||
| <DatePicker | |||
| label={c.label} | |||
| onChange={makeMonthYearChangeHandler(c.paramName)} | |||
| value={ | |||
| inputs[c.paramName] | |||
| ? dayjs(inputs[c.paramName]) | |||
| : dayjs() | |||
| } | |||
| views={["month","year"]} | |||
| /> | |||
| </FormControl> | |||
| </Box> | |||
| </LocalizationProvider> | |||
| )} | |||
| @@ -26,10 +26,15 @@ const UserGroupSearch: React.FC<Props> = ({ users }) => { | |||
| const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | |||
| () => [ | |||
| { | |||
| label: t("User Name"), | |||
| label: t("Group Name"), | |||
| paramName: "name", | |||
| type: "text", | |||
| }, | |||
| { | |||
| label: t("Description"), | |||
| paramName: "description", | |||
| type: "text", | |||
| }, | |||
| ], | |||
| [t] | |||
| ); | |||
| @@ -75,14 +80,13 @@ const UserGroupSearch: React.FC<Props> = ({ users }) => { | |||
| <SearchBox | |||
| criteria={searchCriteria} | |||
| onSearch={(query) => { | |||
| // setFilteredUser( | |||
| // users.filter( | |||
| // (t) => | |||
| // t.name.toLowerCase().includes(query.name.toLowerCase()) && | |||
| // t.code.toLowerCase().includes(query.code.toLowerCase()) && | |||
| // t.description.toLowerCase().includes(query.description.toLowerCase()) | |||
| // ) | |||
| // ) | |||
| setFilteredUser( | |||
| users.filter( | |||
| (u) => | |||
| u.name.toLowerCase().includes(query.name.toLowerCase()) && | |||
| u.description.toLowerCase().includes(query.description.toLowerCase()) | |||
| ) | |||
| ) | |||
| }} | |||
| /> | |||
| <SearchResults<UserGroupResult> items={filteredUser} columns={columns} /> | |||
| @@ -0,0 +1,7 @@ | |||
| { | |||
| "Change Password": "更改密碼", | |||
| "Please Fill in All Fields": "請填寫以下項目", | |||
| "Input Old Password": "舊密碼", | |||
| "Input New Password": "新密碼", | |||
| "Input New Password Again": "重複輸入新密碼" | |||
| } | |||
| @@ -0,0 +1,27 @@ | |||
| { | |||
| "User Group": "用戶群組", | |||
| "Create User Group": "建立用戶群組", | |||
| "Edit User Group": "編輯用戶群組", | |||
| "Edit": "編輯", | |||
| "Group Name": "名稱", | |||
| "Description": "描述", | |||
| "Group Info": "群組資料", | |||
| "Add": "新增", | |||
| "User": "用戶", | |||
| "Remove": "移除", | |||
| "Confirm": "確定", | |||
| "Cancel": "取消", | |||
| "Delete": "刪除", | |||
| "Search by ": "搜尋", | |||
| "Delete Success": "刪除成功", | |||
| "Please input correct ": "請輸入正確", | |||
| "Authority Allocation": "權限分配", | |||
| "Authority Pool": "權限池", | |||
| "Allocated Authority": "已分配權限", | |||
| "User Allocation": "用戶分配", | |||
| "User Pool": "用戶池", | |||
| "Allocated Users": "已分配用戶", | |||
| "Username": "用戶名", | |||
| "Staff Name": "員工名字", | |||
| "Authority": "權限" | |||
| } | |||
| @@ -1,4 +1,12 @@ | |||
| { | |||
| "Staff Monthly Work Hours Analysis Report": "Staff Monthly Work Hours Analysis Report", | |||
| "Project Resource Overconsumption Report": "Project Resource Overconsumption Report", | |||
| "Project": "項目", | |||
| "Date Type": "日期類型" | |||
| "Date Type": "日期類型", | |||
| "Date": "日期", | |||
| "Team": "隊伍", | |||
| "Client": "客戶", | |||
| "Status": "狀態", | |||
| "Staff": "員工" | |||
| } | |||
| @@ -0,0 +1,7 @@ | |||
| { | |||
| "Edit User": "編輯用戶", | |||
| "User Detail": "用戶資料", | |||
| "User Authority": "權限", | |||
| "username": "用戶名", | |||
| "password": "更改密碼" | |||
| } | |||