improvement: all clinic side ui components related to treatment types updated based on the new real world data.
This commit is contained in:
@@ -2,11 +2,13 @@
|
||||
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { CalendarDays } from 'lucide-react';
|
||||
import { purposeStyle } from '@/components/ui/appointments/appointmentPurposeStyles';
|
||||
import { Card } from '@/components/ui/shared/Card';
|
||||
import { ScheduleDayPicker } from '@/components/ui/shared/ScheduleDayPicker';
|
||||
import { startOfLocalDay } from '@/components/appointments/appointmentTime';
|
||||
import { treatmentTypeLabelFromCatalog } from '@/components/ui/treatment/treatmentTypeDisplay';
|
||||
import {
|
||||
treatmentTypeBannerStyle,
|
||||
treatmentTypeLabelFromCatalog,
|
||||
} from '@/components/ui/treatment/treatmentTypeDisplay';
|
||||
import type { TreatmentCatalogEntry } from '@/types/treatment-catalog';
|
||||
import type { TreatmentAppointment } from '@/types/treatment';
|
||||
|
||||
@@ -91,8 +93,8 @@ export function AppointmentsStrip({
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
})}`;
|
||||
const palette = purposeStyle(a.purpose);
|
||||
const purposeLabel = treatmentTypeLabelFromCatalog(a.purpose, treatmentCatalog);
|
||||
const purposeIndex = treatmentCatalog.findIndex((e) => e.code === a.purpose);
|
||||
return (
|
||||
<Card
|
||||
as="button"
|
||||
@@ -100,10 +102,10 @@ export function AppointmentsStrip({
|
||||
type="button"
|
||||
onClick={() => onSelectAppointment(a.id)}
|
||||
padding="none"
|
||||
style={treatmentTypeBannerStyle(a.purpose, purposeIndex < 0 ? 0 : purposeIndex)}
|
||||
className={`
|
||||
text-left rounded-[var(--radius-sm)] px-3 py-2 min-w-[200px] max-w-[280px] transition-shadow min-h-[52px]
|
||||
focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/45
|
||||
${palette}
|
||||
${sel ? 'ring-2 ring-primary ring-offset-2 ring-offset-background-secondary shadow-[inset_0_1px_0_rgba(255,255,255,0.06)]' : 'hover:brightness-110'}
|
||||
`}
|
||||
>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
'use client';
|
||||
|
||||
import { purposeStyle } from '@/components/ui/appointments/appointmentPurposeStyles';
|
||||
import { formatCodeAsLabel } from '@/components/ui/treatment/treatmentTypeDisplay';
|
||||
import {
|
||||
formatCodeAsLabel,
|
||||
treatmentTypeBannerStyle,
|
||||
} from '@/components/ui/treatment/treatmentTypeDisplay';
|
||||
|
||||
interface TreatmentTypeBadgeProps {
|
||||
type: string;
|
||||
@@ -14,7 +16,8 @@ export function TreatmentTypeBadge({ type, label, className = '' }: TreatmentTyp
|
||||
|
||||
return (
|
||||
<span
|
||||
className={`inline-flex items-center justify-center box-border rounded-md border min-h-[1.75rem] px-2.5 py-1 text-xs font-medium leading-none shrink-0 ${purposeStyle(type)} ${className}`.trim()}
|
||||
className={`inline-flex items-center justify-center box-border rounded-md border min-h-[1.75rem] px-2.5 py-1 text-xs font-medium leading-none shrink-0 ${className}`.trim()}
|
||||
style={treatmentTypeBannerStyle(type)}
|
||||
>
|
||||
{display}
|
||||
</span>
|
||||
|
||||
@@ -242,6 +242,10 @@ export function TreatmentWorkspace({ userId, currentOrganization }: TreatmentWor
|
||||
const [orgs, setOrgs] = useState<LinkedOrganizationOption[]>([]);
|
||||
const [labDependentCodes, setLabDependentCodes] = useState<Set<string>>(new Set());
|
||||
const [treatmentCatalog, setTreatmentCatalog] = useState<TreatmentCatalogEntry[]>([]);
|
||||
const treatmentDropdownCatalog = useMemo(
|
||||
() => treatmentCatalog.filter((entry) => entry.availableInTreatment),
|
||||
[treatmentCatalog],
|
||||
);
|
||||
|
||||
const [details, setDetails] = useState<TreatmentDetailDraft[]>(() => [newDetail()]);
|
||||
const [labCaseDrafts, setLabCaseDrafts] = useState<LabCaseDraft[]>([]);
|
||||
@@ -984,7 +988,7 @@ export function TreatmentWorkspace({ userId, currentOrganization }: TreatmentWor
|
||||
onDetailsChange={setDetails}
|
||||
isDetailLocked={isDetailLocked}
|
||||
labDependentCodes={labDependentCodes}
|
||||
treatmentCatalog={treatmentCatalog}
|
||||
treatmentCatalog={treatmentDropdownCatalog}
|
||||
disabled={!canEditTreatmentForDay}
|
||||
canEdit={canEdit}
|
||||
saveStatus={saveStatus}
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
import type { CSSProperties } from 'react';
|
||||
import type { TreatmentCatalogEntry } from '@/types/treatment-catalog';
|
||||
|
||||
/**
|
||||
* Single source of truth for treatment-type colors across the app
|
||||
* (treatment detail dropdown, appointment booking dropdown, appointment legend,
|
||||
* appointment schedule banners, and the treatment feature's appointment cards).
|
||||
*
|
||||
* There is no universal dental type→color standard (only status-based blue/red
|
||||
* 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: '#e2e8f0',
|
||||
radiography: '#cbd5e1',
|
||||
endo: '#fecaca',
|
||||
surgery: '#fca5a5',
|
||||
prosthesis: '#c4b5fd',
|
||||
@@ -11,16 +21,43 @@ const TREATMENT_TYPE_COLORS: Record<string, string> = {
|
||||
orthodontics: '#93c5fd',
|
||||
perio: '#86efac',
|
||||
pediatrics: '#fde68a',
|
||||
extraction: '#f87171',
|
||||
extraction: '#f9a8d4',
|
||||
clinic_visit: '#bae6fd',
|
||||
continue_treatment: '#99f6e4',
|
||||
};
|
||||
|
||||
const FALLBACK_COLORS = ['#ddd6fe', '#fed7aa', '#fecaca', '#bae6fd', '#d9f99d'];
|
||||
const FALLBACK_COLORS = ['#ddd6fe', '#fed7aa', '#fecaca', '#bae6fd', '#d9f99d', '#fbcfe8'];
|
||||
|
||||
/** Dark ink that stays readable on every pastel in the palette. */
|
||||
const BANNER_INK = '#14253d';
|
||||
/** Dark background used behind pastel option text in native <select> dropdowns. */
|
||||
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];
|
||||
}
|
||||
|
||||
/** Filled swatch (legend dots, small indicators). */
|
||||
export function treatmentTypeSwatchStyle(code: string, index = 0): CSSProperties {
|
||||
const color = treatmentTypeColor(code, index);
|
||||
return { backgroundColor: color, borderColor: 'rgba(0, 0, 0, 0.18)' };
|
||||
}
|
||||
|
||||
/** Colored banner / card fill with readable dark text (schedule blocks, appointment cards). */
|
||||
export function treatmentTypeBannerStyle(code: string, index = 0): CSSProperties {
|
||||
const color = treatmentTypeColor(code, index);
|
||||
return {
|
||||
backgroundColor: color,
|
||||
borderColor: 'rgba(0, 0, 0, 0.16)',
|
||||
color: BANNER_INK,
|
||||
};
|
||||
}
|
||||
|
||||
/** Pastel option text on the dark dropdown background. */
|
||||
export function treatmentTypeOptionStyle(code: string, index = 0): CSSProperties {
|
||||
return { color: treatmentTypeColor(code, index), backgroundColor: DROPDOWN_OPTION_BG };
|
||||
}
|
||||
|
||||
export function treatmentTypeLabelFromCatalog(
|
||||
code: string,
|
||||
catalog: TreatmentCatalogEntry[],
|
||||
|
||||
Reference in New Issue
Block a user