diff --git a/src/app/(main)/settings/skill/edit/page.tsx b/src/app/(main)/settings/skill/edit/page.tsx
new file mode 100644
index 0000000..a2d9863
--- /dev/null
+++ b/src/app/(main)/settings/skill/edit/page.tsx
@@ -0,0 +1,28 @@
+import { Edit } from "@mui/icons-material";
+import { useSearchParams } from "next/navigation";
+// import EditStaff from "@/components/EditStaff";
+import { Suspense } from "react";
+import { I18nProvider, getServerI18n } from "@/i18n";
+// import EditStaffWrapper from "@/components/EditStaff/EditStaffWrapper";
+import { Metadata } from "next";
+import EditSkill from "@/components/EditSkill";
+import { Typography } from "@mui/material";
+
+
+const EditSkillPage: React.FC = async () => {
+ const { t } = await getServerI18n("staff");
+
+ return (
+ <>
+ {t("Edit Skill")}
+
+ }>
+
+
+
+ {/* */}
+ >
+ );
+};
+
+export default EditSkillPage;
diff --git a/src/app/api/group/actions.ts b/src/app/api/group/actions.ts
index 204c1d9..6800b29 100644
--- a/src/app/api/group/actions.ts
+++ b/src/app/api/group/actions.ts
@@ -29,8 +29,8 @@ export interface record {
records: auth[];
}
-export const fetchAuth = cache(async (id?: number) => {
- return serverFetchJson(`${BASE_API_URL}/group/auth/combo/${id ?? 0}`, {
+export const fetchAuth = cache(async (target: string, id?: number) => {
+ return serverFetchJson(`${BASE_API_URL}/group/auth/${target}/${id ?? 0}`, {
next: { tags: ["auth"] },
});
});
diff --git a/src/app/api/user/actions.ts b/src/app/api/user/actions.ts
index fff3da4..77b58b5 100644
--- a/src/app/api/user/actions.ts
+++ b/src/app/api/user/actions.ts
@@ -7,8 +7,10 @@ import { UserDetail, UserResult } from ".";
import { cache } from "react";
export interface UserInputs {
- username: string;
- email: string;
+ name: string;
+ email?: string;
+ addAuthIds?: number[];
+ removeAuthIds?: number[];
}
export interface PasswordInputs {
@@ -40,7 +42,7 @@ export const deleteUser = async (id: number) => {
};
export const changePassword = async (data: any) => {
- return serverFetchJson(`${BASE_API_URL}/user/change-password`, {
+ return serverFetchWithNoContent(`${BASE_API_URL}/user/change-password`, {
method: "PATCH",
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
diff --git a/src/app/api/user/index.ts b/src/app/api/user/index.ts
index 3151b64..f34292f 100644
--- a/src/app/api/user/index.ts
+++ b/src/app/api/user/index.ts
@@ -3,7 +3,6 @@ import { BASE_API_URL } from "@/config/api";
import { cache } from "react";
import "server-only";
-
export interface UserResult {
action: any;
id: number;
@@ -20,6 +19,7 @@ export interface UserResult {
phone2: string;
remarks: string;
groupId: number;
+ auths: any
}
// export interface DetailedUser extends UserResult {
@@ -28,9 +28,10 @@ export interface UserResult {
// }
export interface UserDetail {
- authIds: number[];
data: UserResult;
+ authIds: number[];
groupIds: number[];
+ auths: any[]
}
export const preloadUser = () => {
diff --git a/src/components/EditSkill/EditSkill.tsx b/src/components/EditSkill/EditSkill.tsx
new file mode 100644
index 0000000..a34b6e9
--- /dev/null
+++ b/src/components/EditSkill/EditSkill.tsx
@@ -0,0 +1,151 @@
+"use client";
+import { SkillResult } from "@/app/api/skill";
+import {
+ Button,
+ Card,
+ CardContent,
+ Grid,
+ Stack,
+ Tab,
+ Tabs,
+ TabsProps,
+ TextField,
+ Typography,
+} from "@mui/material";
+import {
+ FieldErrors,
+ FormProvider,
+ SubmitErrorHandler,
+ SubmitHandler,
+ useForm,
+ useFormContext,
+} from "react-hook-form";
+import { useCallback, useEffect, useState } from "react";
+import { useTranslation } from "react-i18next";
+import { useRouter, useSearchParams } from "next/navigation";
+import { Check, Close, Error, RestartAlt } from "@mui/icons-material";
+import EditSkillForm from "./EditSkillForm";
+import { CreateSkillInputs, saveSkill } from "@/app/api/skill/actions";
+import AuthAllocation from "../EditUser/AuthAllocation";
+
+interface Props {
+ skills: SkillResult[];
+}
+
+const EditSkill: React.FC = async ({ skills }) => {
+ const { t } = useTranslation();
+ const formProps = useForm();
+ const [serverError, setServerError] = useState("");
+ const router = useRouter();
+ const searchParams = useSearchParams();
+ const id = parseInt(searchParams.get("id") || "0");
+ const [tabIndex, setTabIndex] = useState(0);
+ const [filteredSkill, setFilteredSkill] = useState(() =>
+ skills.filter((s) => s.id === id)[0] as SkillResult
+ );
+ const errors = formProps.formState.errors;
+
+ const onSubmit = useCallback>(
+ async (data) => {
+ try {
+ console.log(data);
+ const postData = {
+ ...data,
+ id: id
+ }
+ await saveSkill(postData)
+ router.replace(`/settings/skill`)
+ } catch (e) {
+ console.log(e);
+ setServerError(t("An error has occurred. Please try again later."));
+ }
+ },
+ [router]
+ );
+
+ const handleCancel = () => {
+ router.back();
+ };
+
+ const handleTabChange = useCallback>(
+ (_e, newValue) => {
+ setTabIndex(newValue);
+ },
+ []
+ );
+
+ useEffect(() => {
+ formProps.reset({
+ name: filteredSkill.name,
+ code: filteredSkill.code,
+ description: filteredSkill.description
+ });
+ }, [skills]);
+
+ const hasErrorsInTab = (
+ tabIndex: number,
+ errors: FieldErrors
+ ) => {
+ switch (tabIndex) {
+ case 0:
+ return Object.keys(errors).length > 0;
+ default:
+ false;
+ }
+ };
+
+ return (
+ <>
+ {serverError && (
+
+ {serverError}
+
+ )}
+
+
+
+
+ ) : undefined
+ }
+ iconPosition="end"
+ />
+ {/* */}
+
+ {tabIndex === 0 && }
+
+ }
+ // onClick={() => console.log("asdasd")}
+ >
+ {t("Reset")}
+
+ }
+ onClick={handleCancel}
+ >
+ {t("Cancel")}
+
+ } type="submit">
+ {t("Confirm")}
+
+
+
+
+ >
+ );
+};
+export default EditSkill;
diff --git a/src/components/EditSkill/EditSkillForm.tsx b/src/components/EditSkill/EditSkillForm.tsx
new file mode 100644
index 0000000..120d2e5
--- /dev/null
+++ b/src/components/EditSkill/EditSkillForm.tsx
@@ -0,0 +1,114 @@
+"use client";
+
+import { CreateSkillInputs } from "@/app/api/skill/actions";
+import {
+ Box,
+ Button,
+ Card,
+ CardContent,
+ Grid,
+ Stack,
+ Tab,
+ Tabs,
+ TabsProps,
+ TextField,
+ Typography,
+} from "@mui/material";
+import { useSearchParams } from "next/navigation";
+import {
+ FieldErrors,
+ FormProvider,
+ SubmitErrorHandler,
+ SubmitHandler,
+ useForm,
+ useFormContext,
+} from "react-hook-form";
+import { useTranslation } from "react-i18next";
+
+interface Props {
+ // users: UserResult[]
+}
+
+const EditSkillForm: React.FC = async ({}) => {
+ const { t } = useTranslation();
+ const searchParams = useSearchParams();
+ const idString = searchParams.get("id");
+ const {
+ register,
+ setValue,
+ getValues,
+ formState: { errors, defaultValues },
+ reset,
+ resetField,
+ } = useFormContext();
+ // const formProps = useForm({});
+
+ return (
+ <>
+
+
+
+
+ {t("Skill Info")}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+export default EditSkillForm;
diff --git a/src/components/EditSkill/EditSkillLoading.tsx b/src/components/EditSkill/EditSkillLoading.tsx
new file mode 100644
index 0000000..74e08af
--- /dev/null
+++ b/src/components/EditSkill/EditSkillLoading.tsx
@@ -0,0 +1,40 @@
+import Card from "@mui/material/Card";
+import CardContent from "@mui/material/CardContent";
+import Skeleton from "@mui/material/Skeleton";
+import Stack from "@mui/material/Stack";
+import React from "react";
+
+// Can make this nicer
+export const EditSkillLoading: React.FC = () => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ Edit Skill
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default EditSkillLoading;
diff --git a/src/components/EditSkill/EditSkillWrapper.tsx b/src/components/EditSkill/EditSkillWrapper.tsx
new file mode 100644
index 0000000..12d7a12
--- /dev/null
+++ b/src/components/EditSkill/EditSkillWrapper.tsx
@@ -0,0 +1,21 @@
+import React from "react";
+import EditSkill from "./EditSkill";
+import EditSkillLoading from "./EditSkillLoading";
+import { fetchStaff, fetchTeamLeads } from "@/app/api/staff";
+import { useSearchParams } from "next/navigation";
+import { fetchSkill } from "@/app/api/skill";
+
+interface SubComponents {
+ Loading: typeof EditSkillLoading;
+}
+
+const EditSkillWrapper: React.FC & SubComponents = async () => {
+ const skills = await fetchSkill()
+ console.log(skills)
+
+ return ;
+};
+
+EditSkillWrapper.Loading = EditSkillLoading;
+
+export default EditSkillWrapper;
diff --git a/src/components/EditSkill/index.ts b/src/components/EditSkill/index.ts
new file mode 100644
index 0000000..ba42dd8
--- /dev/null
+++ b/src/components/EditSkill/index.ts
@@ -0,0 +1 @@
+export { default } from "./EditSkillWrapper";
diff --git a/src/components/EditTeam/Allocation.tsx b/src/components/EditTeam/Allocation.tsx
index 61e9e8f..f1386fe 100644
--- a/src/components/EditTeam/Allocation.tsx
+++ b/src/components/EditTeam/Allocation.tsx
@@ -16,8 +16,8 @@ import { Staff4TransferList, fetchStaffCombo } from "@/app/api/staff/actions";
import { StaffResult, StaffTeamTable } from "@/app/api/staff";
import SearchResults, { Column } from "../SearchResults";
import { Clear, PersonAdd, PersonRemove, Search } from "@mui/icons-material";
-import { Card } from "reactstrap";
import {
+ Card,
Box,
CardContent,
Grid,
@@ -49,8 +49,6 @@ const Allocation: React.FC = ({ allStaffs: staff, teamLead }) => {
reset,
resetField,
} = useFormContext();
-
- // let firstFilter: StaffResult[] = []
const initialStaffs = staff.map((s) => ({ ...s }));
const [filteredStaff, setFilteredStaff] = useState(initialStaffs);
diff --git a/src/components/EditUser/AuthAllocation.tsx b/src/components/EditUser/AuthAllocation.tsx
new file mode 100644
index 0000000..afb44d5
--- /dev/null
+++ b/src/components/EditUser/AuthAllocation.tsx
@@ -0,0 +1,221 @@
+"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 { useTranslation } from "react-i18next";
+import {
+ 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";
+import { UserInputs } from "@/app/api/user/actions";
+import { auth } from "@/app/api/group/actions";
+import SearchResults, { Column } from "../SearchResults";
+
+export interface Props {
+ auths: auth[]
+
+ }
+
+const AuthAllocation: React.FC = ({ auths }) => {
+ const { t } = useTranslation();
+ const searchParams = useSearchParams();
+ const id = parseInt(searchParams.get("id") || "0");
+ const {
+ setValue,
+ getValues,
+ formState: { defaultValues },
+ reset,
+ resetField,
+ } = useFormContext();
+ const initialAuths = auths.map((u) => ({ ...u })).sort((a, b) => a.id - b.id);
+ const [filteredAuths, setFilteredAuths] = useState(initialAuths);
+ const [selectedAuths, setSelectedAuths] = useState(
+ () => {
+ return filteredAuths.filter(
+ (s) => getValues("addAuthIds")?.includes(s.id)
+ );
+ }
+ );
+ const [removeAuthIds, setRemoveAuthIds] = useState([]);
+
+ // 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]);
+
+ // Sync with form
+ useEffect(() => {
+ setValue(
+ "addAuthIds",
+ selectedAuths.map((a) => a.id)
+ );
+ setValue(
+ "removeAuthIds",
+ removeAuthIds
+ );
+ }, [selectedAuths, removeAuthIds, setValue]);
+
+
+ const AuthPoolColumns = useMemo[]>(
+ () => [
+ {
+ label: t("Add"),
+ name: "id",
+ onClick: addAuth,
+ buttonIcon: ,
+ },
+ { label: t("authority"), name: "authority" },
+ { label: t("Auth Name"), name: "name" },
+ // { label: t("Current Position"), name: "currentPosition" },
+ ],
+ [addAuth, t]
+ );
+
+ const allocatedAuthColumns = useMemo[]>(
+ () => [
+ {
+ label: t("Remove"),
+ name: "id",
+ onClick: removeAuth,
+ buttonIcon: ,
+ },
+ { label: t("authority"), name: "authority" },
+ { label: t("Auth Name"), name: "name" },
+ ],
+ [removeAuth, selectedAuths, t]
+ );
+
+ const [query, setQuery] = React.useState("");
+ const onQueryInputChange = React.useCallback<
+ React.ChangeEventHandler
+ >((e) => {
+ setQuery(e.target.value);
+ }, []);
+ const clearQueryInput = React.useCallback(() => {
+ setQuery("");
+ }, []);
+
+ 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))
+ // })
+ // );
+ }, [auths, query]);
+
+ const resetAuth = React.useCallback(() => {
+ clearQueryInput();
+ clearAuth();
+ }, [clearQueryInput, clearAuth]);
+
+ const formProps = useForm({});
+
+ // Tab related
+ const [tabIndex, setTabIndex] = React.useState(0);
+ const handleTabChange = React.useCallback>(
+ (_e, newValue) => {
+ setTabIndex(newValue);
+ },
+ []
+ );
+
+return (
+ <>
+
+
+
+
+
+ {t("Authority")}
+
+
+
+
+
+
+
+
+
+ ),
+ }}
+ />
+
+
+
+
+
+
+
+ {tabIndex === 0 && (
+
+ )}
+ {tabIndex === 1 && (
+
+ )}
+
+
+
+
+
+ >
+ );
+
+}
+export default AuthAllocation
\ No newline at end of file
diff --git a/src/components/EditUser/EditUser.tsx b/src/components/EditUser/EditUser.tsx
index db52fac..853de6a 100644
--- a/src/components/EditUser/EditUser.tsx
+++ b/src/components/EditUser/EditUser.tsx
@@ -1,6 +1,6 @@
"use client";
import { useRouter, useSearchParams } from "next/navigation";
-import { useCallback, useEffect, useMemo, useState } from "react";
+import { useCallback, useEffect, useLayoutEffect, useMemo, useState } from "react";
import SearchResults, { Column } from "../SearchResults";
// import { TeamResult } from "@/app/api/team";
import { useTranslation } from "react-i18next";
@@ -26,9 +26,11 @@ import {
} from "react-hook-form";
import { Check, Close, Error, RestartAlt } from "@mui/icons-material";
import { StaffResult } from "@/app/api/staff";
-import { editUser, fetchUserDetails } from "@/app/api/user/actions";
+import { UserInputs, editUser, fetchUserDetails } from "@/app/api/user/actions";
import UserDetail from "./UserDetail";
import { UserResult } from "@/app/api/user";
+import { auth, fetchAuth } from "@/app/api/group/actions";
+import AuthAllocation from "./AuthAllocation";
interface Props {
// users: UserResult[]
@@ -36,11 +38,14 @@ interface Props {
const EditUser: React.FC = async ({ }) => {
const { t } = useTranslation();
- const formProps = useForm();
+ const formProps = useForm();
const searchParams = useSearchParams();
const id = parseInt(searchParams.get("id") || "0");
const [tabIndex, setTabIndex] = useState(0);
const router = useRouter();
+ const [serverError, setServerError] = useState("");
+ const [data, setData] = useState();
+ const [auths, setAuths] = useState();
const handleTabChange = useCallback>(
(_e, newValue) => {
@@ -49,38 +54,45 @@ const EditUser: React.FC = async ({ }) => {
[]
);
- const [serverError, setServerError] = useState("");
- const [data, setData] = useState();
+ const errors = formProps.formState.errors;
const fetchUserDetail = async () => {
console.log(id);
try {
+ // fetch user info
const userDetail = await fetchUserDetails(id);
console.log(userDetail);
const _data = userDetail.data as UserResult;
console.log(_data);
setData(_data);
+ //fetch user auths
+ const authDetail = await fetchAuth("user", id);
+ setAuths(authDetail.records)
+ const addAuthIds = authDetail.records.filter((item) => item.v === 1).map((item) => item.id).sort((a, b) => a - b);
+
formProps.reset({
- username: _data.username,
- firstname: _data.firstname,
- lastname: _data.lastname,
- title: _data.title,
- department: _data.department,
+ name: _data.username,
email: _data.email,
- phone1: _data.phone1,
- phone2: _data.phone2,
- remarks: _data.remarks,
+ addAuthIds: addAuthIds || []
});
} catch (error) {
console.log(error);
setServerError(t("An error has occurred. Please try again later."));
}
- };
+ }
useEffect(() => {
fetchUserDetail();
}, []);
+ // useEffect(() => {
+ // const thisUser = users.filter((item) => item.id === id)
+ // formProps.reset({
+ // username: thisUser[0].username,
+ // email: thisUser[0].email,
+ // });
+ // }, []);
+
const hasErrorsInTab = (
tabIndex: number,
errors: FieldErrors
@@ -97,14 +109,16 @@ const EditUser: React.FC = async ({ }) => {
router.back();
};
- const onSubmit = useCallback>(
+ const onSubmit = useCallback>(
async (data) => {
try {
console.log(data);
const tempData = {
- username: data.username,
+ name: data.name,
email: data.email,
- locked: false
+ locked: false,
+ addAuthIds: data.addAuthIds || [],
+ removeAuthIds: data.removeAuthIds || [],
}
console.log(tempData);
await editUser(id, tempData);
@@ -116,7 +130,7 @@ const EditUser: React.FC = async ({ }) => {
},
[router]
);
- const onSubmitError = useCallback>(
+ const onSubmitError = useCallback>(
(errors) => {
console.log(errors);
},
@@ -136,7 +150,31 @@ const EditUser: React.FC = async ({ }) => {
component="form"
onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)}
>
-
+
+
+
+ ) : undefined
+ }
+ iconPosition="end"
+ />
+
+
+
+ {tabIndex == 0 && }
+ {tabIndex === 1 && }