Shitty gadgets removed. Some useful gadgets added.
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useRouter } from '@/i18n/navigation';
|
||||
import { AppointmentsStrip } from '@/components/ui/treatment/AppointmentsStrip';
|
||||
import { FdiToothChart } from '@/components/ui/treatment/FdiToothChart';
|
||||
import { LabCasesDispatchPanel } from '@/components/ui/treatment/LabCasesDispatchPanel';
|
||||
@@ -254,10 +255,16 @@ function detailsToPreviewTreatment(
|
||||
interface TreatmentWorkspaceProps {
|
||||
userId: string;
|
||||
currentOrganization: Organization | null;
|
||||
initialAppointmentId?: string | null;
|
||||
}
|
||||
|
||||
export function TreatmentWorkspace({ userId, currentOrganization }: TreatmentWorkspaceProps) {
|
||||
export function TreatmentWorkspace({
|
||||
userId,
|
||||
currentOrganization,
|
||||
initialAppointmentId = null,
|
||||
}: TreatmentWorkspaceProps) {
|
||||
const t = useTranslations('treatment');
|
||||
const router = useRouter();
|
||||
const { showError, showSuccess, messages: toastMessages } = useToast();
|
||||
const canView = canViewTreatment(currentOrganization);
|
||||
const canEdit = canEditTreatment(currentOrganization);
|
||||
@@ -308,6 +315,15 @@ export function TreatmentWorkspace({ userId, currentOrganization }: TreatmentWor
|
||||
const labCaseDraftsRef = useRef(labCaseDrafts);
|
||||
labCaseDraftsRef.current = labCaseDrafts;
|
||||
const skipNextGetDraftRef = useRef(false);
|
||||
const pendingAppointmentIdRef = useRef<string | null>(initialAppointmentId);
|
||||
|
||||
useEffect(() => {
|
||||
pendingAppointmentIdRef.current = initialAppointmentId;
|
||||
if (initialAppointmentId) {
|
||||
setSelectedDay(startOfLocalDay(new Date()));
|
||||
setSelectionLocked(false);
|
||||
}
|
||||
}, [initialAppointmentId]);
|
||||
|
||||
const [sendBusyId, setSendBusyId] = useState<string | null>(null);
|
||||
const [uploadBusyDetailId, setUploadBusyDetailId] = useState<string | null>(null);
|
||||
@@ -464,7 +480,16 @@ export function TreatmentWorkspace({ userId, currentOrganization }: TreatmentWor
|
||||
.map(mapAppointment);
|
||||
setAppointments(list);
|
||||
if (!selectionLockedRef.current) {
|
||||
setSelectedAppointmentId(pickAutoAppointment(list, selectedDay));
|
||||
const pendingId = pendingAppointmentIdRef.current;
|
||||
if (pendingId && list.some((appointment) => appointment.id === pendingId)) {
|
||||
setSelectedAppointmentId(pendingId);
|
||||
setSelectionLocked(true);
|
||||
pendingAppointmentIdRef.current = null;
|
||||
router.replace('/treatment', { scroll: false });
|
||||
} else {
|
||||
pendingAppointmentIdRef.current = null;
|
||||
setSelectedAppointmentId(pickAutoAppointment(list, selectedDay));
|
||||
}
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
if (!cancelled) {
|
||||
@@ -477,7 +502,7 @@ export function TreatmentWorkspace({ userId, currentOrganization }: TreatmentWor
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [userId, selectedDay, showError, t]);
|
||||
}, [userId, selectedDay, showError, t, router]);
|
||||
|
||||
useEffect(() => {
|
||||
const today = startOfLocalDay(new Date());
|
||||
|
||||
83
frontend/src/components/ui/treatment/catalog-type-colors.ts
Normal file
83
frontend/src/components/ui/treatment/catalog-type-colors.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Shared pastel palette for treatment types, prosthesis types, and dashboard charts.
|
||||
* Treatment types own the canonical hex values; prosthesis types reuse the same codes.
|
||||
*/
|
||||
|
||||
export const TREATMENT_TYPE_COLORS: Record<string, string> = {
|
||||
restoration: '#fed7aa',
|
||||
specialized_restoration: '#fdba74',
|
||||
radiography: '#cbd5e1',
|
||||
endo: '#fecaca',
|
||||
surgery: '#fca5a5',
|
||||
prosthesis: '#c4b5fd',
|
||||
implant: '#a5b4fc',
|
||||
orthodontics: '#93c5fd',
|
||||
perio: '#86efac',
|
||||
pediatrics: '#fde68a',
|
||||
extraction: '#f9a8d4',
|
||||
clinic_visit: '#bae6fd',
|
||||
continue_treatment: '#99f6e4',
|
||||
};
|
||||
|
||||
/** Prosthesis codes mapped to treatment-palette hex values (mapping is arbitrary). */
|
||||
export const PROSTHESIS_TYPE_COLORS: Record<string, string> = {
|
||||
pfm_crown: '#cbd5e1',
|
||||
pfz_crown: '#86efac',
|
||||
monolithic_zirconia: '#99f6e4',
|
||||
glass_ceramic_crown: '#fde68a',
|
||||
full_metal_crown: '#cbd5e1',
|
||||
temporary_resin_crown: '#bae6fd',
|
||||
pmma: '#93c5fd',
|
||||
peek_crown: '#99f6e4',
|
||||
veneer_zirconia: '#86efac',
|
||||
veneer_ips_press: '#fed7aa',
|
||||
veneer_ips_cad: '#fdba74',
|
||||
soft_structure: '#ddd6fe',
|
||||
customized_abutment: '#a5b4fc',
|
||||
prefabricated_abutment: '#93c5fd',
|
||||
ti_base_abutment: '#bae6fd',
|
||||
multi_unit_abutment: '#a5b4fc',
|
||||
zirconia_abutment: '#86efac',
|
||||
screw_retained: '#c4b5fd',
|
||||
zirconia_overlay: '#99f6e4',
|
||||
ips_overlay: '#fde68a',
|
||||
smile_design: '#f9a8d4',
|
||||
mockup: '#fbcfe8',
|
||||
};
|
||||
|
||||
export const CATALOG_FALLBACK_COLORS = [
|
||||
'#ddd6fe',
|
||||
'#fed7aa',
|
||||
'#fecaca',
|
||||
'#bae6fd',
|
||||
'#d9f99d',
|
||||
'#fbcfe8',
|
||||
] as const;
|
||||
|
||||
/** Ordered palette for charts and rotating unknown catalog codes. */
|
||||
export const CATALOG_PALETTE_COLORS: readonly string[] = [
|
||||
'#fed7aa',
|
||||
'#fdba74',
|
||||
'#cbd5e1',
|
||||
'#fecaca',
|
||||
'#fca5a5',
|
||||
'#c4b5fd',
|
||||
'#a5b4fc',
|
||||
'#93c5fd',
|
||||
'#86efac',
|
||||
'#fde68a',
|
||||
'#f9a8d4',
|
||||
'#bae6fd',
|
||||
'#99f6e4',
|
||||
'#ddd6fe',
|
||||
'#d9f99d',
|
||||
'#fbcfe8',
|
||||
];
|
||||
|
||||
export function resolveCatalogTypeColor(
|
||||
code: string,
|
||||
colorMap: Record<string, string>,
|
||||
index = 0,
|
||||
): string {
|
||||
return colorMap[code] ?? CATALOG_FALLBACK_COLORS[index % CATALOG_FALLBACK_COLORS.length];
|
||||
}
|
||||
@@ -1,56 +1,21 @@
|
||||
import type { CSSProperties } from 'react';
|
||||
import {
|
||||
PROSTHESIS_TYPE_COLORS,
|
||||
resolveCatalogTypeColor,
|
||||
} from '@/components/ui/treatment/catalog-type-colors';
|
||||
|
||||
/**
|
||||
* Prosthesis-type colors for lab-facing surfaces (Tasks list, Cases detail group
|
||||
* headers / badges). Grouped by material family, loosely inspired by exocad's
|
||||
* material color conventions:
|
||||
* - Zirconia family → pale green/cream
|
||||
* - PFM / full metal → steel gray
|
||||
* - Glass-ceramic / IPS (press & CAD) → warm amber
|
||||
* - Resin / PMMA / PEEK / temporary → mint/teal
|
||||
* - Abutments / screw-retained → slate blue
|
||||
* - Smile design / mockup → lavender/pink
|
||||
* headers / badges). Uses the same hex palette as treatment types.
|
||||
*
|
||||
* Clinic-facing dispatch flows intentionally do NOT use these colors.
|
||||
*/
|
||||
const PROSTHESIS_TYPE_COLORS: Record<string, string> = {
|
||||
// Zirconia family
|
||||
monolithic_zirconia: '#d9f2e6',
|
||||
pfz_crown: '#c7ede0',
|
||||
veneer_zirconia: '#b8e6d5',
|
||||
zirconia_abutment: '#a7dcc8',
|
||||
zirconia_overlay: '#cdeede',
|
||||
// PFM / metal
|
||||
pfm_crown: '#cbd5e1',
|
||||
full_metal_crown: '#b8c2cf',
|
||||
// Glass-ceramic / IPS
|
||||
glass_ceramic_crown: '#fde3a7',
|
||||
veneer_ips_press: '#fcd88f',
|
||||
veneer_ips_cad: '#f9cf9c',
|
||||
ips_overlay: '#fbe0b0',
|
||||
// Resin / PMMA / PEEK / temporary
|
||||
temporary_resin_crown: '#bfeaf0',
|
||||
pmma: '#a9e2ea',
|
||||
peek_crown: '#b7e4dd',
|
||||
soft_structure: '#d4eef0',
|
||||
// Abutments / screw-retained
|
||||
customized_abutment: '#aec6e8',
|
||||
prefabricated_abutment: '#9db8e0',
|
||||
ti_base_abutment: '#c0d0ec',
|
||||
multi_unit_abutment: '#b4c4e6',
|
||||
screw_retained: '#a8bce2',
|
||||
// Design / mockup
|
||||
smile_design: '#e9d5ff',
|
||||
mockup: '#f5d0fe',
|
||||
};
|
||||
|
||||
const FALLBACK_COLORS = ['#ddd6fe', '#fed7aa', '#fecaca', '#bae6fd', '#d9f99d', '#fbcfe8'];
|
||||
|
||||
/** Dark ink that stays readable on every pastel in the palette. */
|
||||
const BADGE_INK = '#14253d';
|
||||
|
||||
export function prosthesisTypeColor(code: string, index = 0): string {
|
||||
return PROSTHESIS_TYPE_COLORS[code] ?? FALLBACK_COLORS[index % FALLBACK_COLORS.length];
|
||||
return resolveCatalogTypeColor(code, PROSTHESIS_TYPE_COLORS, index);
|
||||
}
|
||||
|
||||
/** Filled swatch (small indicator dots). */
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import type { CSSProperties } from 'react';
|
||||
import type { TreatmentCatalogEntry } from '@/types/treatment-catalog';
|
||||
import {
|
||||
resolveCatalogTypeColor,
|
||||
TREATMENT_TYPE_COLORS,
|
||||
} from '@/components/ui/treatment/catalog-type-colors';
|
||||
|
||||
/**
|
||||
* Single source of truth for treatment-type colors across the app
|
||||
@@ -10,23 +14,6 @@ import type { TreatmentCatalogEntry } from '@/types/treatment-catalog';
|
||||
* conventions), so this is a curated pastel palette. Extend it as new treatment
|
||||
* types are added; unknown codes fall back to a rotating pastel set by index.
|
||||
*/
|
||||
const TREATMENT_TYPE_COLORS: Record<string, string> = {
|
||||
restoration: '#fed7aa',
|
||||
specialized_restoration: '#fdba74',
|
||||
radiography: '#cbd5e1',
|
||||
endo: '#fecaca',
|
||||
surgery: '#fca5a5',
|
||||
prosthesis: '#c4b5fd',
|
||||
implant: '#a5b4fc',
|
||||
orthodontics: '#93c5fd',
|
||||
perio: '#86efac',
|
||||
pediatrics: '#fde68a',
|
||||
extraction: '#f9a8d4',
|
||||
clinic_visit: '#bae6fd',
|
||||
continue_treatment: '#99f6e4',
|
||||
};
|
||||
|
||||
const FALLBACK_COLORS = ['#ddd6fe', '#fed7aa', '#fecaca', '#bae6fd', '#d9f99d', '#fbcfe8'];
|
||||
|
||||
/** Dark ink that stays readable on every pastel in the palette. */
|
||||
const BANNER_INK = '#14253d';
|
||||
@@ -34,7 +21,7 @@ const BANNER_INK = '#14253d';
|
||||
export const DROPDOWN_OPTION_BG = '#14253d';
|
||||
|
||||
export function treatmentTypeColor(code: string, index = 0): string {
|
||||
return TREATMENT_TYPE_COLORS[code] ?? FALLBACK_COLORS[index % FALLBACK_COLORS.length];
|
||||
return resolveCatalogTypeColor(code, TREATMENT_TYPE_COLORS, index);
|
||||
}
|
||||
|
||||
/** Filled swatch (legend dots, small indicators). */
|
||||
|
||||
Reference in New Issue
Block a user