improvement: lab/clinic commiunication flow completely overhauled. no more shit.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { apiClient } from '@/lib/api/client';
|
||||
import type { LabCaseActivityItem } from '@/types/lab-case-activity';
|
||||
import type { LabCaseTabReadTarget, TabBadgeCounts } from '@/lib/tabBadgeUtils';
|
||||
|
||||
export const notificationsApi = {
|
||||
@@ -7,6 +8,16 @@ export const notificationsApi = {
|
||||
return response.data;
|
||||
},
|
||||
|
||||
listLabCaseActivities: async (
|
||||
labCaseId: string,
|
||||
limit = 50,
|
||||
): Promise<{ success: boolean; data: LabCaseActivityItem[] }> => {
|
||||
const response = await apiClient.get(`/notifications/lab-cases/${labCaseId}/activities`, {
|
||||
params: { limit },
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
|
||||
markTabRead: async (tab: LabCaseTabReadTarget): Promise<{ success: boolean }> => {
|
||||
const response = await apiClient.post('/notifications/mark-tab-read', { tab });
|
||||
return response.data;
|
||||
|
||||
@@ -24,6 +24,21 @@ export const treatmentsApi = {
|
||||
return response.data;
|
||||
},
|
||||
|
||||
listPatientLabCases: async (
|
||||
patientId: string,
|
||||
): Promise<{ success: boolean; data: import('@/types/lab-case-activity').PatientLabCaseSummary[] }> => {
|
||||
const response = await apiClient.get(`/treatments/patients/${patientId}/lab-cases`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
listUnreadLabCases: async (): Promise<{
|
||||
success: boolean;
|
||||
data: import('@/types/lab-case-activity').PatientLabCaseSummary[];
|
||||
}> => {
|
||||
const response = await apiClient.get('/treatments/lab-cases/unread');
|
||||
return response.data;
|
||||
},
|
||||
|
||||
getDraft: async (
|
||||
appointmentId: string,
|
||||
): Promise<{ success: boolean; data: PastTreatment | null }> => {
|
||||
|
||||
@@ -50,8 +50,8 @@ export function useMarkTabReadOnVisit() {
|
||||
|
||||
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;
|
||||
// Cases and Treatment tab badges clear per opened case (mark-case-read), not on tab visit.
|
||||
if (!tab || tab === 'CASES' || tab === 'TREATMENT' || !currentOrganization?.id) return;
|
||||
|
||||
void notificationsApi.markTabRead(tab).then(() => {
|
||||
window.dispatchEvent(new Event(tabBadgesChangedEventName()));
|
||||
|
||||
56
frontend/src/lib/labCaseActivityLabels.ts
Normal file
56
frontend/src/lib/labCaseActivityLabels.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import type { LabCaseActivityItem } from '@/types/lab-case-activity';
|
||||
|
||||
type ActivityLabelTranslator = (
|
||||
key: string,
|
||||
values?: Record<string, string | number>,
|
||||
) => string;
|
||||
|
||||
export function formatLabCaseActivityLine(
|
||||
activity: LabCaseActivityItem,
|
||||
t: ActivityLabelTranslator,
|
||||
locale: string,
|
||||
): string {
|
||||
const actor = activity.actorName ?? t('activityUnknownActor');
|
||||
const date = new Date(activity.createdAt).toLocaleString(locale, {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
});
|
||||
|
||||
switch (activity.type) {
|
||||
case 'CASE_SENT':
|
||||
return t('activityCaseSent', { date });
|
||||
case 'CLINIC_COMMENT':
|
||||
return t('activityClinicComment', {
|
||||
actor,
|
||||
preview: truncatePreview(activity.commentBody),
|
||||
date,
|
||||
});
|
||||
case 'LAB_COMMENT':
|
||||
return t('activityLabComment', {
|
||||
actor,
|
||||
preview: truncatePreview(activity.commentBody),
|
||||
date,
|
||||
});
|
||||
case 'TASK_COMPLETED':
|
||||
return t('activityTaskCompleted', {
|
||||
step: activity.stepLabel ?? t('activityUnknownStep'),
|
||||
actor,
|
||||
date,
|
||||
});
|
||||
case 'CASE_IMPORTANT':
|
||||
return t('activityCaseImportant', { actor, date });
|
||||
case 'CASE_AMENDED':
|
||||
return t('activityCaseAmended', { actor, date });
|
||||
default:
|
||||
return t('activityGeneric', { date });
|
||||
}
|
||||
}
|
||||
|
||||
function truncatePreview(text: string | null | undefined, max = 60): string {
|
||||
const trimmed = text?.trim() ?? '';
|
||||
if (!trimmed) return '…';
|
||||
if (trimmed.length <= max) return trimmed;
|
||||
return `${trimmed.slice(0, max - 1)}…`;
|
||||
}
|
||||
Reference in New Issue
Block a user