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

@@ -4,7 +4,8 @@ import { useEffect, useState } from 'react';
import { useTranslations } from 'next-intl';
import { Button } from '@/components/ui/shared/Button';
import { Checkbox } from '@/components/ui/shared/Checkbox';
import { isDetailReadyForLabDispatch } from '@/components/treatment/treatmentDetailRules';
import { isDetailReadyForLabDispatch, isLabCaseCompleted } from '@/components/treatment/treatmentDetailRules';
import { toDateInputValue } from '@/components/lab/labCaseDueDateDisplay';
import { FORM_SELECT_CLASS } from '@/components/shared/formSelectStyles';
import { LinkedOrganizationSearchCombobox } from '@/components/ui/treatment/LinkedOrganizationSearchCombobox';
import { CaseSentLabel } from '@/components/ui/treatment/CaseSentLabel';
@@ -221,6 +222,73 @@ export function LabCasesDispatchPanel({
setApplyAllProsthesis('');
}
const caseFullyComplete = isLabCaseCompleted(activeLabCase?.taskProgress);
const canEditDueDate = canEdit && !disabled && (!sent || !caseFullyComplete);
async function handleSentDueDateBlur(nextValue: string) {
if (!activeLabCase?.id || !sent || !canEditDueDate) return;
const dueDate = nextValue || null;
if (dueDate === (activeLabCase.dueDate?.slice(0, 10) ?? null)) return;
try {
const response = await treatmentsApi.updateLabCaseDueDate(activeLabCase.id, dueDate);
updateActiveLabCase({
dueDate: response.data.dueDate,
taskProgress: response.data.taskProgress ?? activeLabCase.taskProgress,
});
} catch (error) {
onCommentError?.(error instanceof Error ? error.message : t('dueDateUpdateError'));
}
}
function renderShipmentCardHeader() {
return (
<div className="flex flex-col gap-3 border-b border-border/60 pb-3 sm:flex-row sm:items-start sm:justify-between">
<p className="text-xs font-semibold text-text-primary">{t('labShipmentIncludedDetails')}</p>
{renderDueDateField()}
</div>
);
}
function renderDueDateField() {
if (!activeLabCase) return null;
const inputValue = toDateInputValue(activeLabCase.dueDate);
return (
<label className="block shrink-0 sm:max-w-[11rem] sm:text-end">
<span className="block text-xs font-medium text-text-secondary sm:text-end">
{t('dueDateLabel')}{' '}
<span className="font-normal text-text-muted">({t('dueDateOptional')})</span>
</span>
<input
type="date"
value={inputValue}
disabled={!canEditDueDate}
onChange={(e) => {
if (!sent) {
updateActiveLabCase({ dueDate: e.target.value || null });
}
}}
onBlur={(e) => {
if (sent) void handleSentDueDateBlur(e.target.value);
}}
className={`${FORM_SELECT_CLASS} mt-2 w-full rounded-md px-2 py-1.5 text-sm`}
/>
{sent && caseFullyComplete && activeLabCase.dueDate ? (
<p className="mt-1.5 text-[11px] text-text-muted sm:text-end">{t('dueDateLockedCompleted')}</p>
) : null}
</label>
);
}
function renderIncludedDetailSummary() {
if (!activeDetail) return null;
return (
<p className="text-sm text-text-primary rounded-[var(--radius-sm)] border border-border/50 bg-background-secondary/50 px-3 py-2">
{detailSummary(activeDetail)}
</p>
);
}
const activeDetailAttachments = activeDetail.attachmentMetas ?? [];
return (
@@ -248,16 +316,10 @@ export function LabCasesDispatchPanel({
<p className="text-xs text-text-muted">{t('labDispatchEmpty')}</p>
) : activeLabCase ? (
<div className="space-y-4 border border-border/60 rounded-[var(--radius-md)] p-4 bg-background-secondary/30">
{renderShipmentCardHeader()}
{sent ? (
<>
<div>
<p className="text-xs font-medium text-text-secondary mb-2">
{t('labShipmentIncludedDetails')}
</p>
<p className="text-sm text-text-primary rounded-[var(--radius-sm)] border border-border/50 bg-background-secondary/50 px-3 py-2">
{detailSummary(activeDetail)}
</p>
</div>
{renderIncludedDetailSummary()}
{activeLabCase.id ? (
<LabCaseCommentsPanel
@@ -296,14 +358,7 @@ export function LabCasesDispatchPanel({
</>
) : (
<>
<div>
<p className="text-xs font-medium text-text-secondary mb-2">
{t('labShipmentIncludedDetails')}
</p>
<p className="text-sm text-text-primary rounded-[var(--radius-sm)] border border-border/50 bg-background-secondary/50 px-3 py-2">
{detailSummary(activeDetail)}
</p>
</div>
{renderIncludedDetailSummary()}
{!sent && activeDetailAttachments.length > 0 ? (
<div>