import { createContext, useContext, useState, useCallback } from "react"; import api from "@/utils/api.util"; import { toast } from "sonner"; const UserContext = createContext(null); export const useUsers = () => { const ctx = useContext(UserContext); if (!ctx) throw new Error("useUsers must be used inside UserProvider"); return ctx; }; const PAGINATION_INIT = { page: 1, limit: 10, totalRecords: 0, totalPages: 0, hasPrevPage: false, hasNextPage: false, }; const BASE = "/admin"; export const UserProvider = ({ children }) => { const [users, setUsers] = useState([]); const [user, setUser] = useState(null); const [sessions, setSessions] = useState([]); const [pagination, setPagination] = useState(PAGINATION_INIT); const [attributes, setAttributes] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const request = useCallback(async (fn) => { setLoading(true); setError(null); try { return await fn(); } catch (err) { const message = err?.response?.data?.message || err.message || "Something went wrong."; setError(message); toast.error(message); return null; } finally { setLoading(false); } }, []); // ─── GET /api/admin/users ────────────────────────────────────────────────── const fetchUsers = useCallback( ({ page = 1, limit = 10, filters = [], sort = [] } = {}) => request(async () => { const { data } = await api.get(`${BASE}/users`, { params: { page, limit, filters: filters.length ? JSON.stringify(filters) : undefined, sort: sort.length ? JSON.stringify(sort) : undefined, }, }); const final_data = data?.data; setUsers(final_data?.data ?? []); setPagination(final_data?.pagination ?? PAGINATION_INIT); setAttributes(final_data?.attributes ?? []); return data?.data; }), [request] ); // ─── GET /api/admin/users/archived ──────────────────────────────────────── const fetchArchivedUsers = useCallback( ({ page = 1, limit = 10, filters = [], sort = [] } = {}) => request(async () => { const { data } = await api.get(`${BASE}/users/archived`, { params: { page, limit, filters: filters.length ? JSON.stringify(filters) : undefined, sort: sort.length ? JSON.stringify(sort) : undefined, }, }); const final_data = data?.data; setUsers(final_data?.data ?? []); setPagination(final_data?.pagination ?? PAGINATION_INIT); setAttributes(final_data?.attributes ?? []); return data?.data; }), [request] ); // ─── GET /api/admin/users/:id ───────────────────────────────────────────── const fetchUser = useCallback( (userId) => request(async () => { const res = await api.get(`${BASE}/users/${userId}`); setUser(res.data?.data ?? null); return res.data; }), [request] ); // ─── POST /api/admin/users/staff ────────────────────────────────────────── const addStaffUser = useCallback( (payload) => request(async () => { const res = await api.post(`${BASE}/users/staff`, payload); toast.success("Staff user added successfully."); return res.data; }), [request] ); // ─── PUT /api/admin/users/:id ───────────────────────────────────────────── const updateUser = useCallback( (userId, payload) => request(async () => { const res = await api.put(`${BASE}/users/${userId}`, payload); setUsers((prev) => prev.map((u) => (u.user_id === userId ? { ...u, ...res.data?.data } : u)) ); toast.success("User updated successfully."); return res.data; }), [request] ); // ─── DELETE /api/admin/users/:id ────────────────────────────────────────── const deactivateUser = useCallback( (userId) => request(async () => { const res = await api.delete(`${BASE}/users/${userId}`); setUsers((prev) => prev.map((u) => (u.user_id === userId ? { ...u, is_active: false } : u)) ); toast.success("User deactivated."); return res.data; }), [request] ); // ─── DELETE /api/admin/users/bulk ───────────────────────────────────────── const deactivateUsers = useCallback( ({ ids }) => request(async () => { const res = await api.delete(`${BASE}/users/bulk`, { data: { ids } }); const { deactivated_ids } = res.data?.data ?? {}; if (deactivated_ids?.length) { setUsers((prev) => prev.map((u) => deactivated_ids.includes(u.user_id) ? { ...u, is_active: false } : u ) ); toast.success(`${deactivated_ids.length} user(s) deactivated.`); } return res.data; }), [request] ); // ─── POST /api/admin/users/:id/restore ──────────────────────────────────── const restoreUser = useCallback( (userId) => request(async () => { const res = await api.post(`${BASE}/users/${userId}/restore`); setUsers((prev) => prev.map((u) => (u.user_id === userId ? { ...u, is_active: true } : u)) ); toast.success("User restored."); return res.data; }), [request] ); // ─── POST /api/admin/users/bulk/restore ─────────────────────────────────── const restoreUsers = useCallback( ({ ids }) => request(async () => { const res = await api.post(`${BASE}/users/bulk/restore`, { ids }); const { restored_ids } = res.data?.data ?? {}; if (restored_ids?.length) { setUsers((prev) => prev.map((u) => restored_ids.includes(u.user_id) ? { ...u, is_active: true } : u ) ); toast.success(`${restored_ids.length} user(s) restored.`); } return res.data; }), [request] ); // ─── GET /api/admin/users/:id/sessions ──────────────────────────────────── const fetchUserSessions = useCallback( (userId) => request(async () => { const res = await api.get(`${BASE}/users/${userId}/sessions`); setSessions(res.data?.data ?? []); return res.data; }), [request] ); // ─── DELETE /api/admin/users/:id/sessions/:sid ──────────────────────────── const terminateSession = useCallback( (userId, sessionId) => request(async () => { const res = await api.delete(`${BASE}/users/${userId}/sessions/${sessionId}`); setSessions((prev) => prev.map((s) => (s.session_id === sessionId ? { ...s, is_active: false } : s)) ); toast.success("Session terminated."); return res.data; }), [request] ); // ─── GET /api/admin/users/field-values ──────────────────────────────────── const fetchUserFieldValues = useCallback( (field) => request(async () => { const res = await api.get(`${BASE}/users/field-values`, { params: { field } }); return res.data?.data; }), [request] ); return ( {children} ); };