mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
281 lines
12 KiB
React
281 lines
12 KiB
React
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 (
|
|
<NotificationBroadcastsContext.Provider value={{
|
|
broadcasts,
|
|
attributes,
|
|
pagination,
|
|
selectedBroadcast,
|
|
stickyBannerSetting,
|
|
loading,
|
|
setPagination,
|
|
setSelectedBroadcast,
|
|
fetchBroadcasts,
|
|
fetchBroadcast,
|
|
fetchArchivedBroadcasts,
|
|
createBroadcast,
|
|
updateBroadcast,
|
|
sendBroadcast,
|
|
archiveBroadcast,
|
|
archiveBroadcasts,
|
|
restoreBroadcast,
|
|
restoreBroadcasts,
|
|
permanentlyDeleteBroadcast,
|
|
permanentlyDeleteBroadcasts,
|
|
fetchStickyBannerSetting,
|
|
updateStickyBannerSetting,
|
|
}}>
|
|
{children}
|
|
</NotificationBroadcastsContext.Provider>
|
|
);
|
|
}
|