|
|
|
|
@@ -3,12 +3,15 @@
|
|
|
|
|
import { useEffect, useId, useRef, useState } from 'react';
|
|
|
|
|
import { useTranslations } from 'next-intl';
|
|
|
|
|
import { ChevronDown, ChevronLeft, ChevronRight } from 'lucide-react';
|
|
|
|
|
import { addCalendarDays, startOfLocalDay } from '@/components/appointments/appointmentTime';
|
|
|
|
|
import { addCalendarDays, compareLocalDayStart, startOfLocalDay } from '@/components/appointments/appointmentTime';
|
|
|
|
|
import { Checkbox } from '@/components/ui/shared/Checkbox';
|
|
|
|
|
|
|
|
|
|
interface ScheduleDayPickerProps {
|
|
|
|
|
value: Date;
|
|
|
|
|
onChange: (day: Date) => void;
|
|
|
|
|
label?: string;
|
|
|
|
|
/** Show a "Today" toggle that jumps to the current local day when enabled. Default true. */
|
|
|
|
|
showTodayToggle?: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const MONTH_KEYS = [
|
|
|
|
|
@@ -56,13 +59,20 @@ const selectClassName = `
|
|
|
|
|
* Calendar day navigator (arrows + year/month/day panel).
|
|
|
|
|
* Does not restrict past dates — parent pages enforce read-only vs editable for schedule grids/forms.
|
|
|
|
|
*/
|
|
|
|
|
export function ScheduleDayPicker({ value, onChange, label }: ScheduleDayPickerProps) {
|
|
|
|
|
export function ScheduleDayPicker({
|
|
|
|
|
value,
|
|
|
|
|
onChange,
|
|
|
|
|
label,
|
|
|
|
|
showTodayToggle = true,
|
|
|
|
|
}: ScheduleDayPickerProps) {
|
|
|
|
|
const t = useTranslations('schedule');
|
|
|
|
|
const panelId = useId();
|
|
|
|
|
const rootRef = useRef<HTMLDivElement>(null);
|
|
|
|
|
const [panelOpen, setPanelOpen] = useState(false);
|
|
|
|
|
|
|
|
|
|
const normalizedValue = startOfLocalDay(value);
|
|
|
|
|
const today = startOfLocalDay(new Date());
|
|
|
|
|
const isTodaySelected = compareLocalDayStart(normalizedValue, today) === 0;
|
|
|
|
|
const resolvedLabel = label ?? t('defaultLabel');
|
|
|
|
|
|
|
|
|
|
const labelText = normalizedValue.toLocaleDateString(undefined, {
|
|
|
|
|
@@ -111,7 +121,22 @@ export function ScheduleDayPicker({ value, onChange, label }: ScheduleDayPickerP
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div ref={rootRef} className="relative w-full max-w-md">
|
|
|
|
|
<p className="text-sm font-medium text-text-secondary mb-2">{resolvedLabel}</p>
|
|
|
|
|
<div className="mb-2 flex items-center justify-between gap-3">
|
|
|
|
|
<p className="text-sm font-medium text-text-secondary">{resolvedLabel}</p>
|
|
|
|
|
{showTodayToggle ? (
|
|
|
|
|
<Checkbox
|
|
|
|
|
checked={isTodaySelected}
|
|
|
|
|
onChange={(checked) => {
|
|
|
|
|
if (checked) {
|
|
|
|
|
onChange(today);
|
|
|
|
|
setPanelOpen(false);
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
label={t('today')}
|
|
|
|
|
className="shrink-0"
|
|
|
|
|
/>
|
|
|
|
|
) : null}
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center gap-1 rounded-[var(--radius-md)] border border-border bg-background-secondary/90 px-1 py-1 shadow-[inset_0_1px_0_rgba(255,255,255,0.02)]">
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
@@ -128,7 +153,7 @@ export function ScheduleDayPicker({ value, onChange, label }: ScheduleDayPickerP
|
|
|
|
|
aria-expanded={panelOpen}
|
|
|
|
|
aria-controls={panelId}
|
|
|
|
|
aria-haspopup="dialog"
|
|
|
|
|
className="flex flex-1 min-w-0 items-center justify-center gap-1 rounded-[var(--radius-sm)] px-2 py-1.5 text-sm font-medium text-text-primary tabular-nums hover:bg-background-card/80 focus:outline-none focus:ring-2 focus:ring-primary/35"
|
|
|
|
|
className="flex flex-1 min-w-0 items-center justify-center gap-3 rounded-[var(--radius-sm)] px-2 py-1.5 text-sm font-medium text-text-primary tabular-nums hover:bg-background-card/80 focus:outline-none focus:ring-2 focus:ring-primary/35"
|
|
|
|
|
>
|
|
|
|
|
<span className="truncate">{labelText}</span>
|
|
|
|
|
<ChevronDown
|
|
|
|
|
|