mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-02 23:53:21 +00:00
Dispatchers see a one-line banner and a one-time toast while a Reactive/Emergency work order is past half of its SEV response window; the banner opens the board on exactly those work orders and dismisses for the session. Missed deadlines stay in the notification center until acknowledged, and Clear all skips them.
67 lines
2.1 KiB
TypeScript
67 lines
2.1 KiB
TypeScript
import { useCallback, useMemo, useRef, useState, type ReactNode } from "react";
|
|
import { NotificationSessionContext } from "./notification-session-context";
|
|
|
|
function addAll<T>(current: ReadonlySet<T>, ids: T[]): ReadonlySet<T> {
|
|
if (ids.every((id) => current.has(id))) {
|
|
return current;
|
|
}
|
|
const next = new Set(current);
|
|
ids.forEach((id) => next.add(id));
|
|
return next;
|
|
}
|
|
|
|
/**
|
|
* Dismissed and read notification ids for this app session only. Nothing is persisted, so a
|
|
* reload starts over and a condition that still holds shows up again.
|
|
*/
|
|
export function NotificationSessionProvider({ children }: { children: ReactNode }) {
|
|
const [dismissedIds, setDismissedIds] = useState<ReadonlySet<string>>(() => new Set());
|
|
const [readIds, setReadIds] = useState<ReadonlySet<string>>(() => new Set());
|
|
const [slaBannerDismissedIds, setSlaBannerDismissedIds] = useState<ReadonlySet<number>>(
|
|
() => new Set(),
|
|
);
|
|
// A ref, not state: claiming must be synchronous so a re-run effect can never toast twice.
|
|
const toastedSlaIds = useRef(new Set<number>());
|
|
|
|
const dismiss = useCallback((ids: string[]) => {
|
|
setDismissedIds((current) => addAll(current, ids));
|
|
}, []);
|
|
const markRead = useCallback((ids: string[]) => {
|
|
setReadIds((current) => addAll(current, ids));
|
|
}, []);
|
|
const dismissSlaBanner = useCallback((workOrderIds: number[]) => {
|
|
setSlaBannerDismissedIds((current) => addAll(current, workOrderIds));
|
|
}, []);
|
|
const claimSlaToasts = useCallback((workOrderIds: number[]) => {
|
|
const claimed = workOrderIds.filter((id) => !toastedSlaIds.current.has(id));
|
|
claimed.forEach((id) => toastedSlaIds.current.add(id));
|
|
return claimed;
|
|
}, []);
|
|
|
|
const value = useMemo(
|
|
() => ({
|
|
dismissedIds,
|
|
readIds,
|
|
dismiss,
|
|
markRead,
|
|
slaBannerDismissedIds,
|
|
dismissSlaBanner,
|
|
claimSlaToasts,
|
|
}),
|
|
[
|
|
dismissedIds,
|
|
readIds,
|
|
dismiss,
|
|
markRead,
|
|
slaBannerDismissedIds,
|
|
dismissSlaBanner,
|
|
claimSlaToasts,
|
|
],
|
|
);
|
|
|
|
return (
|
|
<NotificationSessionContext.Provider value={value}>
|
|
{children}
|
|
</NotificationSessionContext.Provider>
|
|
);
|
|
}
|