improvement: duedate added for shipped cases. cases and tasks ui and ux updated accordingly.

This commit is contained in:
2026-07-13 16:30:36 +03:30
parent a391eee15f
commit 08a1f34c4f
29 changed files with 502 additions and 30 deletions

View 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>
);
}

View 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: 27 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';
}

View File

@@ -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: [],
});

View File

@@ -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
);