improvement: attachment selection for lab dispatch added. attachment preview added to cases feature.
This commit is contained in:
@@ -9,7 +9,7 @@ import { PastTreatmentsPanel } from '@/components/ui/treatment/PastTreatmentsPan
|
||||
import { TreatmentDetailsEditor } from '@/components/ui/treatment/TreatmentDetailsEditor';
|
||||
import { TreatmentPreviewCard } from '@/components/ui/treatment/TreatmentPreviewCard';
|
||||
import { ToastStack } from '@/components/ui/shared/Toast';
|
||||
import { treatmentTypeLabelFromCatalog } from '@/components/ui/treatment/treatmentTypeDisplay';
|
||||
import { treatmentTypeLabelFromCatalog, treatmentTypeColor } from '@/components/ui/treatment/treatmentTypeDisplay';
|
||||
import {
|
||||
addCalendarDays,
|
||||
compareLocalDayStart,
|
||||
@@ -133,6 +133,7 @@ function newLabCaseDraft(): LabCaseDraft {
|
||||
destinationOrganizationId: null,
|
||||
detailClientIds: [],
|
||||
toothProsthesis: [],
|
||||
attachmentIds: [],
|
||||
sentAt: null,
|
||||
sends: [],
|
||||
};
|
||||
@@ -180,6 +181,7 @@ function mapLabCaseDraftFromApi(lc: PastLabCase): LabCaseDraft {
|
||||
tooth: tp.tooth,
|
||||
prosthesisTypeCode: tp.prosthesisTypeCode,
|
||||
})),
|
||||
attachmentIds: (lc.attachments ?? []).map((a) => a.id),
|
||||
sentAt: lc.sentAt ?? null,
|
||||
sends: lc.sends ?? [],
|
||||
};
|
||||
@@ -296,6 +298,7 @@ export function TreatmentWorkspace({ userId, currentOrganization }: TreatmentWor
|
||||
const [uploadBusyDetailId, setUploadBusyDetailId] = useState<string | null>(null);
|
||||
const [organizationSearch, setOrganizationSearch] = useState('');
|
||||
const [recentOrganizationIds, setRecentOrganizationIds] = useState<string[]>([]);
|
||||
const [showWholeTreatmentPlan, setShowWholeTreatmentPlan] = useState(false);
|
||||
|
||||
const isDetailLocked = useCallback(
|
||||
(detail: TreatmentDetailDraft) =>
|
||||
@@ -388,6 +391,35 @@ export function TreatmentWorkspace({ userId, currentOrganization }: TreatmentWor
|
||||
|
||||
const selectedTeethSet = useMemo(() => new Set(activeDetail?.teeth ?? []), [activeDetail?.teeth]);
|
||||
|
||||
const wholePlanTeethSet = useMemo(() => {
|
||||
const set = new Set<FdiToothId>();
|
||||
for (const detail of details) {
|
||||
for (const tooth of detail.teeth) set.add(tooth);
|
||||
}
|
||||
return set;
|
||||
}, [details]);
|
||||
|
||||
const wholePlanToothColors = useMemo(() => {
|
||||
const colors: Partial<Record<FdiToothId, string>> = {};
|
||||
for (let i = 0; i < details.length; i++) {
|
||||
const detail = details[i];
|
||||
const catalogIndex = treatmentCatalog.findIndex((e) => e.code === detail.treatmentType);
|
||||
const color = treatmentTypeColor(detail.treatmentType, catalogIndex >= 0 ? catalogIndex : i);
|
||||
for (const tooth of detail.teeth) {
|
||||
if (!(tooth in colors)) colors[tooth] = color;
|
||||
}
|
||||
}
|
||||
return colors;
|
||||
}, [details, treatmentCatalog]);
|
||||
|
||||
const chartSelectedTeeth = showWholeTreatmentPlan ? wholePlanTeethSet : selectedTeethSet;
|
||||
const chartToothColors = showWholeTreatmentPlan ? wholePlanToothColors : undefined;
|
||||
|
||||
// Reset whole-plan overview when switching details.
|
||||
useEffect(() => {
|
||||
setShowWholeTreatmentPlan(false);
|
||||
}, [activeDetailId]);
|
||||
|
||||
// Sync active lab shipment when the selected treatment detail changes.
|
||||
useEffect(() => {
|
||||
const match = labCaseDrafts.find((lc) => lc.detailClientIds.includes(activeDetailId));
|
||||
@@ -817,6 +849,7 @@ export function TreatmentWorkspace({ userId, currentOrganization }: TreatmentWor
|
||||
};
|
||||
})
|
||||
.filter((row): row is { treatmentDetailId: string; tooth: string; prosthesisTypeCode: string } => row !== null),
|
||||
attachmentIds: lc.attachmentIds,
|
||||
}));
|
||||
|
||||
if (payload.length === 0) {
|
||||
@@ -1040,9 +1073,24 @@ export function TreatmentWorkspace({ userId, currentOrganization }: TreatmentWor
|
||||
|
||||
<div className="space-y-3 min-w-0 w-full">
|
||||
<FdiToothChart
|
||||
selected={selectedTeethSet}
|
||||
selected={chartSelectedTeeth}
|
||||
toothColors={chartToothColors}
|
||||
readOnly={showWholeTreatmentPlan}
|
||||
headerControl={
|
||||
details.length > 1 ? (
|
||||
<label className="flex items-center gap-2 text-[11px] text-text-muted cursor-pointer select-none">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={showWholeTreatmentPlan}
|
||||
onChange={(e) => setShowWholeTreatmentPlan(e.target.checked)}
|
||||
className="rounded border-border"
|
||||
/>
|
||||
{t('toothChartWholePlan')}
|
||||
</label>
|
||||
) : undefined
|
||||
}
|
||||
onToggle={(fdi) => {
|
||||
if (!canEditTreatmentForDay || isDetailLocked(activeDetail)) return;
|
||||
if (!canEditTreatmentForDay || isDetailLocked(activeDetail) || showWholeTreatmentPlan) return;
|
||||
setDetails((prev) =>
|
||||
prev.map((d) => {
|
||||
if (d.clientId !== activeDetailId) return d;
|
||||
|
||||
Reference in New Issue
Block a user