import { createContext, useCallback, useContext, useState } from "react"; import api from "@/utils/api.util"; import { toast } from "sonner"; const NotificationBroadcastsContext = createContext(null); export function useNotificationBroadcasts() { const ctx = useContext(NotificationBroadcastsContext); if (!ctx) throw new Error("useNotificationBroadcasts must be used within a NotificationBroadcastsProvider"); return ctx; } // ─── Initial States ──────────────────────────────────────────────────────────── const PAGINATION_INIT = { page: 1, limit: 10, totalRecords: 0, totalPages: 0, hasPrevPage: false, hasNextPage: false, }; export function NotificationBroadcastsProvider({ children }) { const [broadcasts, setBroadcasts] = useState([]); const [attributes, setAttributes] = useState([]); const [pagination, setPagination] = useState(PAGINATION_INIT); const [selectedBroadcast, setSelectedBroadcast] = useState(null); const [stickyBannerSetting, setStickyBannerSetting] = useState(null); 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(message); return null; } finally { setLoading(false); } }, []); // ─── GET /api/admin/announcements ───────────────────────────────────────── const fetchBroadcasts = useCallback( ({ page = 1, limit = 10, filters = [], sort = [] } = {}) => request(async () => { const { data } = await api.get("/admin/announcements", { params: { page, limit, filters: filters.length ? JSON.stringify(filters) : undefined, sort: sort.length ? JSON.stringify(sort) : undefined, }, }); const result = data?.data; setBroadcasts(result?.data ?? []); setPagination(result?.pagination ?? PAGINATION_INIT); setAttributes(result.attributes); return data.data; }), [request] ); // ─── GET /api/admin/announcements/:broadcastId ─────────────────────────── const fetchBroadcast = useCallback( (broadcastId) => request(async () => { const res = await api.get(`/admin/announcements/${broadcastId}`); setSelectedBroadcast(res.data?.data?.data ?? null); return res.data; }), [request] ); // ─── GET /api/admin/announcements/archived ──────────────────────────────── const fetchArchivedBroadcasts = useCallback( ({ page = 1, limit = 10, filters = [], sort = [] } = {}) => request(async () => { const { data } = await api.get("/admin/announcements/archived", { params: { page, limit, filters: filters.length ? JSON.stringify(filters) : undefined, sort: sort.length ? JSON.stringify(sort) : undefined, }, }); const final_data = data?.data; setBroadcasts(final_data?.data ?? []); setPagination(final_data?.pagination ?? PAGINATION_INIT); setAttributes(final_data.attributes); return data; }), [request] ); // ─── POST /api/admin/announcements ──────────────────────────────────────── const createBroadcast = useCallback( (fields) => request(async () => { const res = await api.post("/admin/announcements", fields); const broadcast = res.data?.data?.data ?? null; if (broadcast) { setBroadcasts((prev) => [broadcast, ...prev]); toast("Notification broadcast created."); } return res.data; }), [request] ); // ─── PATCH /api/admin/announcements/:broadcastId ───────────────────────── const updateBroadcast = useCallback( (broadcastId, fields) => request(async () => { const res = await api.patch(`/admin/announcements/${broadcastId}`, fields); const broadcast = res.data?.data?.data ?? null; if (broadcast) { setBroadcasts((prev) => prev.map((b) => (b.broadcast_id === broadcastId ? broadcast : b))); setSelectedBroadcast(broadcast); toast("Notification broadcast updated."); } return res.data; }), [request] ); // ─── PATCH /api/admin/announcements/:broadcastId/send ──────────────────── const sendBroadcast = useCallback( (broadcastId) => request(async () => { const res = await api.patch(`/admin/announcements/${broadcastId}/send`); const broadcast = res.data?.data?.data ?? null; if (broadcast) { setBroadcasts((prev) => prev.map((b) => (b.broadcast_id === broadcastId ? broadcast : b))); setSelectedBroadcast(broadcast); toast("Notification broadcast sent."); } return res.data; }), [request] ); // ─── DELETE /api/admin/announcements/:broadcastId ──────────────────────── const archiveBroadcast = useCallback( (broadcastId, { deletedBy } = {}) => request(async () => { const res = await api.delete(`/admin/announcements/${broadcastId}`, { data: { deletedBy }, }); setBroadcasts((prev) => prev.filter((b) => b.broadcast_id !== broadcastId)); setSelectedBroadcast((prev) => (prev?.broadcast_id === broadcastId ? null : prev)); toast("Notification broadcast archived."); return res.data; }), [request] ); // ─── DELETE /api/admin/announcements/bulk ───────────────────────────────── const archiveBroadcasts = useCallback( ({ ids }, { deletedBy } = {}) => request(async () => { const res = await api.delete("/admin/announcements/bulk", { data: { ids, deletedBy }, }); setBroadcasts((prev) => prev.filter((b) => !ids.includes(b.broadcast_id))); toast(`${ids.length} notification broadcast(s) archived.`); return res.data; }), [request] ); // ─── PATCH /api/admin/announcements/:broadcastId/restore ───────────────── const restoreBroadcast = useCallback( (broadcastId) => request(async () => { const res = await api.patch(`/admin/announcements/${broadcastId}/restore`); const broadcast = res.data?.data?.data ?? null; if (broadcast) { setBroadcasts((prev) => prev.filter((b) => b.broadcast_id !== broadcastId)); toast("Notification broadcast restored."); } return res.data; }), [request] ); // ─── PATCH /api/admin/announcements/bulk-restore ────────────────────────── const restoreBroadcasts = useCallback( ({ ids }) => request(async () => { const res = await api.patch("/admin/announcements/bulk-restore", { ids }); setBroadcasts((prev) => prev.filter((b) => !ids.includes(b.broadcast_id))); toast(`${ids.length} notification broadcast(s) restored.`); return res.data; }), [request] ); // ─── DELETE /api/admin/announcements/:broadcastId/permanent ────────────── const permanentlyDeleteBroadcast = useCallback( (broadcastId) => request(async () => { const res = await api.delete(`/admin/announcements/${broadcastId}/permanent`); setBroadcasts((prev) => prev.filter((b) => b.broadcast_id !== broadcastId)); toast("Notification broadcast permanently deleted."); return res.data; }), [request] ); // ─── GET /api/admin/announcements/sticky-banner ─────────────────────────── // Shared banner image for the whole rotating sticky bar — one image for // all (up to 3) concurrently-active announcements, not one per announcement. const fetchStickyBannerSetting = useCallback( () => request(async () => { const res = await api.get("/admin/announcements/sticky-banner"); const setting = res.data?.data?.data ?? null; setStickyBannerSetting(setting); return res.data; }), [request] ); // ─── PATCH /api/admin/announcements/sticky-banner ──────────────────────── const updateStickyBannerSetting = useCallback( (fields) => request(async () => { const res = await api.patch("/admin/announcements/sticky-banner", fields); const setting = res.data?.data?.data ?? null; if (setting) { setStickyBannerSetting((prev) => ({ ...prev, ...setting })); toast("Sticky banner image updated."); } return res.data; }), [request] ); // ─── DELETE /api/admin/announcements/bulk/permanent ────────────────────── const permanentlyDeleteBroadcasts = useCallback( ({ ids }) => request(async () => { const res = await api.delete("/admin/announcements/bulk/permanent", { data: { ids } }); setBroadcasts((prev) => prev.filter((b) => !ids.includes(b.broadcast_id))); toast(`${ids.length} notification broadcast(s) permanently deleted.`); return res.data; }), [request] ); return ( {children} ); }