diff --git a/src/app/(main)/settings/group/edit/page.tsx b/src/app/(main)/settings/group/edit/page.tsx
index e69de29..ae51d16 100644
--- a/src/app/(main)/settings/group/edit/page.tsx
+++ b/src/app/(main)/settings/group/edit/page.tsx
@@ -0,0 +1,26 @@
+import EditPosition from "@/components/EditPosition";
+import EditUserGroup from "@/components/EditUserGroup";
+import { I18nProvider, getServerI18n } from "@/i18n";
+import Typography from "@mui/material/Typography";
+import { Metadata } from "next";
+
+export const metadata: Metadata = {
+ title: "Edit User Group",
+};
+
+const Positions: React.FC = async () => {
+ const { t } = await getServerI18n("group");
+
+ // Preload necessary dependencies
+
+ return (
+ <>
+ {/* {t("Edit User Group")} */}
+
+
+
+ >
+ );
+};
+
+export default Positions;
\ No newline at end of file
diff --git a/src/app/api/group/actions.ts b/src/app/api/group/actions.ts
index c8881de..204c1d9 100644
--- a/src/app/api/group/actions.ts
+++ b/src/app/api/group/actions.ts
@@ -29,11 +29,11 @@ export interface record {
records: auth[];
}
- export const fetchAuth = cache(async () => {
- return serverFetchJson(`${BASE_API_URL}/group/auth/combo`, {
- next: { tags: ["auth"] },
- });
+export const fetchAuth = cache(async (id?: number) => {
+ return serverFetchJson(`${BASE_API_URL}/group/auth/combo/${id ?? 0}`, {
+ next: { tags: ["auth"] },
});
+});
export const saveGroup = async (data: CreateGroupInputs) => {
return serverFetchJson(`${BASE_API_URL}/group/save`, {
@@ -41,4 +41,11 @@ export const saveGroup = async (data: CreateGroupInputs) => {
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
});
- };
\ No newline at end of file
+ };
+
+export const deleteGroup = async (id: number) => {
+ return serverFetchWithNoContent(`${BASE_API_URL}/group/${id}`, {
+ method: "DELETE",
+ headers: { "Content-Type": "application/json" },
+ });
+};
\ No newline at end of file
diff --git a/src/components/CreateGroup/AuthorityAllocation.tsx b/src/components/CreateGroup/AuthorityAllocation.tsx
index fd9610b..bdd4ccb 100644
--- a/src/components/CreateGroup/AuthorityAllocation.tsx
+++ b/src/components/CreateGroup/AuthorityAllocation.tsx
@@ -50,6 +50,7 @@ const AuthorityAllocation: React.FC = ({ auth }) => {
);
}
);
+
// Adding / Removing Auth
const addAuth = useCallback((auth: auth) => {
setSelectedAuths((a) => [...a, auth]);
@@ -126,10 +127,6 @@ const AuthorityAllocation: React.FC = ({ auth }) => {
// );
}, [auth, query]);
- useEffect(() => {
- // console.log(getValues("addStaffIds"))
- }, [initialAuths]);
-
const resetAuth = React.useCallback(() => {
clearQueryInput();
clearAuth();
diff --git a/src/components/EditUserGroup/AuthorityAllocation.tsx b/src/components/EditUserGroup/AuthorityAllocation.tsx
new file mode 100644
index 0000000..da502da
--- /dev/null
+++ b/src/components/EditUserGroup/AuthorityAllocation.tsx
@@ -0,0 +1,210 @@
+"use client";
+import React, { useCallback, useEffect, useMemo, useState } from "react";
+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 { CreateGroupInputs, auth } from "@/app/api/group/actions";
+import SearchResults, { Column } from "../SearchResults";
+import { Add, Clear, Remove, Search } from "@mui/icons-material";
+
+export interface Props {
+ auth: auth[];
+}
+
+const AuthorityAllocation: React.FC = ({ auth }) => {
+ const { t } = useTranslation();
+ const {
+ setValue,
+ getValues,
+ formState: { defaultValues },
+ reset,
+ resetField,
+ } = useFormContext();
+ 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(
+ () => initialAuths.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))
+ // })
+ // );
+ }, [auth, 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 AuthorityAllocation;
diff --git a/src/components/EditUserGroup/EditUserGroup.tsx b/src/components/EditUserGroup/EditUserGroup.tsx
new file mode 100644
index 0000000..fe75821
--- /dev/null
+++ b/src/components/EditUserGroup/EditUserGroup.tsx
@@ -0,0 +1,165 @@
+"use client";
+import { useRouter, useSearchParams } from "next/navigation";
+import { useCallback, useEffect, useMemo, useState } from "react";
+import SearchResults, { Column } from "../SearchResults";
+// import { TeamResult } from "@/app/api/team";
+import { useTranslation } from "react-i18next";
+import { Button, Stack, Tab, Tabs, TabsProps, Typography } from "@mui/material";
+import { CreateTeamInputs, saveTeam } from "@/app/api/team/actions";
+import {
+ FieldErrors,
+ FormProvider,
+ SubmitHandler,
+ useForm,
+ useFormContext,
+} from "react-hook-form";
+import { Check, Close, Error } from "@mui/icons-material";
+import { StaffResult } from "@/app/api/staff";
+import { CreateGroupInputs, auth, fetchAuth, saveGroup } from "@/app/api/group/actions";
+import { UserGroupResult } from "@/app/api/group";
+import { UserResult } from "@/app/api/user";
+import GroupInfo from "./GroupInfo";
+import AuthorityAllocation from "./AuthorityAllocation";
+import UserAllocation from "./UserAllocation";
+interface Props {
+ groups: UserGroupResult[];
+// auths: auth[];
+ users: UserResult[];
+}
+
+const EditUserGroup: React.FC = ({ groups, users }) => {
+ // console.log(users)
+ const { t } = useTranslation();
+ const [serverError, setServerError] = useState("");
+ const formProps = useForm();
+ const searchParams = useSearchParams();
+ const id = parseInt(searchParams.get("id") || "0");
+ const router = useRouter();
+ const [tabIndex, setTabIndex] = useState(0);
+ const [auths, setAuths] = useState();
+
+ const errors = formProps.formState.errors;
+
+ const handleTabChange = useCallback>(
+ (_e, newValue) => {
+ setTabIndex(newValue);
+ },
+ []
+ );
+
+ const hasErrorsInTab = (
+ tabIndex: number,
+ errors: FieldErrors
+ ) => {
+ switch (tabIndex) {
+ case 0:
+ return Object.keys(errors).length > 0;
+ default:
+ false;
+ }
+ };
+
+ const onSubmit = useCallback>(
+ async (data) => {
+ try {
+ console.log(data);
+ const tempData = {
+ ...data,
+ removeUserIds: data.removeUserIds ?? [],
+ removeAuthIds: data.removeAuthIds ?? [],
+ id: id
+ }
+ console.log(tempData)
+ await saveGroup(tempData);
+ router.replace("/settings/group");
+ } catch (e) {
+ console.log(e);
+ setServerError(t("An error has occurred. Please try again later."));
+ }
+ },
+ [router]
+ );
+ useEffect(() => {
+ const thisGroup = groups.filter((item) => item.id === id)[0];
+ const addUserIds = users.filter((item) => item.groupId === id).map((data) => data.id)
+ let addAuthIds: number[] = []
+ fetchAuth(id).then((data) => {
+ setAuths(data.records)
+ addAuthIds = data.records.filter((data) => data.v === 1).map((data) => data.id).sort((a, b) => a - b);
+ formProps.reset({
+ name: thisGroup.name,
+ description: thisGroup.description,
+ addAuthIds: addAuthIds,
+ addUserIds: addUserIds,
+ });
+ });
+ // console.log(auths)
+ }, [groups, users]);
+
+ return (
+ <>
+
+
+
+ {t("Edit User Group")}
+
+
+
+
+ ) : undefined
+ }
+ iconPosition="end"
+ />
+
+
+
+
+ {serverError && (
+
+ {serverError}
+
+ )}
+ {tabIndex === 0 && }
+ {tabIndex === 1 && }
+ {tabIndex === 2 && }
+
+ }
+ // onClick={handleCancel}
+ >
+ {t("Cancel")}
+
+ }
+ type="submit"
+ // disabled={Boolean(formProps.watch("isGridEditing"))}
+ >
+ {t("Confirm")}
+
+
+
+
+ >
+ );
+};
+
+export default EditUserGroup;
diff --git a/src/components/EditUserGroup/EditUserGroupLoading.tsx b/src/components/EditUserGroup/EditUserGroupLoading.tsx
new file mode 100644
index 0000000..9238474
--- /dev/null
+++ b/src/components/EditUserGroup/EditUserGroupLoading.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 EditUserGroupLoading: React.FC = () => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ EditUserGroup
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default EditUserGroupLoading;
diff --git a/src/components/EditUserGroup/EditUserGroupWrapper.tsx b/src/components/EditUserGroup/EditUserGroupWrapper.tsx
new file mode 100644
index 0000000..84f7501
--- /dev/null
+++ b/src/components/EditUserGroup/EditUserGroupWrapper.tsx
@@ -0,0 +1,31 @@
+import React from "react";
+import EditUserGroup from "./EditUserGroup";
+import EditUserGroupLoading from "./EditUserGroupLoading";
+import { fetchGroup } from "@/app/api/group";
+import { fetchAuth } from "@/app/api/group/actions";
+import { fetchUser } from "@/app/api/user";
+import { useSearchParams } from "next/navigation";
+
+interface SubComponents {
+ Loading: typeof EditUserGroupLoading;
+}
+
+const EditUserGroupWrapper: React.FC & SubComponents = async () => {
+
+ const [
+ groups,
+ // auths,
+ users,
+ ] = await Promise.all([
+ fetchGroup(),
+ // fetchAuth(),
+ fetchUser(),
+ ]);
+ console.log(users)
+
+ return ;
+};
+
+EditUserGroupWrapper.Loading = EditUserGroupLoading;
+
+export default EditUserGroupWrapper;
diff --git a/src/components/EditUserGroup/GroupInfo.tsx b/src/components/EditUserGroup/GroupInfo.tsx
new file mode 100644
index 0000000..d9141bc
--- /dev/null
+++ b/src/components/EditUserGroup/GroupInfo.tsx
@@ -0,0 +1,81 @@
+"use client";
+import Stack from "@mui/material/Stack";
+import Box from "@mui/material/Box";
+import Card from "@mui/material/Card";
+import CardContent from "@mui/material/CardContent";
+import Grid from "@mui/material/Grid";
+import TextField from "@mui/material/TextField";
+import Typography from "@mui/material/Typography";
+import { CreateGroupInputs } from "@/app/api/group/actions";
+import { useFormContext } from "react-hook-form";
+import { useTranslation } from "react-i18next";
+import { useCallback } from "react";
+
+const GroupInfo: React.FC = () => {
+ const { t } = useTranslation();
+ const {
+ register,
+ formState: { errors, defaultValues },
+ control,
+ reset,
+ resetField,
+ setValue,
+ } = useFormContext();
+
+
+ const resetGroup = useCallback(() => {
+ console.log(defaultValues);
+ if (defaultValues !== undefined) {
+ resetField("description");
+ }
+ }, [defaultValues]);
+
+
+ return (
+
+
+
+
+ {t("Group Info")}
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default GroupInfo;
diff --git a/src/components/EditUserGroup/UserAllocation.tsx b/src/components/EditUserGroup/UserAllocation.tsx
new file mode 100644
index 0000000..14ed975
--- /dev/null
+++ b/src/components/EditUserGroup/UserAllocation.tsx
@@ -0,0 +1,216 @@
+"use client";
+import React, { useCallback, useEffect, useMemo, useState } from "react";
+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 { CreateGroupInputs, auth } from "@/app/api/group/actions";
+import SearchResults, { Column } from "../SearchResults";
+import { Add, Clear, Remove, Search } from "@mui/icons-material";
+import { UserResult } from "@/app/api/user";
+
+export interface Props {
+ users: UserResult[];
+}
+
+const UserAllocation: React.FC = ({ users }) => {
+ const { t } = useTranslation();
+ const {
+ setValue,
+ getValues,
+ formState: { defaultValues },
+ reset,
+ resetField,
+ } = useFormContext();
+ const initialUsers = users.map((u) => ({ ...u })).sort((a, b) => a.id - b.id);
+ const [filteredUsers, setFilteredUsers] = useState(initialUsers);
+ const [selectedUsers, setSelectedUsers] = useState(
+ () => {
+ return filteredUsers.filter(
+ (s) => getValues("addUserIds")?.includes(s.id)
+ );
+ }
+ );
+ const [deletedUserIds, setDeletedUserIds] = useState([]);
+
+ // Adding / Removing Auth
+ const addUser = useCallback((users: UserResult) => {
+ setSelectedUsers((a) => [...a, users]);
+ }, []);
+
+ const removeUser = useCallback((users: UserResult) => {
+ setSelectedUsers((a) => a.filter((a) => a.id !== users.id));
+ setDeletedUserIds((prevIds) => [...prevIds, users.id]);
+ }, []);
+
+ const clearUser = useCallback(() => {
+ if (defaultValues !== undefined) {
+ resetField("addUserIds");
+ setSelectedUsers(
+ initialUsers.filter((s) => defaultValues.addUserIds?.includes(s.id))
+ );
+ }
+ }, [defaultValues]);
+
+ // Sync with form
+ useEffect(() => {
+ setValue(
+ "addUserIds",
+ selectedUsers.map((u) => u.id)
+ );
+ setValue(
+ "removeUserIds",
+ deletedUserIds
+ );
+ }, [selectedUsers, deletedUserIds, setValue]);
+
+ const UserPoolColumns = useMemo[]>(
+ () => [
+ {
+ label: t("Add"),
+ name: "id",
+ onClick: addUser,
+ buttonIcon: ,
+ },
+ { label: t("User Name"), name: "username" },
+ { label: t("name"), name: "name" },
+ ],
+ [addUser, t]
+ );
+
+ const allocatedUserColumns = useMemo[]>(
+ () => [
+ {
+ label: t("Remove"),
+ name: "id",
+ onClick: removeUser,
+ buttonIcon: ,
+ },
+ { label: t("User Name"), name: "username" },
+ { label: t("name"), name: "name" },
+ ],
+ [removeUser, selectedUsers, 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))
+ // })
+ // );
+ }, [users, query]);
+
+ const resetUser = React.useCallback(() => {
+ clearQueryInput();
+ clearUser();
+ }, [clearQueryInput, clearUser]);
+
+ const formProps = useForm({});
+
+ // Tab related
+ const [tabIndex, setTabIndex] = React.useState(0);
+ const handleTabChange = React.useCallback>(
+ (_e, newValue) => {
+ setTabIndex(newValue);
+ },
+ []
+ );
+
+ return (
+ <>
+
+
+
+
+
+ {t("User")}
+
+
+
+
+
+
+
+
+
+ ),
+ }}
+ />
+
+
+
+
+
+
+
+ {tabIndex === 0 && (
+
+ )}
+ {tabIndex === 1 && (
+
+ )}
+
+
+
+
+
+ >
+ );
+};
+
+export default UserAllocation;
diff --git a/src/components/EditUserGroup/index.ts b/src/components/EditUserGroup/index.ts
new file mode 100644
index 0000000..b062020
--- /dev/null
+++ b/src/components/EditUserGroup/index.ts
@@ -0,0 +1 @@
+export { default } from "./EditUserGroupWrapper";
diff --git a/src/components/UserGroupSearch/UserGroupSearch.tsx b/src/components/UserGroupSearch/UserGroupSearch.tsx
index 0480167..fee25e4 100644
--- a/src/components/UserGroupSearch/UserGroupSearch.tsx
+++ b/src/components/UserGroupSearch/UserGroupSearch.tsx
@@ -10,6 +10,7 @@ import { useRouter } from "next/navigation";
import { deleteDialog, successDialog } from "../Swal/CustomAlerts";
import { UserGroupResult } from "@/app/api/group";
import { deleteUser } from "@/app/api/user/actions";
+import { deleteGroup } from "@/app/api/group/actions";
interface Props {
users: UserGroupResult[];
@@ -34,20 +35,20 @@ const UserGroupSearch: React.FC = ({ users }) => {
);
const onUserClick = useCallback(
- (users: UserGroupResult) => {
- console.log(users);
- // router.push(`/settings/user/edit?id=${users.id}`)
+ (group: UserGroupResult) => {
+ console.log(group);
+ router.push(`/settings/group/edit?id=${group.id}`)
},
[router, t]
);
- const onDeleteClick = useCallback((users: UserGroupResult) => {
+ const onDeleteClick = useCallback((group: UserGroupResult) => {
deleteDialog(async () => {
- await deleteUser(users.id);
+ await deleteGroup(group.id);
successDialog(t("Delete Success"), t);
- setFilteredUser((prev) => prev.filter((obj) => obj.id !== users.id));
+ setFilteredUser((prev) => prev.filter((obj) => obj.id !== group.id));
}, t);
}, []);