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

@@ -11,6 +11,7 @@ import { FORM_SELECT_CLASS } from '@/components/shared/formSelectStyles';
import { LabCaseAttachmentPreview } from '@/components/ui/lab/LabCaseAttachmentPreview';
import { LabCaseAttachmentsDialog } from '@/components/ui/lab/LabCaseAttachmentsDialog';
import { labTaskStatusVariant } from '@/components/lab/labTaskStatusDisplay';
import { LabCaseDueDateBadge } from '@/components/lab/LabCaseDueDateBadge';
import {
formatToothList,
prosthesisTypeBadgeStyleFromCatalog,
@@ -104,9 +105,12 @@ export function CaseDetailPanel({
<div className="space-y-4">
<header className="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between border-b border-border pb-3">
<div className="min-w-0 flex-1 space-y-1">
<h2 className="text-lg font-semibold text-text-primary">
{formatPatientName(labCase.patient)}
</h2>
<div className="flex flex-wrap items-center gap-2">
<h2 className="text-lg font-semibold text-text-primary">
{formatPatientName(labCase.patient)}
</h2>
<LabCaseDueDateBadge dueDate={labCase.dueDate} locale={locale} />
</div>
{!canEditImportant && labCase.isImportant ? (
<Badge variant="warning" fixedWidth={false} className="mt-1">
{t('importantLabel')}

View File

@@ -13,6 +13,7 @@ import {
formatCaseDateTime,
formatPatientName,
} from '@/components/lab/caseDetailUtils';
import { LabCaseDueDateBadge } from '@/components/lab/LabCaseDueDateBadge';
import { casesApi } from '@/lib/api/cases';
import { tasksApi } from '@/lib/api/tasks';
import { treatmentCatalogApi } from '@/lib/api/treatment-catalog';
@@ -376,6 +377,11 @@ export function CasesPage() {
<div className="font-medium text-text-primary">
{formatPatientName(item.patient)}
</div>
<LabCaseDueDateBadge
dueDate={item.dueDate}
locale={locale}
className="text-[10px]"
/>
{item.isImportant ? (
<Badge variant="warning" fixedWidth={false}>
{t('importantLabel')}

View File

@@ -2,6 +2,7 @@
import { useTranslations } from 'next-intl';
import { Badge } from '@/components/ui/shared/Badge';
import { LabCaseDueDateBadge } from '@/components/lab/LabCaseDueDateBadge';
import type { CaseTaskGroup } from '@/components/lab/taskListGrouping';
import { countCaseTaskProgress } from '@/components/lab/taskListGrouping';
@@ -34,6 +35,11 @@ export function TaskCaseGroupHeader({ caseGroup, locale }: TaskCaseGroupHeaderPr
{t('fromClinic', { name: caseGroup.clinic.name })} ·{' '}
{formatPatientName(caseGroup.patient)}
</p>
<LabCaseDueDateBadge
dueDate={caseGroup.caseDueDate}
locale={locale}
className="text-[10px]"
/>
{caseGroup.isImportant ? (
<Badge variant="warning" fixedWidth={false} className="text-[10px]">
{t('importantBadge')}

View File

@@ -11,6 +11,7 @@ import {
labTaskStatusSelectStyle,
labTaskStatusVariant,
} from '@/components/lab/labTaskStatusDisplay';
import { LabCaseDueDateBadge } from '@/components/lab/LabCaseDueDateBadge';
import {
formatToothList,
prosthesisTypeBadgeStyleFromCatalog,
@@ -101,6 +102,13 @@ export function TaskRow({
{t('importantBadge')}
</Badge>
) : null}
{flatMode && task.caseDueDate && !exiting ? (
<LabCaseDueDateBadge
dueDate={task.caseDueDate}
locale={locale}
className="text-[10px]"
/>
) : null}
</div>
{flatMode ? (
<p className="text-[11px] text-text-secondary truncate">

View File

@@ -68,6 +68,7 @@ export function TasksPage() {
const [stepCompleted, setStepCompleted] = useState(DEFAULT_TASKS_VIEW.stepCompleted);
const [importantOnly, setImportantOnly] = useState(DEFAULT_TASKS_VIEW.importantOnly);
const [assignedToMe, setAssignedToMe] = useState(DEFAULT_TASKS_VIEW.assignedToMe);
const [overdueOnly, setOverdueOnly] = useState(DEFAULT_TASKS_VIEW.overdueOnly);
const [sortBy, setSortBy] = useState<TaskSortField>(DEFAULT_TASKS_VIEW.sortBy);
const [sortDir, setSortDir] = useState<'asc' | 'desc'>(DEFAULT_TASKS_VIEW.sortDir);
const [highlightTaskId, setHighlightTaskId] = useState<string | null>(
@@ -103,8 +104,9 @@ export function TasksPage() {
if (stepCompleted) params.stepCompleted = stepCompleted;
if (importantOnly) params.pinImportant = true;
if (assignedToMe) params.assignedToMe = true;
if (overdueOnly) params.overdue = true;
return params;
}, [page, search, clinicId, statusFilter, stepCompleted, importantOnly, assignedToMe, sortBy, sortDir]);
}, [page, search, clinicId, statusFilter, stepCompleted, importantOnly, assignedToMe, overdueOnly, sortBy, sortDir]);
const displayModel = useMemo(() => groupTasksForDisplay(tasks, sortBy), [tasks, sortBy]);
@@ -120,6 +122,7 @@ export function TasksPage() {
sortDir,
importantOnly,
assignedToMe,
overdueOnly,
page,
highlightTaskId,
}),
@@ -132,6 +135,7 @@ export function TasksPage() {
sortDir,
importantOnly,
assignedToMe,
overdueOnly,
page,
highlightTaskId,
],
@@ -197,6 +201,7 @@ export function TasksPage() {
setStepCompleted(DEFAULT_TASKS_VIEW.stepCompleted);
setImportantOnly(DEFAULT_TASKS_VIEW.importantOnly);
setAssignedToMe(DEFAULT_TASKS_VIEW.assignedToMe);
setOverdueOnly(DEFAULT_TASKS_VIEW.overdueOnly);
setSortBy(DEFAULT_TASKS_VIEW.sortBy);
setSortDir(DEFAULT_TASKS_VIEW.sortDir);
setPage(DEFAULT_TASKS_VIEW.page);
@@ -213,6 +218,8 @@ export function TasksPage() {
setStatusFilter(DEFAULT_TASKS_VIEW.statusFilter);
setStepCompleted(DEFAULT_TASKS_VIEW.stepCompleted);
setImportantOnly(DEFAULT_TASKS_VIEW.importantOnly);
setAssignedToMe(DEFAULT_TASKS_VIEW.assignedToMe);
setOverdueOnly(DEFAULT_TASKS_VIEW.overdueOnly);
setSortBy(DEFAULT_TASKS_VIEW.sortBy);
setSortDir(DEFAULT_TASKS_VIEW.sortDir);
setExpandedCommentsTaskId(null);
@@ -289,6 +296,8 @@ export function TasksPage() {
return 'groupingOffTaskType';
case 'status':
return 'groupingOffStatus';
case 'dueDate':
return 'groupingOffDueDate';
default:
return null;
}
@@ -410,6 +419,7 @@ export function TasksPage() {
className={`${filterSelectClass} min-w-0 flex-1`}
>
<option value="date">{t('sortDate')}</option>
<option value="dueDate">{t('sortDueDate')}</option>
<option value="clinic">{t('sortClinic')}</option>
<option value="patient">{t('sortPatient')}</option>
<option value="prosthesis">{t('sortProsthesis')}</option>
@@ -440,6 +450,12 @@ export function TasksPage() {
label={t('assignedToMe')}
className="text-xs [&_span:last-child]:text-xs"
/>
<Checkbox
checked={overdueOnly}
onChange={(checked) => applyFilterChange(() => setOverdueOnly(checked))}
label={t('overdueOnly')}
className="text-xs [&_span:last-child]:text-xs"
/>
{showReset ? (
<Button type="button" variant="ghost" size="sm" onClick={resetView}>
{t('resetView')}