소스 검색

update

tags/Baseline_30082024_FRONTEND_UAT
MSI\derek 1 년 전
부모
커밋
b91acd6675
4개의 변경된 파일214개의 추가작업 그리고 147개의 파일을 삭제
  1. +10
    -14
      src/components/EditTeam/Allocation.tsx
  2. +100
    -96
      src/components/EditUser/AuthAllocation.tsx
  3. +65
    -35
      src/components/EditUser/EditUser.tsx
  4. +39
    -2
      src/components/EditUser/UserDetail.tsx

+ 10
- 14
src/components/EditTeam/Allocation.tsx 파일 보기

@@ -60,24 +60,20 @@ const Allocation: React.FC<Props> = ({ allStaffs: staff, teamLead }) => {
return rearrangedStaff.filter((s) => getValues("addStaffIds")?.includes(s.id))
}
);
const [seletedTeamLead, setSeletedTeamLead] = useState<number>();
const [deletedStaffIds, setDeletedStaffIds] = useState<number[]>([]);

// Adding / Removing staff
const addStaff = useCallback((staff: StaffResult) => {
setSelectedStaff((s) => [...s, staff]);
// setDeletedStaffIds((s) => s.filter((s) => s === selectedStaff.id))
}, []);

const removeStaff = useCallback((staff: StaffResult) => {
setSelectedStaff((s) => s.filter((s) => s.id !== staff.id));
// setDeletedStaffIds((s) => s)
setDeletedStaffIds((prevIds) => [...prevIds, staff.id]);
}, []);

const setTeamLead = useCallback(
(staff: StaffResult) => {
setSeletedTeamLead(staff.id);
const rearrangedList = getValues("addStaffIds").reduce<number[]>(
(acc, num, index) => {
if (num === staff.id && index !== 0) {
@@ -171,16 +167,16 @@ const Allocation: React.FC<Props> = ({ allStaffs: staff, teamLead }) => {
}, []);

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))
// })
// );
setFilteredStaff(
initialStaffs.filter((i) => {
const q = query.toLowerCase();
return (
i.staffId.toLowerCase().includes(q) ||
i.name.toLowerCase().includes(q) ||
i.currentPosition.toLowerCase().includes(q)
);
})
);
}, [staff, query]);

useEffect(() => {


+ 100
- 96
src/components/EditUser/AuthAllocation.tsx 파일 보기

@@ -1,93 +1,97 @@
"use client";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { Add, Clear, PersonAdd, PersonRemove, Remove, Search } from "@mui/icons-material";
import {
Add,
Clear,
PersonAdd,
PersonRemove,
Remove,
Search,
} from "@mui/icons-material";
import { useTranslation } from "react-i18next";
import {
FieldErrors,
FormProvider,
SubmitErrorHandler,
SubmitHandler,
useForm,
useFormContext,
} from "react-hook-form";
FieldErrors,
FormProvider,
SubmitErrorHandler,
SubmitHandler,
useForm,
useFormContext,
} from "react-hook-form";
import {
Box,
Card,
CardContent,
Grid,
IconButton,
InputAdornment,
Stack,
Tab,
Tabs,
TabsProps,
TextField,
Typography,
} from "@mui/material";
import { differenceBy } from "lodash";
Box,
Card,
CardContent,
Grid,
IconButton,
InputAdornment,
Stack,
Tab,
Tabs,
TabsProps,
TextField,
Typography,
} from "@mui/material";
import { differenceBy } from "lodash";
import { UserInputs } from "@/app/api/user/actions";
import { auth } from "@/app/api/group/actions";
import SearchResults, { Column } from "../SearchResults";

export interface Props {
auths: auth[]
}
auths: auth[];
}

