Skip to main content
ScheduleField ← Back to Table of Contents

Summary

Weekly availability / opening-hours editor with per-day toggles, from/to time slots, optional break entries, copy-to-weekdays, searchable timezone selector, and locked days. Time pickers use the compact FlexTimeSegmentsField dropdown (hour + minute columns). Default control size is sm; the timezone selector always renders at md.

Basic usage

Filament resource example:

State format

Day keys use three-letter English abbreviations: mon, tue, wed, thu, fri, sat, sun. Times are HH:MM strings in 24-hour format (09:00, 17:30). On save, invalid slot entries are dropped and times are zero-padded.

Minimal example

Split shift with lunch break

Overnight slots

Add "overnight": true when a work slot crosses midnight (e.g. 22:00 → 06:00). The UI exposes an Overnight toggle on work slots; overlap validation uses the same ScheduleV2 / interval-engine rules as PHP.
When timezone(null) hides the selector, omit the timezone key from state — it is not required or validated.

Export & audit helpers


Default state

The field default calls ScheduleField::defaultSchedule():
Static helper signature:
Examples:

Configuration API

All methods accept Closure for dynamic configuration.

days(array|Closure $days)

Which days to render. Invalid day codes are ignored. Empty array falls back to all seven days. Default: ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'].

timezone(string|Closure|null $timezone)

Default timezone identifier when the selector is shown, or null to hide the timezone block entirely. Default: 'UTC' (selector visible).
When shown, the selector uses the shared TimezoneField UI (search, UTC offset badge). It always renders at md size regardless of the schedule field size().

timeStep(int|Closure $minutes)

Minute step for FlexTimeSegmentsField dropdowns (From / To). Clamped to 1–60. Default: 5.

minSlots(int|Closure $count) / maxSlots(int|Closure $count)

Per-day slot limits (includes both work slots and breaks). Defaults: 1 / 10.

requireSlotsForEnabledDays(bool|Closure $condition = true)

When true (default), each enabled day must have at least minSlots() valid entries. When false, an enabled day may have zero slots (useful for “open but hours TBD” flows — use carefully).

allowCopyToWeekdays(bool|Closure $condition = true)

Shows Copy to weekdays on the copy source day. Default: true.

copySourceDay(string|Closure $day = 'mon')

Which day row displays the copy button. Must be one of mon … sun. Default: mon.

workdays(array|Closure $days)

Target days for Copy to weekdays. Invalid entries are filtered. Empty after filter falls back to Mon–Fri. Default: ['mon', 'tue', 'wed', 'thu', 'fri'].
Copy replaces slots on target workdays with a clone of the source day’s slots. It does not change enabled flags.

lockedDays(array|Closure $days)

Days that cannot be toggled on/off. Shows a lock icon instead of the switch. Locked days still display their schedule when enabled in state. Only days present in days() are kept.

variant(string|Closure $variant)

Visual container style.

size(string|ControlSize|Closure $size)

Size of time inputs and day rows. Default: sm. Timezone selector remains md.

readOnly(bool|Closure $condition = true) / disabled(bool|Closure $condition = true)

Inherited from Filament. Disables toggles, time pickers, copy, and slot toolbar actions.

focusOutline(bool|Closure $condition = true)

Inherited from HasFieldFocusOutline. Default: false. Adds focus ring on time input shells when enabled.

Public helper methods


Validation

Built-in rule runs on submit (and when Livewire validates). Custom Filament required is mapped to nullable at rule level — emptiness is checked inside the schedule validator when required() is set. Real-time UI validation mirrors server messages via Alpine (from_before_to, min_slots, max_slots, overlap). Disabled days skip slot validation entirely.

Model & persistence

Loading existing state:
Reading normalized state in PHP:

Recipe: restaurant — weekdays + lunch break

Users can add breaks in the UI via Add break.

Recipe: support desk — weekdays only, no weekends in UI

Recipe: always-closed weekends (locked)

Recipe: no timezone selector (local hours)

Recipe: 30-minute steps, larger time inputs

Recipe: read-only preview on view page


UI behaviour


CSS classes

Timezone block reuses TimezoneField classes (fff-timezone-field, teleported menu classes).

Assets

Lazy-loaded bundles (FlexFieldAssets::stylesheetsFor('schedule-field')):
  • flex-text-input
  • switch
  • teleported-menu
  • timezone-field
  • flex-time-segments
Alpine components:
  • schedule-field (main coordinator)
  • flex-time-segments (preloaded per slot — shared chunk)
Uses wire:ignore on the field root. After deploy, run:

Playground

Slug: schedule-field /admin/flex-fields-playground/schedule-field — see Playground.

Implementation notes

  • Normalization: ScheduleNormalizer — pads times, drops invalid slots, coerces type to slot or break.
  • Validation: ScheduleValidator — overlap detection sorts slots by start time.
  • Day constants: ScheduleDays::ALL, ScheduleDays::WEEKDAYS.
  • Click-outside on the field closes open time menus and the timezone dropdown.
  • Mobile layout reflows day header (copy button moves below label row).