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 ( {children} ); }