mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
278 lines
11 KiB
React
278 lines
11 KiB
React
import { createContext, useCallback, useContext, useState } from "react";
|
|
import api from "@/utils/api.util";
|
|
import { toast } from "sonner";
|
|
|
|
const BASE = "/admin";
|
|
const UserGroupContext = createContext(null);
|
|
|
|
export function useUserGroups() {
|
|
const ctx = useContext(UserGroupContext);
|
|
if (!ctx) throw new Error("useUserGroups must be used within a UserGroupProvider");
|
|
return ctx;
|
|
}
|
|
|
|
export function UserGroupProvider({ children }) {
|
|
const [groups, setGroups] = useState([]);
|
|
const [group, setGroup] = useState(null);
|
|
const [members, setMembers] = useState([]);
|
|
const [usersIn, setUsersIn] = useState([]);
|
|
const [usersNotIn, setUsersNotIn] = useState([]);
|
|
const [attributes, setAttributes] = useState([]);
|
|
const [pagination, setPagination] = useState({ page: 1, limit: 10, total: 0, totalPages: 1 });
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
const request = useCallback(async (fn) => {
|
|
setLoading(true);
|
|
try {
|
|
return await fn();
|
|
} catch (err) {
|
|
const message = err?.response?.data?.message ?? "Something went wrong.";
|
|
toast.error(message);
|
|
return null;
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, []);
|
|
|
|
// ─── GET /api/admin/groups ─────────────────────────────────────────────────
|
|
const fetchGroups = useCallback(
|
|
({ page = 1, limit = 10, filters = [], sort = [] } = {}) =>
|
|
request(async () => {
|
|
const res = await api.get(`${BASE}/groups`, {
|
|
params: { page, limit, filters: JSON.stringify(filters), sort: JSON.stringify(sort) },
|
|
});
|
|
const { data, attributes: attrs, pagination: pg } = res.data?.data ?? {};
|
|
setGroups(data ?? []);
|
|
setAttributes(attrs ?? []);
|
|
setPagination(pg ?? { page, limit, total: 0, totalPages: 1 });
|
|
return res.data;
|
|
}),
|
|
[request]
|
|
);
|
|
|
|
// ─── GET /api/admin/groups/archived ───────────────────────────────────────
|
|
const fetchArchivedGroups = useCallback(
|
|
({ page = 1, limit = 10, filters = [], sort = [] } = {}) =>
|
|
request(async () => {
|
|
const res = await api.get(`${BASE}/groups/archived`, {
|
|
params: { page, limit, filters: JSON.stringify(filters), sort: JSON.stringify(sort) },
|
|
});
|
|
const { data, attributes: attrs, pagination: pg } = res.data?.data ?? {};
|
|
setGroups(data ?? []);
|
|
setAttributes(attrs ?? []);
|
|
setPagination(pg ?? { page, limit, total: 0, totalPages: 1 });
|
|
return res.data;
|
|
}),
|
|
[request]
|
|
);
|
|
|
|
// ─── GET /api/admin/groups/:gid ───────────────────────────────────────────
|
|
const fetchGroup = useCallback(
|
|
(gid, paginationParams = {}) =>
|
|
request(async () => {
|
|
const { page = 1, limit = 10, filters = [], sort = [] } = paginationParams;
|
|
const res = await api.get(`${BASE}/groups/${gid}`, {
|
|
params: { page, limit, filters: JSON.stringify(filters), sort: JSON.stringify(sort) },
|
|
});
|
|
const { group: g, members: m } = res.data?.data ?? {};
|
|
setGroup(g ?? null);
|
|
setMembers(m?.data ?? []);
|
|
setPagination(m?.pagination ?? { page, limit, total: 0, totalPages: 1 });
|
|
return res.data;
|
|
}),
|
|
[request]
|
|
);
|
|
|
|
// ─── GET /api/admin/groups/:gid/users ─────────────────────────────────────
|
|
const fetchUsersInGroup = useCallback(
|
|
(gid) =>
|
|
request(async () => {
|
|
const res = await api.get(`${BASE}/groups/${gid}/users`);
|
|
setUsersIn(res.data?.data ?? []);
|
|
return res.data;
|
|
}),
|
|
[request]
|
|
);
|
|
|
|
// ─── GET /api/admin/groups/:gid/users/add ─────────────────────────────────
|
|
const fetchUsersNotInGroup = useCallback(
|
|
(gid) =>
|
|
request(async () => {
|
|
const res = await api.get(`${BASE}/groups/${gid}/users/add`);
|
|
setUsersNotIn(res.data?.data ?? []);
|
|
return res.data;
|
|
}),
|
|
[request]
|
|
);
|
|
|
|
// ─── GET /api/admin/groups/field-values ───────────────────────────────────
|
|
const fetchGroupFieldValues = useCallback(
|
|
(field) =>
|
|
request(async () => {
|
|
const res = await api.get(`${BASE}/groups/field-values`, { params: { field } });
|
|
return res.data;
|
|
}),
|
|
[request]
|
|
);
|
|
|
|
// ─── POST /api/admin/groups ────────────────────────────────────────────────
|
|
const createGroup = useCallback(
|
|
({ name, description, group_code }) =>
|
|
request(async () => {
|
|
const res = await api.post(`${BASE}/groups`, { name, description, group_code });
|
|
setGroups((prev) => [res.data?.data, ...prev]);
|
|
toast.success("Group created successfully.");
|
|
return res.data;
|
|
}),
|
|
[request]
|
|
);
|
|
|
|
// ─── PUT /api/admin/groups/:gid ───────────────────────────────────────────
|
|
const updateGroup = useCallback(
|
|
(gid, { name, description, group_code }) =>
|
|
request(async () => {
|
|
const res = await api.put(`${BASE}/groups/${gid}`, { name, description, group_code });
|
|
setGroups((prev) =>
|
|
prev.map((g) => (g.group_id === gid ? { ...g, ...res.data?.data } : g))
|
|
);
|
|
setGroup((prev) => (prev?.group_id === gid ? { ...prev, ...res.data?.data } : prev));
|
|
toast.success("Group updated successfully.");
|
|
return res.data;
|
|
}),
|
|
[request]
|
|
);
|
|
|
|
// ─── PATCH /api/admin/groups/:gid/deactivate ──────────────────────────────
|
|
const deactivateGroup = useCallback(
|
|
(gid) =>
|
|
request(async () => {
|
|
const res = await api.patch(`${BASE}/groups/${gid}/deactivate`);
|
|
setGroups((prev) =>
|
|
prev.map((g) => (g.group_id === gid ? { ...g, is_active: false } : g))
|
|
);
|
|
toast.success("Group deactivated.");
|
|
return res.data;
|
|
}),
|
|
[request]
|
|
);
|
|
|
|
// ─── DELETE /api/admin/groups/bulk ────────────────────────────────────────
|
|
const deactivateGroups = useCallback(
|
|
({ ids }) =>
|
|
request(async () => {
|
|
const res = await api.delete(`${BASE}/groups/bulk`, { data: { ids } });
|
|
const { deactivated_ids } = res.data?.data ?? {};
|
|
if (deactivated_ids?.length) {
|
|
setGroups((prev) =>
|
|
prev.map((g) =>
|
|
deactivated_ids.includes(g.group_id) ? { ...g, is_active: false } : g
|
|
)
|
|
);
|
|
toast.success(`${deactivated_ids.length} group(s) deactivated.`);
|
|
}
|
|
return res.data;
|
|
}),
|
|
[request]
|
|
);
|
|
|
|
// ─── PATCH /api/admin/groups/:gid/restore ─────────────────────────────────
|
|
const restoreGroup = useCallback(
|
|
(gid) =>
|
|
request(async () => {
|
|
const res = await api.patch(`${BASE}/groups/${gid}/restore`);
|
|
setGroups((prev) =>
|
|
prev.map((g) => (g.group_id === gid ? { ...g, is_active: true } : g))
|
|
);
|
|
toast.success("Group restored.");
|
|
return res.data;
|
|
}),
|
|
[request]
|
|
);
|
|
|
|
// ─── POST /api/admin/groups/bulk/restore ──────────────────────────────────
|
|
const restoreGroups = useCallback(
|
|
({ ids }) =>
|
|
request(async () => {
|
|
const res = await api.post(`${BASE}/groups/bulk/restore`, { ids });
|
|
const { restored_ids } = res.data?.data ?? {};
|
|
if (restored_ids?.length) {
|
|
setGroups((prev) =>
|
|
prev.map((g) =>
|
|
restored_ids.includes(g.group_id) ? { ...g, is_active: true } : g
|
|
)
|
|
);
|
|
toast.success(`${restored_ids.length} group(s) restored.`);
|
|
}
|
|
return res.data;
|
|
}),
|
|
[request]
|
|
);
|
|
|
|
// ─── DELETE /api/admin/groups/:gid/permanent ──────────────────────────────
|
|
const permanentlyDeleteGroup = useCallback(
|
|
(gid) =>
|
|
request(async () => {
|
|
const res = await api.delete(`${BASE}/groups/${gid}/permanent`);
|
|
setGroups((prev) => prev.filter((g) => g.group_id !== gid));
|
|
toast.success("Group permanently deleted.");
|
|
return res.data;
|
|
}),
|
|
[request]
|
|
);
|
|
|
|
// ─── DELETE /api/admin/groups/bulk/permanent ──────────────────────────────
|
|
const permanentlyDeleteGroups = useCallback(
|
|
({ ids }) =>
|
|
request(async () => {
|
|
const res = await api.delete(`${BASE}/groups/bulk/permanent`, { data: { ids } });
|
|
const { deleted_ids } = res.data?.data ?? {};
|
|
if (deleted_ids?.length) {
|
|
setGroups((prev) => prev.filter((g) => !deleted_ids.includes(g.group_id)));
|
|
toast.success(`${deleted_ids.length} group(s) permanently deleted.`);
|
|
}
|
|
return res.data;
|
|
}),
|
|
[request]
|
|
);
|
|
|
|
// ─── POST /api/admin/groups/:gid/users ────────────────────────────────────
|
|
const addUsersToGroup = useCallback(
|
|
(gid, user_ids) =>
|
|
request(async () => {
|
|
const res = await api.post(`${BASE}/groups/${gid}/users`, { user_ids });
|
|
setUsersNotIn((prev) => prev.filter((u) => !user_ids.includes(u.user_id)));
|
|
toast.success("Users added to group.");
|
|
return res.data;
|
|
}),
|
|
[request]
|
|
);
|
|
|
|
// ─── DELETE /api/admin/groups/:gid/users ──────────────────────────────────
|
|
const removeUsersFromGroup = useCallback(
|
|
(gid, user_ids) =>
|
|
request(async () => {
|
|
const res = await api.delete(`${BASE}/groups/${gid}/users`, { data: { user_ids } });
|
|
setMembers((prev) => prev.filter((u) => !user_ids.includes(u.user_id)));
|
|
setUsersIn((prev) => prev.filter((u) => !user_ids.includes(u.user_id)));
|
|
toast.success("Users removed from group.");
|
|
return res.data;
|
|
}),
|
|
[request]
|
|
);
|
|
|
|
return (
|
|
<UserGroupContext.Provider value={{
|
|
groups, group, members, usersIn, usersNotIn, attributes, pagination, loading,
|
|
setPagination,
|
|
fetchGroups, fetchArchivedGroups, fetchGroup,
|
|
fetchUsersInGroup, fetchUsersNotInGroup, fetchGroupFieldValues,
|
|
createGroup, updateGroup,
|
|
deactivateGroup, deactivateGroups,
|
|
restoreGroup, restoreGroups,
|
|
permanentlyDeleteGroup, permanentlyDeleteGroups,
|
|
addUsersToGroup, removeUsersFromGroup,
|
|
}}>
|
|
{children}
|
|
</UserGroupContext.Provider>
|
|
);
|
|
} |