improvement: badge & card components colour pallete updated so their text become more readable in light mode.

This commit is contained in:
2026-05-08 13:56:33 +03:30
parent f314c116ec
commit dcc4b10f00
8 changed files with 231 additions and 55 deletions

View File

@@ -3,7 +3,10 @@
import { Trash2 } from 'lucide-react';
import type { AppointmentColumnProvider, AppointmentRecord } from '@/types/appointment';
import { formatHourLabel } from '@/lib/appointmentTime';
import { purposeDeleteIconClass, purposeStyle } from '@/components/ui/appointments/appointmentPurposeStyles';
import {
purposeDeleteIconClass,
purposeStyle,
} from '@/components/ui/appointments/appointmentPurposeStyles';
const HOUR_PX = 40;
const HOURS = Array.from({ length: 24 }, (_, i) => i);
@@ -133,7 +136,7 @@ export function AppointmentScheduleGrid({
{canDelete && onDeleteAppointment && (
<button
type="button"
className="group pointer-events-auto shrink-0 self-center z-20 mr-2 ml-1 inline-flex cursor-pointer items-center justify-center rounded-[var(--radius-sm)] border-0 bg-transparent p-1 outline-none focus-visible:ring-2 focus-visible:ring-primary/35"
className="group pointer-events-auto shrink-0 self-center z-20 mr-0.5 ml-0.5 inline-flex cursor-pointer items-center justify-center rounded-[var(--radius-sm)] bg-transparent p-1 outline-none focus-visible:ring-2 focus-visible:ring-primary/35"
aria-label="Delete appointment"
title="Delete appointment"
onClick={(e) => {

View File

@@ -10,11 +10,12 @@ export const APPOINTMENT_PURPOSE_LABEL: Record<AppointmentPurpose, string> = {
/** Background + border for blocks / legend (matches reference palette). */
export const APPOINTMENT_PURPOSE_STYLES: Record<AppointmentPurpose, string> = {
consultation: 'bg-violet-500/25 border-violet-400/50 text-violet-100',
filling: 'bg-orange-500/25 border-orange-400/50 text-orange-100',
endo: 'bg-red-500/25 border-red-400/50 text-red-100',
visit: 'bg-sky-500/25 border-sky-400/50 text-sky-100',
hygiene: 'bg-lime-500/20 border-lime-400/45 text-lime-100',
consultation:
'bg-purpose-consultation-bg border-purpose-consultation-border text-purpose-consultation-fg',
filling: 'bg-purpose-filling-bg border-purpose-filling-border text-purpose-filling-fg',
endo: 'bg-purpose-endo-bg border-purpose-endo-border text-purpose-endo-fg',
visit: 'bg-purpose-visit-bg border-purpose-visit-border text-purpose-visit-fg',
hygiene: 'bg-purpose-hygiene-bg border-purpose-hygiene-border text-purpose-hygiene-fg',
};
export function purposeStyle(purpose: string): string {
@@ -26,13 +27,13 @@ export function purposeStyle(purpose: string): string {
export function purposeDeleteIconClass(purpose: string): string {
const p = purpose as AppointmentPurpose;
const map: Record<AppointmentPurpose, string> = {
consultation: '!text-violet-400 group-hover:!text-violet-300',
filling: '!text-orange-400 group-hover:!text-orange-300',
endo: '!text-red-400 group-hover:!text-red-300',
visit: '!text-sky-400 group-hover:!text-sky-300',
hygiene: '!text-lime-600 group-hover:!text-lime-500',
consultation: '!text-purpose-consultation-fg',
filling: '!text-purpose-filling-fg',
endo: '!text-purpose-endo-fg',
visit: '!text-purpose-visit-fg',
hygiene: '!text-purpose-hygiene-fg',
};
return map[p] ?? '!text-text-muted group-hover:!text-text-secondary';
return map[p] ?? '!text-text-muted';
}
/** Small swatch for legend (background + border only). */

View File

@@ -14,10 +14,10 @@ interface BadgeProps {
}
const variantStyles: Record<BadgeVariant, string> = {
success: 'bg-emerald-900/30 text-emerald-300 border-emerald-700/60',
warning: 'bg-amber-900/30 text-amber-300 border-amber-700/60',
danger: 'bg-red-950/30 text-red-300 border-red-700/60',
default: 'bg-background-secondary text-text-secondary border-border',
success: 'bg-badge-success-bg text-badge-success-fg border-badge-success-border',
warning: 'bg-badge-warning-bg text-badge-warning-fg border-badge-warning-border',
danger: 'bg-badge-danger-bg text-badge-danger-fg border-badge-danger-border',
default: 'bg-badge-default-bg text-badge-default-fg border-badge-default-border',
};
/** Explicit width + height so every row matches; flex centers label optically. */

View File

@@ -0,0 +1,36 @@
import type { ElementType, ReactNode } from 'react';
type CardPadding = 'none' | 'sm' | 'md' | 'lg';
type CardProps<T extends ElementType = 'div'> = {
children: ReactNode;
padding?: CardPadding;
as?: T;
className?: string;
} & Omit<React.ComponentPropsWithoutRef<T>, 'as' | 'children' | 'className'>;
const paddingClassMap: Record<CardPadding, string> = {
none: '',
sm: 'p-3',
md: 'p-4',
lg: 'p-6',
};
export function Card<T extends ElementType = 'div'>({
children,
as,
className = '',
padding = 'md',
...props
}: CardProps<T>) {
const Component = as ?? 'div';
return (
<Component
className={`rounded-[var(--radius-lg)] border border-card-border bg-card text-card-foreground ${paddingClassMap[padding]} ${className}`}
{...props}
>
{children}
</Component>
);
}

View File

@@ -2,6 +2,7 @@
import { CalendarDays } from 'lucide-react';
import { purposeStyle } from '@/components/ui/appointments/appointmentPurposeStyles';
import { Card } from '@/components/ui/common/Card';
import { ScheduleDayPicker } from '@/components/ui/common/ScheduleDayPicker';
import { startOfLocalDay } from '@/lib/appointmentTime';
import type { TreatmentAppointment } from '@/types/treatment';
@@ -32,7 +33,7 @@ export function AppointmentsStrip({
}: AppointmentsStripProps) {
if (stripHidden) {
return (
<div className="surface-card p-3 flex items-center justify-between gap-3 flex-wrap">
<Card className="flex items-center justify-between gap-3 flex-wrap" padding="sm">
<p className="text-sm text-text-secondary">Appointments are hidden.</p>
<button
type="button"
@@ -41,12 +42,12 @@ export function AppointmentsStrip({
>
Show appointments
</button>
</div>
</Card>
);
}
return (
<div className="surface-card p-4 space-y-4">
<Card className="space-y-4">
<div className="flex items-center justify-between gap-3 flex-wrap">
<div className="flex items-center gap-2 min-w-0">
<CalendarDays className="w-5 h-5 text-text-muted shrink-0 icon-flat" aria-hidden />
@@ -89,12 +90,14 @@ export function AppointmentsStrip({
})}`;
const palette = purposeStyle(a.purpose);
return (
<button
<Card
as="button"
key={a.id}
type="button"
onClick={() => onSelectAppointment(a.id)}
padding="none"
className={`
text-left rounded-[var(--radius-sm)] border px-3 py-2 min-w-[200px] max-w-[280px] transition-shadow min-h-[52px]
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'}
@@ -105,10 +108,10 @@ export function AppointmentsStrip({
{a.patientFirstName} {a.patientLastName}
</p>
<p className="text-[11px] opacity-90 capitalize mt-0.5">{a.purpose}</p>
</button>
</Card>
);
})}
</div>
</div>
</Card>
);
}