improvement: duedate added for shipped cases. cases and tasks ui and ux updated accordingly.
This commit is contained in:
29
frontend/src/components/lab/LabCaseDueDateBadge.tsx
Normal file
29
frontend/src/components/lab/LabCaseDueDateBadge.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
'use client';
|
||||
|
||||
import { Badge } from '@/components/ui/shared/Badge';
|
||||
import {
|
||||
dueDateBadgeVariantFromIso,
|
||||
formatLabCaseDueDate,
|
||||
} from '@/components/lab/labCaseDueDateDisplay';
|
||||
|
||||
interface LabCaseDueDateBadgeProps {
|
||||
dueDate: string | null | undefined;
|
||||
locale: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function LabCaseDueDateBadge({ dueDate, locale, className }: LabCaseDueDateBadgeProps) {
|
||||
const label = formatLabCaseDueDate(dueDate, locale);
|
||||
if (!label) return null;
|
||||
|
||||
return (
|
||||
<Badge
|
||||
variant={dueDateBadgeVariantFromIso(dueDate)}
|
||||
fixedWidth={false}
|
||||
className={className}
|
||||
title={label}
|
||||
>
|
||||
{label}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
46
frontend/src/components/lab/labCaseDueDateDisplay.ts
Normal file
46
frontend/src/components/lab/labCaseDueDateDisplay.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import type { BadgeVariant } from '@/components/ui/shared/Badge';
|
||||
|
||||
export function toDateInputValue(iso: string | null | undefined): string {
|
||||
if (!iso) return '';
|
||||
const date = new Date(iso);
|
||||
if (Number.isNaN(date.getTime())) return '';
|
||||
return date.toISOString().slice(0, 10);
|
||||
}
|
||||
|
||||
export function startOfUtcDay(date = new Date()): Date {
|
||||
const d = new Date(date);
|
||||
d.setUTCHours(0, 0, 0, 0);
|
||||
return d;
|
||||
}
|
||||
|
||||
export function formatLabCaseDueDate(
|
||||
iso: string | null | undefined,
|
||||
locale: string,
|
||||
): string | null {
|
||||
if (!iso) return null;
|
||||
const date = new Date(iso);
|
||||
if (Number.isNaN(date.getTime())) return null;
|
||||
return new Intl.DateTimeFormat(locale, {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
}).format(date);
|
||||
}
|
||||
|
||||
/** Whole calendar days from today (UTC) until due date. Negative = overdue. */
|
||||
export function daysUntilDue(iso: string | null | undefined): number | null {
|
||||
if (!iso) return null;
|
||||
const due = new Date(iso);
|
||||
if (Number.isNaN(due.getTime())) return null;
|
||||
const msPerDay = 24 * 60 * 60 * 1000;
|
||||
return Math.round((startOfUtcDay(due).getTime() - startOfUtcDay().getTime()) / msPerDay);
|
||||
}
|
||||
|
||||
/** Green: >7 days · Yellow: 2–7 days · Red: ≤1 day (today, tomorrow, or overdue). */
|
||||
export function dueDateBadgeVariantFromIso(iso: string | null | undefined): BadgeVariant {
|
||||
const days = daysUntilDue(iso);
|
||||
if (days === null) return 'default';
|
||||
if (days <= 1) return 'danger';
|
||||
if (days <= 7) return 'warning';
|
||||
return 'success';
|
||||
}
|
||||
@@ -14,6 +14,8 @@ export type CaseTaskGroup = {
|
||||
clinic: LabTaskListItem['clinic'];
|
||||
patient: LabTaskListItem['patient'];
|
||||
caseSentAt: string | null;
|
||||
caseDueDate: string | null;
|
||||
isCaseOverdue: boolean;
|
||||
isImportant: boolean;
|
||||
prosthesisGroups: ProsthesisTaskGroup[];
|
||||
};
|
||||
@@ -47,6 +49,8 @@ export function groupTasksForDisplay(
|
||||
clinic: task.clinic,
|
||||
patient: task.patient,
|
||||
caseSentAt: task.caseSentAt ?? null,
|
||||
caseDueDate: task.caseDueDate ?? null,
|
||||
isCaseOverdue: task.isCaseOverdue,
|
||||
isImportant: task.isImportant,
|
||||
prosthesisGroups: [],
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@ export type TasksViewState = {
|
||||
sortDir: 'asc' | 'desc';
|
||||
importantOnly: boolean;
|
||||
assignedToMe: boolean;
|
||||
overdueOnly: boolean;
|
||||
page: number;
|
||||
highlightTaskId: string | null;
|
||||
};
|
||||
@@ -22,6 +23,7 @@ export const DEFAULT_TASKS_VIEW: TasksViewState = {
|
||||
sortDir: 'desc',
|
||||
importantOnly: false,
|
||||
assignedToMe: false,
|
||||
overdueOnly: false,
|
||||
page: 1,
|
||||
highlightTaskId: null,
|
||||
};
|
||||
@@ -38,6 +40,7 @@ export function isDefaultTasksView(state: TasksViewState): boolean {
|
||||
state.sortDir === DEFAULT_TASKS_VIEW.sortDir &&
|
||||
state.importantOnly === DEFAULT_TASKS_VIEW.importantOnly &&
|
||||
state.assignedToMe === DEFAULT_TASKS_VIEW.assignedToMe &&
|
||||
state.overdueOnly === DEFAULT_TASKS_VIEW.overdueOnly &&
|
||||
state.page === DEFAULT_TASKS_VIEW.page &&
|
||||
state.highlightTaskId === DEFAULT_TASKS_VIEW.highlightTaskId
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user