diff --git a/src/app/api/skill/actions.ts b/src/app/api/skill/actions.ts index 15a27a7..952d942 100644 --- a/src/app/api/skill/actions.ts +++ b/src/app/api/skill/actions.ts @@ -8,9 +8,9 @@ import { cache } from "react"; export interface CreateSkillInputs { id?: number; - name: String; - code: String; - description: String; + name: string; + code: string; + description: string; } export interface comboProp { diff --git a/src/components/CreateSkill/CreateSkill.tsx b/src/components/CreateSkill/CreateSkill.tsx index cb2acc7..3924498 100644 --- a/src/components/CreateSkill/CreateSkill.tsx +++ b/src/components/CreateSkill/CreateSkill.tsx @@ -15,10 +15,16 @@ import { useTranslation } from "react-i18next"; import { CreateSkillInputs, saveSkill } from "@/app/api/skill/actions"; import { Error } from "@mui/icons-material"; import SkillInfo from "./SkillInfo"; +import { SkillResult } from "@/app/api/skill"; -interface Props {} +interface Props { + skill: SkillResult[] +} -const CreateSkill: React.FC = () => { +const CreateSkill: React.FC = ({ + skill +}) => { + const codeList = skill.map(s => s.code.toLowerCase().trim()) const formProps = useForm(); const [serverError, setServerError] = useState(""); const router = useRouter(); @@ -29,7 +35,15 @@ const CreateSkill: React.FC = () => { const onSubmit = useCallback>( async (data) => { try { - console.log(data); + let haveError = false + haveError = codeList.includes(data.code.toLowerCase().trim()) + if (haveError) { + formProps.setError("code", { message: t("Duplicated Code"), type: "required" }) + return + } + data.name = data.name.trim() + data.code = data.code.trim() + data.description = data.description.trim() await saveSkill(data) router.replace(`/settings/skill`) } catch (e) { diff --git a/src/components/CreateSkill/CreateSkillWrapper.tsx b/src/components/CreateSkill/CreateSkillWrapper.tsx index f2f667a..198607b 100644 --- a/src/components/CreateSkill/CreateSkillWrapper.tsx +++ b/src/components/CreateSkill/CreateSkillWrapper.tsx @@ -3,15 +3,16 @@ import CreateSkill from "./CreateSkill"; import CreateSkillLoading from "./CreateSkillLoading"; import { fetchStaff, fetchTeamLeads } from "@/app/api/staff"; import { useSearchParams } from "next/navigation"; +import { fetchSkill } from "@/app/api/skill"; interface SubComponents { Loading: typeof CreateSkillLoading; } const CreateSkillWrapper: React.FC & SubComponents = async () => { + const skill = await fetchSkill() - - return ; + return ; }; CreateSkillWrapper.Loading = CreateSkillLoading; diff --git a/src/components/CreateStaff/CreateStaff.tsx b/src/components/CreateStaff/CreateStaff.tsx index 1d47f42..4c9a24d 100644 --- a/src/components/CreateStaff/CreateStaff.tsx +++ b/src/components/CreateStaff/CreateStaff.tsx @@ -69,8 +69,8 @@ const CreateStaff: React.FC = ({ combos }) => { try { console.log(data); let haveError = false; - let regex_email = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/ - let regex_phone = /^\d{8}$/ + const regex_email = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/ + const regex_phone = /^\d{8}$/ if (!regex_email.test(data.email)) { haveError = true diff --git a/src/components/StaffSearch/StaffSearch.tsx b/src/components/StaffSearch/StaffSearch.tsx index 7d2db97..b741505 100644 --- a/src/components/StaffSearch/StaffSearch.tsx +++ b/src/components/StaffSearch/StaffSearch.tsx @@ -11,27 +11,38 @@ import { useRouter } from "next/navigation"; import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; import Person from '@mui/icons-material/Person'; import { MAINTAIN_USER, VIEW_USER } from "@/middleware"; +import { TeamResult } from "@/app/api/team"; +import { Grade } from "@/app/api/grades"; +import { PositionResult } from "@/app/api/positions"; interface Props { staff: StaffResult[]; - abilities: string[] + teams: TeamResult[] + grades: Grade[] + positions: PositionResult[] + isAuthed: boolean } type SearchQuery = Partial>; type SearchParamNames = keyof SearchQuery; -const StaffSearch: React.FC = ({ staff, abilities }) => { +const StaffSearch: React.FC = ({ staff, teams, grades, positions, isAuthed }) => { + console.log(staff) const { t } = useTranslation(); const [filteredStaff, setFilteredStaff] = useState(staff); const router = useRouter(); + const teamCombo = teams.map(t => `${t.name} - ${t.code}`) + const gradeCombo = grades.map(g => `${g.name}`) + const positionCombo = positions.map(p => `${p.name}`) + const searchCriteria: Criterion[] = useMemo( () => [ { label: t("Team"), paramName: "team", type: "select", - options: ["1", "2"], + options: teamCombo, }, { label: t("Staff Name"), @@ -47,13 +58,13 @@ const StaffSearch: React.FC = ({ staff, abilities }) => { label: t("Grade"), paramName: "grade", type: "select", - options: ["pos1", "CEO"], + options: gradeCombo, }, { label: t("Current Position"), paramName: "currentPosition", type: "select", - options: ["pos1", "CEO"], + options: positionCombo, }, ], [t] @@ -97,7 +108,7 @@ const StaffSearch: React.FC = ({ staff, abilities }) => { label: t("Users"), onClick: onUserClick, buttonIcon: , - isHidden: ![MAINTAIN_USER, VIEW_USER].some((ability) => abilities.includes(ability)), + isHidden: !isAuthed, }, { name: "team", label: t("Team") }, { name: "name", label: t("Staff Name") }, @@ -114,20 +125,22 @@ const StaffSearch: React.FC = ({ staff, abilities }) => { ], [t, onStaffClick, deleteClick] ); + // postData.teamId = team[index].id return ( <> { + // console.log(teamCombo.findIndex(team => team === query.team)) setFilteredStaff( staff.filter( (s) => - s.staffId.toLowerCase().includes(query.staffId.toLowerCase()) && - s.name.toLowerCase().includes(query.name.toLowerCase()) - // (query.team === "All" || s.team === query.team) && - // (query.category === "All" || s.category === query.category) && - // (query.team === "All" || s.team === query.team), + s.staffId.toLowerCase().includes(query.staffId.toLowerCase()) + && s.name.toLowerCase().includes(query.name.toLowerCase()) + && (query.team === "All" || s.teamId === teams[teamCombo.findIndex(team => team === query.team)].id) + && (query.grade === "All" || s.grade === query.grade) + && (query.currentPosition === "All" || s.currentPosition === query.currentPosition) ) ); }} diff --git a/src/components/StaffSearch/StaffSearchWrapper.tsx b/src/components/StaffSearch/StaffSearchWrapper.tsx index 4f3b57d..42f5f83 100644 --- a/src/components/StaffSearch/StaffSearchWrapper.tsx +++ b/src/components/StaffSearch/StaffSearchWrapper.tsx @@ -4,6 +4,10 @@ import StaffSearch from "./StaffSearch"; import StaffSearchLoading from "./StaffSearchLoading"; import { Session, getServerSession } from "next-auth"; import { authOptions } from "@/config/authConfig"; +import { fetchTeam } from "@/app/api/team"; +import { fetchPositions } from "@/app/api/positions"; +import { fetchGrades } from "@/app/api/grades"; +import { MAINTAIN_USER, VIEW_USER } from "@/middleware"; interface SubComponents { Loading: typeof StaffSearchLoading; @@ -14,11 +18,15 @@ interface SessionWithAbilities extends Session { } const StaffSearchWrapper: React.FC & SubComponents = async () => { - const staff = await fetchStaff(); const session = await getServerSession(authOptions) as SessionWithAbilities; - const abilities: string[] = session.abilities!! + const isAuthed = ![MAINTAIN_USER, VIEW_USER].some((ability) => session.abilities!.includes(ability)) + + const staff = await fetchStaff(); + const teams = await fetchTeam(); + const grades = await fetchGrades(); + const positions = await fetchPositions(); - return ; + return ; }; StaffSearchWrapper.Loading = StaffSearchLoading; diff --git a/src/i18n/zh/staff.json b/src/i18n/zh/staff.json index 3c1ad59..caa3123 100644 --- a/src/i18n/zh/staff.json +++ b/src/i18n/zh/staff.json @@ -25,6 +25,7 @@ "Depart Date": "離職日期", "Depart Reason": "離職原因", "Remark": "備註", + "Reset": "重設", "Confirm": "確認", "Cancel": "取消" } \ No newline at end of file