const AuthAllocation: React.FC<Props> = ({ auths }) => {
const { t } = useTranslation();
const searchParams = useSearchParams();
const id = parseInt(searchParams.get("id") || "0");
const {
setValue,
getValues,
formState: { defaultValues },
reset,
resetField,
} = useFormContext<UserInputs>();
const initialAuths = auths.map((u) => ({ ...u })).sort((a, b) => a.id - b.id);
const [filteredAuths, setFilteredAuths] = useState(initialAuths);
const [selectedAuths, setSelectedAuths] = useState<typeof filteredAuths>(
() => {
return filteredAuths.filter(
(s) => getValues("addAuthIds")?.includes(s.id)
);
}
const { t } = useTranslation();
const searchParams = useSearchParams();
const id = parseInt(searchParams.get("id") || "0");
const {
setValue,
getValues,
formState: { defaultValues },
reset,
resetField,
} = useFormContext<UserInputs>();
const initialAuths = auths.map((u) => ({ ...u })).sort((a, b) => a.id - b.id);
const [filteredAuths, setFilteredAuths] = useState(initialAuths);
const [selectedAuths, setSelectedAuths] = useState<typeof filteredAuths>(
() => {
return filteredAuths.filter(
(s) => getValues("addAuthIds")?.includes(s.id)
);
const [removeAuthIds, setRemoveAuthIds] = useState<number[]>([]);
}
);
const [removeAuthIds, setRemoveAuthIds] = useState<number[]>([]);

// Adding / Removing Auth
const addAuth = useCallback((auth: auth) => {
setSelectedAuths((a) => [...a, auth]);
}, []);
const removeAuth = useCallback((auth: auth) => {
setSelectedAuths((a) => a.filter((a) => a.id !== auth.id));
setRemoveAuthIds((prevIds) => [...prevIds, auth.id]);
}, []);
// Adding / Removing Auth
const addAuth = useCallback((auth: auth) => {
setSelectedAuths((a) => [...a, auth]);
}, []);
const removeAuth = useCallback((auth: auth) => {
setSelectedAuths((a) => a.filter((a) => a.id !== auth.id));
setRemoveAuthIds((prevIds) => [...prevIds, auth.id]);
}, []);

const clearAuth = useCallback(() => {
if (defaultValues !== undefined) {
resetField("addAuthIds");
setSelectedAuths(
initialAuths.filter((auth) => defaultValues.addAuthIds?.includes(auth.id))
);
}
}, [defaultValues]);
const clearAuth = useCallback(() => {
if (defaultValues !== undefined) {
resetField("addAuthIds");
setSelectedAuths(
initialAuths.filter(
(auth) => defaultValues.addAuthIds?.includes(auth.id)
)
);
}
}, [defaultValues]);

// Sync with form
// Sync with form
useEffect(() => {
setValue(
"addAuthIds",
selectedAuths.map((a) => a.id)
);
setValue(
"removeAuthIds",
removeAuthIds
);
setValue("removeAuthIds", removeAuthIds);
}, [selectedAuths, removeAuthIds, setValue]);

const AuthPoolColumns = useMemo<Column<auth>[]>(
() => [
{
@@ -97,8 +101,7 @@ const AuthAllocation: React.FC<Props> = ({ auths }) => {
buttonIcon: <Add />,
},
{ label: t("authority"), name: "authority" },
{ label: t("Auth Name"), name: "name" },
// { label: t("Current Position"), name: "currentPosition" },
{ label: t("description"), name: "name" },
],
[addAuth, t]
);
@@ -109,10 +112,10 @@ const AuthAllocation: React.FC<Props> = ({ auths }) => {
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("description"), name: "name" },
],
[removeAuth, selectedAuths, t]
);
@@ -128,16 +131,14 @@ const AuthAllocation: React.FC<Props> = ({ auths }) => {
}, []);

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())
)
)
);
}, [auths, query]);

