improvement: notify counter badge added to treatment and tasks tabs for upadted and edited cases.

This commit is contained in:
2026-07-13 17:44:44 +03:30
parent 547457d637
commit 2ad572f4c8
33 changed files with 908 additions and 31 deletions

View File

@@ -0,0 +1,19 @@
import { apiClient } from '@/lib/api/client';
import type { LabCaseTabReadTarget, TabBadgeCounts } from '@/lib/tabBadgeUtils';
export const notificationsApi = {
tabCounts: async (): Promise<{ success: boolean; data: TabBadgeCounts }> => {
const response = await apiClient.get('/notifications/tab-counts');
return response.data;
},
markTabRead: async (tab: LabCaseTabReadTarget): Promise<{ success: boolean }> => {
const response = await apiClient.post('/notifications/mark-tab-read', { tab });
return response.data;
},
markCaseRead: async (labCaseId: string): Promise<{ success: boolean }> => {
const response = await apiClient.post('/notifications/mark-case-read', { labCaseId });
return response.data;
},
};

View File

@@ -0,0 +1,60 @@
'use client';
import { useCallback, useEffect, useState } from 'react';
import { usePathname } from '@/i18n/navigation';
import { notificationsApi } from '@/lib/api/notifications';
import {
tabBadgesChangedEventName,
tabFromPathname,
type TabBadgeCounts,
} from '@/lib/tabBadgeUtils';
import { useAuth } from '@/lib/hooks/useAuth';
const EMPTY_COUNTS: TabBadgeCounts = {};
export function useTabBadgeCounts(): TabBadgeCounts {
const pathname = usePathname();
const { currentOrganization } = useAuth();
const [counts, setCounts] = useState<TabBadgeCounts>(EMPTY_COUNTS);
const fetchCounts = useCallback(async () => {
if (!currentOrganization?.id) {
setCounts(EMPTY_COUNTS);
return;
}
try {
const res = await notificationsApi.tabCounts();
setCounts(res.data ?? EMPTY_COUNTS);
} catch {
setCounts(EMPTY_COUNTS);
}
}, [currentOrganization?.id]);
useEffect(() => {
void fetchCounts();
}, [fetchCounts, pathname]);
useEffect(() => {
const onChanged = () => void fetchCounts();
window.addEventListener(tabBadgesChangedEventName(), onChanged);
return () => window.removeEventListener(tabBadgesChangedEventName(), onChanged);
}, [fetchCounts]);
return counts;
}
export function useMarkTabReadOnVisit() {
const pathname = usePathname();
const { currentOrganization } = useAuth();
useEffect(() => {
const tab = tabFromPathname(pathname);
// Cases tab badge clears per opened case (mark-case-read), not on tab visit.
if (!tab || tab === 'CASES' || !currentOrganization?.id) return;
void notificationsApi.markTabRead(tab).then(() => {
window.dispatchEvent(new Event(tabBadgesChangedEventName()));
});
}, [pathname, currentOrganization?.id]);
}

View File

@@ -0,0 +1,31 @@
export type TabBadgeCounts = {
cases?: number;
tasks?: number;
treatment?: number;
};
export type LabCaseTabReadTarget = 'CASES' | 'TASKS' | 'TREATMENT';
const TAB_BADGES_CHANGED_EVENT = 'tab-badges-changed';
export function notifyTabBadgesChanged() {
window.dispatchEvent(new Event(TAB_BADGES_CHANGED_EVENT));
}
export function tabBadgesChangedEventName() {
return TAB_BADGES_CHANGED_EVENT;
}
export function tabFromPathname(pathname: string): LabCaseTabReadTarget | null {
if (pathname === '/cases' || pathname.startsWith('/cases/')) return 'CASES';
if (pathname === '/tasks' || pathname.startsWith('/tasks/')) return 'TASKS';
if (pathname === '/treatment' || pathname.startsWith('/treatment/')) return 'TREATMENT';
return null;
}
export function badgeCountForPath(counts: TabBadgeCounts, pathname: string): number {
if (pathname === '/cases' || pathname.startsWith('/cases/')) return counts.cases ?? 0;
if (pathname === '/tasks' || pathname.startsWith('/tasks/')) return counts.tasks ?? 0;
if (pathname === '/treatment' || pathname.startsWith('/treatment/')) return counts.treatment ?? 0;
return 0;
}