react-appointment-scheduler
Version:
A production-ready React scheduler component for appointment management with day/week views, drag-and-drop, and TypeScript support
913 lines (828 loc) • 31.1 kB
TypeScript
import { DragEndEvent } from '@dnd-kit/core';
import { DragStartEvent } from '@dnd-kit/core';
import { JSX } from 'react/jsx-runtime';
import { Modifier } from '@dnd-kit/core';
import { NamedExoticComponent } from 'react';
/**
* Adds minutes to a date and returns a new Date
*/
export declare function addMinutes(date: Date, minutes: number): Date;
/**
* Represents a single appointment in the scheduler
*/
export declare interface Appointment {
/** Unique identifier for the appointment */
id: string;
/** Name of the client */
client: Client;
/** Optional list of jobs (multi-service appointment support) */
jobs?: Job[];
/** Type of lash service being performed */
serviceType: ServiceType;
/** Lash artist: string id or object { id, name } (objects supported when data comes from APIs) */
artist?: Artist;
/** Start time of the appointment */
startTime: Date;
/** Duration of the appointment in minutes */
duration: number;
/** Optional notes about the appointment */
notes?: string;
/** Appointment status */
status: AppointmentStatus;
/** Optional phone number for client contact */
phone?: string;
/** Email address for client contact */
email: string;
}
/**
* Memoized AppointmentBlock for performance
*/
export declare const AppointmentBlock: NamedExoticComponent<AppointmentBlockProps>;
/**
* AppointmentBlock Component
*
* Renders a single appointment as a positioned block within the time grid.
*
* Features:
* - Positioned absolutely based on start time and duration
* - Handles overlapping appointments by adjusting width and horizontal position
* - Color-coded by service type
* - Draggable for rescheduling (uses @dnd-kit)
* - Displays client name, service type, and time range
*/
declare interface AppointmentBlockProps {
/** Layout information including position and overlap data */
layout: AppointmentLayout;
/** Callback when the appointment is clicked */
onClick?: (appointment: Appointment) => void;
/** Whether this appointment is currently selected */
isSelected?: boolean;
/** Whether this appointment is being dragged */
isDragging?: boolean;
}
/**
* Layout information for rendering an appointment block
*/
export declare interface AppointmentLayout {
/** The appointment data */
appointment: Appointment;
/** Lane index for overlapping appointments (0-based) */
lane: number;
/** Total number of lanes in this overlap group */
totalLanes: number;
/** Calculated top position in pixels */
top: number;
/** Calculated height in pixels */
height: number;
/** Resolved color for this block (from technician or default) */
color?: string;
}
/** Status options for appointments */
export declare type AppointmentStatus = 'pending' | 'confirmed' | 'cancelled' | 'completed';
/** Artist can be a string id or an object with id and optional name (e.g. from APIs) */
declare type Artist = string | {
id: string;
name?: string;
};
/**
* Calculates layout information for all appointments
* Handles overlap detection and assigns lanes for proper positioning
*
* @param appointments - Array of appointments to layout
* @param gridStartHour - The hour the grid starts at (for calculating top position)
* @returns Array of AppointmentLayout objects with position data
*/
export declare function calculateAppointmentLayouts(appointments: Appointment[], gridStartHour: number): AppointmentLayout[];
/**
* Calculates total grid height in pixels
*/
export declare function calculateGridHeight(startHour: number, endHour: number, slotHeight: number): number;
/**
* Calculates the height in pixels for an appointment based on its duration
*
* @param durationMinutes - Duration in minutes
* @returns Height in pixels
*/
export declare function calculateHeight(durationMinutes: number): number;
/**
* Calculates the top position in pixels for an appointment
* based on its start time relative to the grid's start hour
*
* @param startTime - The appointment's start time
* @param gridStartHour - The hour the grid starts at
* @returns Top position in pixels
*/
export declare function calculateTopPosition(startTime: Date, gridStartHour: number): number;
/**
* Calculates the number of time slots for a given hour range
* Each hour has 2 slots (30-minute intervals)
*/
export declare function calculateTotalSlots(startHour: number, endHour: number): number;
declare type Client = {
name: string;
path: string;
};
export declare const CreateAppointmentModal: NamedExoticComponent<CreateAppointmentModalProps>;
declare interface CreateAppointmentModalProps {
/** Whether the modal is open */
isOpen: boolean;
/** Callback to close the modal */
onClose: () => void;
/** Callback when appointment is created */
onCreate: (appointment: NewAppointmentData) => void;
/** Pre-selected start time (from slot click) */
initialStartTime?: Date | null;
/** Pre-selected end time (from slot click) */
initialEndTime?: Date | null;
/** Pre-selected technician ID (from slot click in day view) */
initialTechnicianId?: string | null;
/** List of available technicians with id and name */
technicians?: Technician[];
/** List of available services with id, name, and category */
services: Service[];
/**
* Map of technician IDs to service IDs they can perform.
* When provided, technician dropdown in the job builder will be
* filtered based on the selected service.
*/
technicianServices?: TechnicianServices;
}
/**
* Creates a Date object from a date and separate hour/minute values
*/
export declare function createDateTime(date: Date, hour: number, minute: number): Date;
/**
* DatePickerModal Component
*
* A modal dialog for selecting dates.
* - In 'single' mode: select a single date
* - In 'range' mode: select a date range (max 7 days by default)
*/
export declare function DatePickerModal({ isOpen, onClose, mode, initialDate, initialEndDate, maxRangeDays, onSelectDate, onSelectRange, }: DatePickerModalProps): JSX.Element | null;
declare interface DatePickerModalProps {
isOpen: boolean;
onClose: () => void;
mode: 'single' | 'range';
initialDate?: Date;
initialEndDate?: Date;
maxRangeDays?: number;
onSelectDate?: (date: Date) => void;
onSelectRange?: (startDate: Date, endDate: Date) => void;
}
/**
* Open/close hours for a single day of the week.
* Used when business hours differ by day (e.g. Mon–Fri 10–19, Sat–Sun 11–18).
*
* @example
* ```ts
* const businessHours: DaySchedule[] = [
* { day: 'monday', open: '10', close: '19' },
* { day: 'saturday', open: '11', close: '18' },
* ];
* ```
*/
declare interface DaySchedule {
/** Day name in lowercase: 'sunday' | 'monday' | ... | 'saturday' */
day: string;
/** Opening hour (0–23) as string, e.g. '10' for 10:00 */
open: string;
/** Closing hour (0–23) as string, e.g. '19' for 19:00 */
close: string;
}
export declare const DayView: NamedExoticComponent<DayViewProps>;
/**
* DayView Component
*
* Displays a single day's schedule with:
* - Technician/artist columns on the x-axis
* - Time of day on the y-axis
* - Each column shows appointments for that technician
* - Droppable zones for cross-technician drag-and-drop
*/
declare interface DayViewProps {
/** The date to display */
date: Date;
/** All appointments (will be filtered to this day) */
appointments: Appointment[];
/** List of technicians with id and name for columns */
technicians: Technician[];
/** Starting hour of the work day */
startHour: number;
/** Ending hour of the work day */
endHour: number;
/** Callback when an appointment is clicked */
onAppointmentClick?: (appointment: Appointment) => void;
/** Callback when an empty slot is clicked (technicianId is passed) */
onSlotClick?: (startTime: Date, endTime: Date, technicianId?: string) => void;
/** Currently selected appointment ID */
selectedAppointmentId?: string | null;
/** ID of appointment being dragged */
draggingAppointmentId?: string | null;
}
/**
* Default color used for a technician when the app does not provide one.
* Uses a neutral slate that works in light and dark themes.
*/
export declare const DEFAULT_TECHNICIAN_COLOR = "#64748b";
/** Detail display modes */
export declare type DetailDisplayMode = 'modal' | 'panel';
export declare const DetailModal: NamedExoticComponent<DetailModalProps>;
/**
* DetailModal Component
*
* A centered modal overlay that displays appointment details with edit capability.
*
* Features:
* - View and Edit modes
* - Backdrop blur effect
* - Click outside to close
* - Escape key to close
* - Focus trap for accessibility
* - Smooth enter/exit animations
*/
declare interface DetailModalProps {
/** The appointment to display (null when closed) */
appointment: Appointment | null;
/** Whether the modal is open */
isOpen: boolean;
/** Callback to close the modal */
onClose: () => void;
/** Callback when appointment is updated */
onUpdate?: (appointment: Appointment) => void;
/** Callback when appointment is deleted */
onDelete?: (id: string) => void;
/** List of available services with id, name, and category */
services: Service[];
/** List of available technicians with id and name */
technicians?: Technician[];
/** Map of technician IDs to service IDs they can perform */
technicianServices?: TechnicianServices;
}
export declare const DetailPanel: NamedExoticComponent<DetailPanelProps>;
/**
* DetailPanel Component
*
* A slide-in side panel that displays appointment details with edit capability.
* Alternative to the modal for users who prefer to keep context visible.
*
* Features:
* - View and Edit modes
* - Slides in from the right
* - Escape key to close
* - Focus trap for accessibility
* - Smooth animations
*/
declare interface DetailPanelProps {
/** The appointment to display (null when closed) */
appointment: Appointment | null;
/** Whether the panel is open */
isOpen: boolean;
/** Callback to close the panel */
onClose: () => void;
/** Callback when appointment is updated */
onUpdate?: (appointment: Appointment) => void;
/** Callback when appointment is deleted */
onDelete?: (id: string) => void;
/** List of available services with id, name, and category */
services: Service[];
/** List of available technicians with id and name */
technicians?: Technician[];
/** Map of technician IDs to service IDs they can perform */
technicianServices?: TechnicianServices;
}
/**
* Gets the end of the day (23:59:59.999) for a given date
*/
export declare function endOfDay(date: Date): Date;
/**
* Filters appointments to only those on a specific day
*
* @param appointments - Array of all appointments
* @param date - The date to filter for
* @returns Appointments that occur on the given day
*/
export declare function filterAppointmentsByDay(appointments: Appointment[], date: Date): Appointment[];
/**
* Filters appointments to only those within working hours
* Adjusts appointments that partially fall outside working hours
*
* @param appointments - Array of appointments
* @param startHour - Working hours start
* @param endHour - Working hours end
* @returns Appointments within working hours
*/
export declare function filterByWorkingHours(appointments: Appointment[], startHour: number, endHour: number): Appointment[];
/**
* Formats a Date to a full date string (e.g., "Monday, January 15, 2024")
*/
export declare function formatFullDate(date: Date): string;
/**
* Formats a Date to a short date string (e.g., "Mon 15")
*/
export declare function formatShortDate(date: Date): string;
/**
* Formats a Date to a 12-hour time string (e.g., "9:00 AM")
*/
export declare function formatTime(date: Date): string;
/**
* Generates time slots for the given hour range
* Creates 30-minute intervals from startHour to endHour
*
* @param date - The base date for the slots
* @param startHour - Starting hour (0-23)
* @param endHour - Ending hour (0-23)
* @returns Array of TimeSlot objects
*/
export declare function generateTimeSlots(date: Date, startHour: number, endHour: number): TimeSlot[];
/**
* Gets a combined class string for an appointment block
*
* @param serviceType - The type of lash service
* @param isDragging - Whether the appointment is being dragged
* @returns Combined CSS class string
*/
export declare function getAppointmentClasses(serviceType: ServiceType, isDragging?: boolean): string;
/**
* Get the current theme from localStorage or system preference
*/
export declare function getCurrentTheme(): Theme;
/**
* Gets estimated duration for a service type (default values)
* Used for reference when creating new appointments
*/
export declare function getDefaultDuration(serviceType: ServiceType): number;
/**
* Gets the color configuration for a service type
*
* @param serviceType - The type of lash service
* @returns ServiceColors object with CSS class names
*/
export declare function getServiceColors(serviceType: ServiceType): ServiceColors;
/**
* Gets the display name for a service type with emoji
*
* @param serviceType - The type of lash service
* @returns Formatted service name
*/
export declare function getServiceDisplayName(serviceType: ServiceType): string;
/**
* Resolves the color for a technician by ID.
* Your app can set technician.color (e.g. hex); if missing, this default is used.
*
* @param technicianId - Technician/artist ID from the appointment
* @param technicians - List of technicians (with optional color)
* @returns CSS color string (hex or same as technician.color)
*/
export declare function getTechnicianColor(technicianId: string | undefined, technicians: Technician[]): string;
/**
* Resolves the technician color for an appointment (uses appointment.artist).
*/
export declare function getTechnicianColorForAppointment(appointment: Appointment, technicians: Technician[]): string;
/**
* Gets today's date at midnight
*/
export declare function getToday(): Date;
/**
* Gets an array of dates for the week containing the given date
* Week starts on Sunday
*
* @param date - Any date in the desired week
* @returns Array of 7 Date objects (Sun-Sat)
*/
export declare function getWeekDates(date: Date): Date[];
/**
* Initialize theme on app load
*/
export declare function initializeTheme(): void;
/**
* Checks if two dates are on the same day
*/
export declare function isSameDay(date1: Date, date2: Date): boolean;
/**
* Checks if a date is today
*/
export declare function isToday(date: Date): boolean;
/**
* Checks if a time is within working hours
*/
export declare function isWithinWorkingHours(time: Date, startHour: number, endHour: number): boolean;
/**
* Represents a single job within an appointment.
* Each job corresponds to one service performed by one technician.
*
* @example
* ```ts
* const jobs: Job[] = [
* { serviceType: 'service-1', technicianId: 'tech-1' },
* { serviceType: 'service-1', technicianId: 'tech-2' },
* { serviceType: 'service-3', technicianId: 'tech-1' },
* ];
* ```
*/
export declare interface Job {
/** The service ID for this job */
serviceType: ServiceType;
/** The technician assigned to perform this job */
technicianId?: string;
}
/**
* Data for creating a new appointment (without ID).
* An appointment can have multiple jobs (services).
*/
export declare interface NewAppointmentData {
client: Client;
/** List of jobs (services) for this appointment */
jobs: Job[];
artist?: string;
/** Status for newly created appointments (defaults to 'pending' when omitted) */
status?: AppointmentStatus;
startTime: Date;
duration: number;
/** Optional email for client contact */
email?: string;
/** Required phone number for client contact */
phone: string;
notes?: string;
}
/**
* Rounds a date to the nearest 30-minute slot
*/
export declare function roundToSlot(date: Date): Date;
export declare function Scheduler({ appointments, technicians: providedTechnicians, services, technicianServices, startHour, endHour, businessHours, view: initialView, selectedDate: initialDate, detailDisplay, onSelectAppointment, onCreateAppointment, onNewAppointment, onUpdateAppointment, onDeleteAppointment, onRescheduleAppointment, }: SchedulerProps): JSX.Element;
/**
* Props for the main Scheduler component
*/
export declare interface SchedulerProps {
/** Array of appointments to display */
appointments: Appointment[];
/** List of technicians/artists with id and name */
technicians?: Technician[];
/** List of available services (Service[] or string[]; strings are normalized to { id, name, category: 'general' }) */
services: Service[] | string[];
/**
* Map of technician IDs to service IDs they can perform.
* When provided, the Create Appointment modal will filter available
* services based on the selected technician.
* If a technician is not in the map, all services will be shown.
*/
technicianServices?: TechnicianServices;
/** Starting hour of the work day (default: 8 for 8 AM). Ignored when businessHours is provided. */
startHour?: number;
/** Ending hour of the work day (default: 21 for 9 PM). Ignored when businessHours is provided. */
endHour?: number;
/**
* Per-day open/close hours. When provided (non-null, non-undefined, non-empty), the grid
* and slots use these hours per day instead of a single startHour/endHour.
* When null, undefined, or an empty array, the whole day uses startHour/endHour.
* Day names must be lowercase ('monday' … 'sunday').
*/
businessHours?: DaySchedule[] | null;
/** Current view mode */
view?: ViewMode;
/** Currently selected/focused date */
selectedDate?: Date;
/** How to display appointment details */
detailDisplay?: DetailDisplayMode;
/** Callback when an appointment is clicked/selected */
onSelectAppointment?: (appointment: Appointment) => void;
/** Callback when an empty slot is clicked to create new appointment (legacy) */
onCreateAppointment?: (startTime: Date, endTime: Date) => void;
/** Callback when a new appointment is created with full data */
onNewAppointment?: (appointmentData: NewAppointmentData) => void;
/** Callback when an appointment is updated */
onUpdateAppointment?: (appointment: Appointment) => void;
/** Callback when an appointment is deleted */
onDeleteAppointment?: (id: string) => void;
/** Callback when an appointment is rescheduled via drag-and-drop */
onRescheduleAppointment?: (id: string, newStartTime: Date) => void;
}
/**
* Represents a service that can be booked.
*
* @example
* ```ts
* const services: Service[] = [
* { id: '1', name: 'Classic Lashes', category: 'lashes' },
* { id: '2', name: 'Regular Pedicure', category: { id: 1, name: 'Nail' } },
* ];
* ```
*/
export declare interface Service {
/** Unique identifier for the service */
id: string;
/** Display name of the service */
name: string;
/** Category for grouping: string (legacy) or category object with id and name */
category: string | ServiceCategory;
/** Optional duration in minutes for this service */
duration?: number;
}
/**
* Color mappings for lash service types
*
* - Classic: Rose/Pink - Soft and elegant
* - Hybrid: Lavender/Violet - Modern and versatile
* - Volume: Peach/Amber - Warm and dramatic
* - Refill: Sage/Emerald - Fresh and natural
*/
export declare const SERVICE_COLORS: Record<ServiceType, ServiceColors>;
/** Category object when provided by API */
declare interface ServiceCategory {
id: number;
name: string;
description?: string | null;
image?: string | null;
updatedAt?: string;
createdAt?: string;
}
/**
* Color utility functions for the scheduler
* Maps service types to consistent color schemes
*/
/**
* Color configuration for each service type
* Uses CSS class names for consistent theming
*/
export declare interface ServiceColors {
/** CSS class name for the service type */
className: string;
/** Badge background color (CSS variable) */
badgeColor: string;
}
/**
* Core types for the Lash Studio Scheduler component
*/
/** Available lash service types (stores service ID) */
export declare type ServiceType = string;
/**
* Set the theme and persist to localStorage
*/
export declare function setTheme(theme: Theme): void;
/** Duration of each slot in minutes */
export declare const SLOT_DURATION = 30;
/** Height of each 30-minute slot in pixels */
export declare const SLOT_HEIGHT = 60;
/**
* Gets the start of the day (midnight) for a given date
*/
export declare function startOfDay(date: Date): Date;
/**
* Represents a technician/artist who can perform services.
*
* @example
* ```ts
* const technicians: Technician[] = [
* { id: 'tech-1', name: 'Sarah Wilson' },
* { id: 'tech-2', name: 'Emily Chen' },
* { id: 'tech-3', name: 'Jessica Rodriguez' },
* ];
* ```
*/
export declare interface Technician {
/** Unique identifier for the technician */
id: string;
/** Display name of the technician */
name: string;
/** Optional color (e.g. hex #rrggbb) for this technician; used for blocks and UI. If omitted, a default is used. */
color?: string;
}
/**
* Map of technician IDs to the service IDs they can perform.
* This allows parent applications to define which technicians are qualified
* for which services.
*
* @example
* ```ts
* const technicianServices: TechnicianServices = {
* 'tech-1': ['1', '2', '3'], // Technician tech-1 can do services with these IDs
* 'tech-2': ['1', '4'], // Technician tech-2 can only do services 1 and 4
* 'tech-3': ['3', '5'], // Technician tech-3 specializes in services 3 and 5
* };
* ```
*/
export declare type TechnicianServices = Record<string, string[]>;
/**
* Theme Utilities
*
* Helper functions for theme management
*/
export declare type Theme = 'light' | 'dark';
export declare function ThemeToggle({ className }: ThemeToggleProps): JSX.Element;
/**
* ThemeToggle Component
*
* A toggle button for switching between light and dark themes.
* Persists the user's preference in localStorage.
*/
export declare interface ThemeToggleProps {
/** Optional className for custom styling */
className?: string;
}
/**
* Memoized TimeColumn to prevent unnecessary re-renders
* The time column rarely changes, so memoization provides good optimization
*/
export declare const TimeColumn: NamedExoticComponent<TimeColumnProps>;
/**
* TimeColumn Component
*
* Renders the sticky time labels column on the left side of the scheduler.
* Displays hour labels and provides visual anchors for the time grid.
*
* Uses position: sticky for smooth horizontal scrolling while keeping
* time labels always visible.
*/
declare interface TimeColumnProps {
/** Array of time slots to display labels for */
slots: TimeSlot[];
/** Height of each slot in pixels */
slotHeight: number;
}
/**
* Memoized TimeGrid component
*/
export declare const TimeGrid: NamedExoticComponent<TimeGridProps>;
/**
* TimeGrid Component
*
* Renders the main time grid with:
* - Sticky time column on the left
* - Time slots as rows (30-minute intervals)
* - Appointment blocks positioned absolutely within the grid
* - Click handlers for empty slots to create new appointments
*
* This is the core layout engine for both DayView and WeekView.
*/
declare interface TimeGridProps {
/** The date this grid represents */
date: Date;
/** All appointments to potentially display */
appointments: Appointment[];
/** Starting hour of the work day */
startHour: number;
/** Ending hour of the work day */
endHour: number;
/** Callback when an appointment is clicked */
onAppointmentClick?: (appointment: Appointment) => void;
/** Callback when an empty slot is clicked */
onSlotClick?: (startTime: Date, endTime: Date) => void;
/** Currently selected appointment ID (for highlighting) */
selectedAppointmentId?: string | null;
/** ID of appointment being dragged (for visual feedback) */
draggingAppointmentId?: string | null;
}
/**
* Time slot for the grid
*/
export declare interface TimeSlot {
/** The time for this slot */
time: Date;
/** Hour component (0-23) */
hour: number;
/** Minute component (0 or 30 for 30-min slots) */
minute: number;
/** Formatted display string (e.g., "9:00 AM") */
label: string;
/** Whether this is the start of an hour (for styling) */
isHourStart: boolean;
}
/**
* Toggle between light and dark theme
*/
export declare function toggleTheme(): Theme;
export declare function useDragDrop({ startHour, endHour, getHoursForDate, onReschedule, }: UseDragDropOptions): UseDragDropReturn;
/**
* Custom hook for managing drag-and-drop state and logic
*
* Provides:
* - Tracking of currently dragged appointment
* - Calculation of new appointment time based on drop position
* - Support for cross-day rescheduling in week view
* - Snap-to-slot modifier for smooth dragging
*/
declare interface UseDragDropOptions {
/** Starting hour of the work day (for bounds checking) */
startHour: number;
/** Ending hour of the work day (for bounds checking) */
endHour: number;
/** When set, bounds are taken from this per-day lookup (e.g. from businessHours) */
getHoursForDate?: (date: Date) => {
startHour: number;
endHour: number;
};
/** Callback when an appointment is successfully rescheduled */
onReschedule?: (appointmentId: string, newStartTime: Date) => void;
}
declare interface UseDragDropReturn {
/** ID of the appointment currently being dragged */
draggingId: string | null;
/** Handler for drag start events */
handleDragStart: (event: DragStartEvent) => void;
/** Handler for drag end events */
handleDragEnd: (event: DragEndEvent) => void;
/** Modifier to snap dragging to 30-minute slots */
snapModifier: Modifier;
}
export declare function useScheduler({ initialView, initialDate, detailDisplay, }?: UseSchedulerOptions): UseSchedulerReturn;
/**
* Main state management hook for the Scheduler component
*
* Manages:
* - Current view mode (day/week)
* - Selected date
* - Selected appointment
* - Detail panel/modal visibility
*/
declare interface UseSchedulerOptions {
/** Initial view mode */
initialView?: ViewMode;
/** Initial selected date */
initialDate?: Date;
/** Detail display mode */
detailDisplay?: DetailDisplayMode;
}
declare interface UseSchedulerReturn {
/** Current view mode */
view: ViewMode;
/** Change the view mode */
setView: (view: ViewMode) => void;
/** Currently selected date */
selectedDate: Date;
/** Change the selected date */
setSelectedDate: (date: Date) => void;
/** Navigate to previous day/week */
goToPrevious: () => void;
/** Navigate to next day/week */
goToNext: () => void;
/** Navigate to today */
goToToday: () => void;
/** Currently selected appointment */
selectedAppointment: Appointment | null;
/** Select an appointment (opens detail view) */
selectAppointment: (appointment: Appointment | null) => void;
/** Whether the detail view is open */
isDetailOpen: boolean;
/** Close the detail view */
closeDetail: () => void;
/** How to display the detail */
detailDisplay: DetailDisplayMode;
}
/** View modes for the scheduler */
export declare type ViewMode = 'day' | 'week';
export declare const ViewToggle: NamedExoticComponent<ViewToggleProps>;
/**
* ViewToggle Component
*
* A toggle button group for switching between Day and Week views.
* Styled to match the minimal, elegant aesthetic of a beauty business.
*/
declare interface ViewToggleProps {
/** Current active view */
view: ViewMode;
/** Callback when view is changed */
onViewChange: (view: ViewMode) => void;
}
export declare const WeekView: NamedExoticComponent<WeekViewProps>;
/**
* WeekView Component
*
* Displays a full week's schedule with:
* - 7 day columns (Sunday to Saturday)
* - Shared time column on the left
* - Each day column shows appointments for that day
* - Day headers with date and "Today" indicator
* - Droppable zones for cross-day drag-and-drop
*
* Layout: Time column is sticky, day columns scroll horizontally if needed
*/
declare interface WeekViewProps {
/** The date used to determine which week to display */
selectedDate: Date;
/** All appointments (will be filtered per day) */
appointments: Appointment[];
/** Starting hour of the work day (union range for time column) */
startHour: number;
/** Ending hour of the work day (union range for time column) */
endHour: number;
/** When set, each day column uses this day's open/close; slots outside are disabled */
getHoursForDate?: (date: Date) => {
startHour: number;
endHour: number;
};
/** Callback when an appointment is clicked */
onAppointmentClick?: (appointment: Appointment) => void;
/** Callback when an empty slot is clicked */
onSlotClick?: (startTime: Date, endTime: Date) => void;
/** Currently selected appointment ID */
selectedAppointmentId?: string | null;
/** ID of appointment being dragged */
draggingAppointmentId?: string | null;
/** Selected date range for highlighting (week view) */
selectedDateRange?: {
start: Date;
end: Date;
} | null;
/** List of technicians (used to resolve block color per technician) */
technicians?: Technician[];
}
export { }