Tasks 07 through 13 each changed something the rest of the system might want to know about, with no contract for saying so. And task 05 shipped a powerful segment editor with no feedback on whether a clinic defined its segments right. Events - A closed list of names, because a consumer branches on the string and a one-letter typo would produce an event nobody hears and no error either - Payloads carry uuids and scalars only; non-scalars are dropped, not serialised, so a consumer always fetches fresh rather than reading a stale detached entity - record() deliberately does not flush: the event row commits with the change it describes, so a rolled-back transaction leaves no event behind. A test pins exactly that - app:events:publish drains the outbox; five failed attempts park a row with its error rather than deleting it, because a silently dropped event is a loss with no trace. app:events:prune only ever removes published rows Reports - Resource utilisation separates available, occupied and active minutes. The gap between occupied and active is what exposes a bad segment definition, and available is multiplied by capacity so a three-chair room does not read as permanently over 100% - A resource with no calendar reports utilization: null, not zero — dividing by zero means something different from being idle - Plan accuracy compares planned against actual duration per service and flags both directions: running short wastes capacity that could have been sold. Its row links straight to editing that service's segments, because a report with no route to a fix does not get read Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
34 lines
1.2 KiB
TypeScript
34 lines
1.2 KiB
TypeScript
import { useQuery } from '@tanstack/react-query';
|
|
import { api, type ApiResponse } from '../lib/api';
|
|
import type { AccuracyRow, ReportEnvelope, UtilizationRow } from '../types';
|
|
|
|
/**
|
|
* گزارشهای بهرهوری و دقت برنامه.
|
|
*
|
|
* بازه همیشه صریح فرستاده میشود تا نمودار با پیشفرض سرور جابهجا نشود.
|
|
*/
|
|
export function useResourceUtilization(branchUuid: string | undefined, from: number, to: number) {
|
|
const query = useQuery({
|
|
queryKey: ['resource-utilization', branchUuid, from, to],
|
|
queryFn: () =>
|
|
api.get<ApiResponse<ReportEnvelope<UtilizationRow>>>(
|
|
`/api/v1/reports/resource-utilization?branch_uuid=${branchUuid}&from=${from}&to=${to}`,
|
|
),
|
|
enabled: !!branchUuid,
|
|
});
|
|
|
|
return { rows: query.data?.data?.rows ?? [], loading: query.isLoading };
|
|
}
|
|
|
|
export function usePlanAccuracy(from: number, to: number) {
|
|
const query = useQuery({
|
|
queryKey: ['plan-accuracy', from, to],
|
|
queryFn: () =>
|
|
api.get<ApiResponse<ReportEnvelope<AccuracyRow>>>(
|
|
`/api/v1/reports/plan-accuracy?from=${from}&to=${to}`,
|
|
),
|
|
});
|
|
|
|
return { rows: query.data?.data?.rows ?? [], loading: query.isLoading };
|
|
}
|