const resetAuth = React.useCallback(() => {
@@ -147,16 +148,16 @@ const AuthAllocation: React.FC<Props> = ({ auths }) => {

const formProps = useForm({});

// Tab related
const [tabIndex, setTabIndex] = React.useState(0);
const handleTabChange = React.useCallback<NonNullable<TabsProps["onChange"]>>(
(_e, newValue) => {
setTabIndex(newValue);
},
[]
);
// Tab related
const [tabIndex, setTabIndex] = React.useState(0);
const handleTabChange = React.useCallback<NonNullable<TabsProps["onChange"]>>(
(_e, newValue) => {
setTabIndex(newValue);
},
[]
);

return (
return (
<>
<FormProvider {...formProps}>
<Card sx={{ display: "block" }}>
@@ -175,7 +176,9 @@ return (
fullWidth
onChange={onQueryInputChange}
value={query}
placeholder={t("Search by staff ID, name or position.")}
placeholder={t(
"Search by Authority or description or position."
)}
InputProps={{
endAdornment: query && (
<InputAdornment position="end">
@@ -191,18 +194,20 @@ return (
<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}
@@ -216,6 +221,5 @@ return (
</FormProvider>
</>
);

}
export default AuthAllocation
};
export default AuthAllocation;

+ 65
- 35
src/components/EditUser/EditUser.tsx 파일 보기

@@ -1,6 +1,12 @@
"use client";
import { useRouter, useSearchParams } from "next/navigation";
import { useCallback, useEffect, useLayoutEffect, useMemo, useState } from "react";
import React, {
useCallback,
useEffect,
useLayoutEffect,
useMemo,
useState,
} from "react";
import SearchResults, { Column } from "../SearchResults";
// import { TeamResult } from "@/app/api/team";
import { useTranslation } from "react-i18next";
@@ -26,23 +32,24 @@ import {
} from "react-hook-form";
import { Check, Close, Error, RestartAlt } from "@mui/icons-material";
import { StaffResult } from "@/app/api/staff";
import { UserInputs, adminChangePassword, editUser, fetchUserDetails } from "@/app/api/user/actions";
import {
UserInputs,
adminChangePassword,
editUser,
fetchUserDetails,
} from "@/app/api/user/actions";
import UserDetail from "./UserDetail";
import { UserResult, passwordRule } from "@/app/api/user";
import { auth, fetchAuth } from "@/app/api/group/actions";
import AuthAllocation from "./AuthAllocation";

interface Props {
user: UserResult,
rules: passwordRule,
auths: auth[]
}
user: UserResult;
rules: passwordRule;
auths: auth[];
}

const EditUser: React.FC<Props> = async ({
user,
rules,
auths
}) => {
const EditUser: React.FC<Props> = async ({ user, rules, auths }) => {
const { t } = useTranslation();
const formProps = useForm<UserInputs>();
const searchParams = useSearchParams();
@@ -50,6 +57,13 @@ const EditUser: React.FC<Props> = async ({
const [tabIndex, setTabIndex] = useState(0);
const router = useRouter();
const [serverError, setServerError] = useState("");
const addAuthIds =
auths && auths.length > 0
? auths
.filter((item) => item.v === 1)
.map((item) => item.id)
.sort((a, b) => a - b)
: [];

const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>(
(_e, newValue) => {
@@ -60,22 +74,27 @@ const EditUser: React.FC<Props> = async ({

const errors = formProps.formState.errors;

useEffect(() => {
const resetForm = React.useCallback(() => {
console.log("triggerred");
console.log(addAuthIds);
try {
const addAuthIds = auths && auths.length > 0
? auths.filter((item) => item.v === 1).map((item) => item.id).sort((a, b) => a - b)
: []

formProps.reset({
name: user.username,
email: user.email,
addAuthIds: addAuthIds
addAuthIds: addAuthIds,
removeAuthIds: [],
password: "",
});
console.log(formProps.formState.defaultValues);
} catch (error) {
console.log(error);
setServerError(t("An error has occurred. Please try again later."));
}
}, [user, auths]);
}, [auths, user]);

useEffect(() => {
resetForm();
}, []);

const hasErrorsInTab = (
tabIndex: number,
@@ -96,22 +115,33 @@ const EditUser: React.FC<Props> = async ({
const onSubmit = useCallback<SubmitHandler<UserInputs>>(
async (data) => {
try {
let haveError = false
let regex_pw = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{8,20}$/
let pw = ''
let haveError = false;
let regex_pw =
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{8,20}$/;
let pw = "";
if (data.password && data.password.length > 0) {
pw = data.password
pw = data.password;
if (pw.length < rules.min) {
haveError = true
formProps.setError("password", { message: t("The password requires 8-20 characters."), type: "required" })
haveError = true;
formProps.setError("password", {
message: t("The password requires 8-20 characters."),
type: "required",
});
}
if (pw.length > rules.max) {
haveError = true
formProps.setError("password", { message: t("The password requires 8-20 characters."), type: "required" })
haveError = true;
formProps.setError("password", {
message: t("The password requires 8-20 characters."),
type: "required",
});
}
if (!regex_pw.test(pw)) {
haveError = true
formProps.setError("password", { message: "A combination of uppercase letters, lowercase letters, numbers, and symbols is required.", type: "required" })
haveError = true;
formProps.setError("password", {
message:
"A combination of uppercase letters, lowercase letters, numbers, and symbols is required.",
type: "required",
});
}
}
const userData = {
@@ -119,16 +149,16 @@ const EditUser: React.FC<Props> = async ({
locked: false,
addAuthIds: data.addAuthIds || [],
removeAuthIds: data.removeAuthIds || [],
}
};
const pwData = {
id: id,
password: "",
newPassword: pw
}
newPassword: pw,
};
if (haveError) {
return
return;
}
console.log("passed")
console.log("passed");
await editUser(id, userData);
if (data.password && data.password.length > 0) {
await adminChangePassword(pwData);
@@ -185,12 +215,12 @@ const EditUser: React.FC<Props> = async ({
</Tabs>
</Stack>
{tabIndex == 0 && <UserDetail />}
{tabIndex === 1 && <AuthAllocation auths={auths!}/>}
{tabIndex === 1 && <AuthAllocation auths={auths!} />}
<Stack direction="row" justifyContent="flex-end" gap={1}>
<Button
variant="text"
startIcon={<RestartAlt />}
// onClick={() => console.log("asdasd")}
onClick={resetForm}
>
{t("Reset")}
</Button>


+ 39
- 2
src/components/EditUser/UserDetail.tsx 파일 보기

@@ -9,13 +9,13 @@ import {
Stack,
TextField,
Typography,
makeStyles,
} from "@mui/material";
import { useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";



const UserDetail: React.FC = () => {

const { t } = useTranslation();
const {
register,
@@ -45,6 +45,30 @@ const UserDetail: React.FC = () => {
label={t("password")}
fullWidth
{...register("password")}
// helperText={
// Boolean(errors.password) &&
// (errors.password?.message
// ? t(errors.password.message)
// :
// (<>
// - 8-20 characters
// <br/>
// - Uppercase letters
// <br/>
// - Lowercase letters
// <br/>
// - Numbers
// <br/>
// - Symbols
// </>)
// )
// }
helperText={
Boolean(errors.password) &&
(errors.password?.message
? t(errors.password.message)
: t("Please input correct password"))
}
error={Boolean(errors.password)}
/>
</Grid>
@@ -55,3 +79,16 @@ const UserDetail: React.FC = () => {
};

export default UserDetail;


{/* <>
- 8-20 characters
<br/>
- Uppercase letters
<br/>
- Lowercase letters
<br/>
- Numbers
<br/>
- Symbols
</> */}

불러오는 중...
취소
저장