@@ -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": "更改密碼" | |||
} |