bugfix: no-detail treatment ui made some confusion. it is improved. at least to a degree.

This commit is contained in:
2026-08-22 19:36:37 +03:30
parent f52ad6b84a
commit 50725b7b3f
9 changed files with 27 additions and 43 deletions

View File

@@ -717,6 +717,7 @@
"detailsTitle": "Treatment details",
"addDetail": "Add detail",
"confirmRemoveDetail": "Remove this treatment detail?",
"confirmRemoveLastDetail": "This is the last detail. Removing it leaves the plan empty until you add another. Continue?",
"removeDetailAria": "Remove detail {n}",
"detailLabel": "Detail {n}",
"detailSentBadge": "sent",

View File

@@ -718,6 +718,7 @@
"detailsTitle": "جزئیات درمان",
"addDetail": "افزودن جزئیات",
"confirmRemoveDetail": "این جزئیات درمان حذف شود؟",
"confirmRemoveLastDetail": "این آخرین جزئیات است. با حذف آن برنامه خالی می‌ماند تا جزئیات جدیدی اضافه کنید. ادامه می‌دهید؟",
"removeDetailAria": "حذف جزئیات {n}",
"detailLabel": "جزئیات {n}",
"detailSentBadge": "ارسال‌شده",

View File

@@ -717,6 +717,7 @@
"detailsTitle": "Behandeldetails",
"addDetail": "Detail toevoegen",
"confirmRemoveDetail": "Dit behandelingsdetail verwijderen?",
"confirmRemoveLastDetail": "Dit is het laatste detail. Als u het verwijdert, blijft het plan leeg tot u een nieuw detail toevoegt. Doorgaan?",
"removeDetailAria": "Detail {n} verwijderen",
"detailLabel": "Detail {n}",
"detailSentBadge": "verzonden",

View File

@@ -12,3 +12,10 @@ export type DayStripItem = {
subtitle: string;
canDelete?: boolean;
};
/** Unscheduled card banner: first lines type only. Empty / missing type → no color. */
export function unscheduledStripColorCode(
details: readonly { treatmentType?: string | null }[],
): string {
return details[0]?.treatmentType?.trim() ?? '';
}

View File

@@ -28,7 +28,7 @@ interface TreatmentDetailsEditorProps {
canEdit: boolean;
saveStatus: 'idle' | 'dirty' | 'saving' | 'saved' | 'error';
uploadBusy: boolean;
onAddDetail: (initialType?: string) => void;
onAddDetail: () => void;
onRemoveDetail?: (detailClientId: string) => void;
onUploadFiles: (files: File[], onProgress: (percent: number) => void) => Promise<void>;
onRemoveAttachment?: (attachmentId: string) => void;
@@ -113,7 +113,7 @@ export function TreatmentDetailsEditor({
type="button"
variant="primary"
disabled={!canEdit || disabled}
onClick={() => onAddDetail()}
onClick={onAddDetail}
fullWidth
className="sm:w-auto shrink-0"
>
@@ -269,34 +269,7 @@ export function TreatmentDetailsEditor({
{footer ? <div className="pt-1">{footer}</div> : null}
</div>
) : showFields && !activeDetail ? (
<div className="space-y-3">
<Dropdown
label={t('treatmentType')}
value=""
onChange={(e) => {
const nextType = e.target.value;
if (nextType) onAddDetail(nextType);
}}
disabled={!canEdit || disabled}
>
<option value="">{t('treatmentTypePlaceholder')}</option>
{treatmentCatalog.map((entry, index) => (
<option key={entry.code} value={entry.code} style={treatmentTypeOptionStyle(entry.code, index)}>
{entry.label}
</option>
))}
</Dropdown>
{chart ? (
<div className="relative min-w-0 w-full">
<div className="absolute inset-0 z-10 flex items-center justify-center rounded-[var(--radius-md)] bg-background-card/70 px-4">
<p className="text-sm text-text-secondary text-center">{chartLockMessage}</p>
</div>
<div className="pointer-events-none opacity-40">{chart}</div>
</div>
) : chartLockMessage ? (
<p className="text-sm text-text-secondary">{chartLockMessage}</p>
) : null}
</div>
<p className="text-sm text-text-muted">{t('noDetails')}</p>
) : null}
</div>
);

View File

@@ -32,7 +32,7 @@ import { prosthesisCatalogApi } from '@/lib/api/prosthesis-catalog';
import { treatmentsApi } from '@/lib/api/treatments';
import { notificationsApi } from '@/lib/api/notifications';
import { pickAutoAppointment } from '@/components/shared/treatmentSelection';
import type { DayStripItem } from '@/components/treatment/dayStrip';
import { unscheduledStripColorCode, type DayStripItem } from '@/components/treatment/dayStrip';
import {
areDetailsPersistable,
defaultTreatmentTypeForAppointment,
@@ -603,8 +603,8 @@ export function TreatmentWorkspace({
setStandaloneTreatments((prev) => {
const current = prev.find((row) => row.id === selectedStandaloneId);
if (!current) return prev;
const prevColor = current.details.find((d) => d.treatmentType?.trim())?.treatmentType?.trim() ?? '';
const nextColor = nextDetails.find((d) => d.treatmentType?.trim())?.treatmentType?.trim() ?? '';
const prevColor = unscheduledStripColorCode(current.details);
const nextColor = unscheduledStripColorCode(nextDetails);
if (
prevColor === nextColor &&
areUnscheduledDetailsStripDeletable(current.details) ===
@@ -635,8 +635,7 @@ export function TreatmentWorkspace({
const sourceDetails = tr.id === selectedStandaloneId && !draftHydratingRef.current
? details
: tr.details;
const typedDetail = sourceDetails.find((d) => Boolean(d.treatmentType?.trim()));
const colorCode = typedDetail?.treatmentType?.trim() ?? '';
const colorCode = unscheduledStripColorCode(sourceDetails);
return {
kind: 'unscheduled' as const,
id: tr.id,
@@ -1887,7 +1886,9 @@ export function TreatmentWorkspace({
if (idx < 0) return;
const target = details[idx];
if (!target || isDetailLocked(target)) return;
if (!window.confirm(t('confirmRemoveDetail'))) return;
if (!window.confirm(
details.length <= 1 ? t('confirmRemoveLastDetail') : t('confirmRemoveDetail'),
)) return;
const nextDetails = details.filter((d) => d.clientId !== detailClientId);
const nextActive =
@@ -2342,8 +2343,8 @@ export function TreatmentWorkspace({
canEdit={canEdit}
saveStatus={saveStatus}
uploadBusy={uploadBusyDetailId === activeDetailId}
onAddDetail={(initialType) => {
const next = newDetail(initialType);
onAddDetail={() => {
const next = newDetail();
setDetails((prev) => [...prev, next]);
setActiveDetailId(next.clientId);
setEntryStep('treatment');