Services and hours
A React block (a whole screen) in Beamline's Clinic app.
A practice's services and the hours its practitioners can be booked, one relation seen from both ends. A service reads as values and changes on demand: its name edits in place and online booking switches beside it (refused, with the reason, when nobody gives it); its length is a bar on the hour you drag (the field is the exact twin); its price in minor units (or not priced, never free), who gives it, where and one client or a group are values that each open a short form; its bookable week draws every window the schedules offer it, per practitioner and weekday, packed with back-to-back sessions of its length and what is left over, re-packing as the length moves, in practice time or each practitioner's own. A practitioner carries the services they give and their named schedules: weekly hours with date exceptions, between dates, at a place, for some services, switched off and kept; two schedules on the same hours say so on both, hours in another zone are said in practice time. A service also holds time before and after each session and a start step, said as a sentence under the length (Time around it opens the fields) and drawn on the week as held time. Places are rooms with an address or video with a link. Booking online sets how far ahead clients may book on one runway (hours near, weeks far) and who is approved by hand, each choice saying what happens to a request; a saved change is a new version, requests keep the version they were made under and waiting is never called booked; booking is shared for chosen practitioners and services as the app's own link, button or embed, the audience said first and no link when publishing is off. Every edit is a draft until saved; a refused save keeps it.
Use it for
- Setting up what a practice, studio or salon sells by appointment and when each person can be booked for it: services with length, price, staff and places, and each person's schedules.
- Owners and practice managers checking how many sessions a week a service can actually take before they publish it.
Not for
- Rooms and equipment a service needs (resource-setup).
- One person's weekly hours alone (availability-rule-editor).
- Why a particular start time is closed (availability-layers).
- Booking an appointment (team-calendar).
Anatomy
- header: the title, Services, Practitioners and Places with their counts, Booking online, New service or New place
- list: services (name, length, price or Not priced, Group of N, Online or Desk only, who gives it, or Nobody gives it; Unsaved or Not saved yet) or practitioners (avatar, role, schedules, hours a week)
- service: the name (edits in place) with Clients can book it online beside it (off with the reason when nobody gives it); the length bar on three hours with its ticks, the grip at its end and the minutes field beside it; the time held around a session as a sentence with Time around it (before, after, starts); four values that each open a short form: price (Has a price; not priced, said), who gives it (avatars and names; pressable people inside; Nobody gives it in the warning tone), where, one client or a group of N
- bookable week: a column per weekday (a day with no hours folds to a narrow column that says No hours), the hour axis spanning only the hours offered plus half an hour, a lane per practitioner who gives it; each window a schedule offers is a stack of back-to-back sessions of the service's length with their starts and a hatched remnant for what is left (its minutes written when there is room); each day's total in its head; the week's sessions in one sentence with a rolling count; Practice time or Their own time (a lane in another zone says how far ahead)
- practitioner: avatar, name, role, hours a week; the services they give (each opens its service); every schedule a card, collapsed to its name, Off or Unsaved, place, hours a week and Overlaps <other> when it clashes, with its own week strip marking the clashing hours; one card open: Bookable or Off, From and Until (No end), Place, Services in these hours (only services they give), a warning for no service, each overlap naming the other card (pressing the name opens it) with the day and times, the weekly hours and date exceptions in the screen's time format, the hours again in practice time when the schedule's zone differs; Add schedule
- save bar: Unsaved changes to the item by name, Discard and Save; a refusal's reason in place, every change kept
- held time (in the service): Before and After in minutes, Starts (back to back or every 15, 30 or 60 min), and the sentence "1 h session, 15 min after: the next start is 1 h 15 min later."; on the week each session carries its held time as a dashed box before and after it, and a step gap where a start waits
- places: the list (type icon, name, Room or Video, the address or link, or Not set yet) and the open place: Type as Room or Video cards (icon and word), Name, Address for a room or Joining link for video, and what uses it
- booking online: How far ahead (a sentence, the runway with Now, hours to 3 days, a break, then weeks to 12, the bookable band between two grips, and an example from now); Approved by hand (Everyone, New clients only, Nobody, each with what happens to a request), the version and its date, and the save bar; Share booking (practitioners and services as chips, the audience sentence, Link, Button or Embed, the app's link or code as selectable text with Copy); the requests waiting, each with the version it was made under
States
- draft (a new service: Not saved yet; an edited one: Unsaved, kept while moving to another item)
- unpriced (Not priced, never a price of 0)
- group service (Group of N, its places)
- online disabled (Desk only; online refused with the reason when nobody gives it)
- no eligible staff (Nobody gives it, in the list and the editor; no bookable week)
- online with no times (a warning)
- length held (the bar follows the hand per 5-minute step; every window re-packs: sessions resize and glide to their new starts, new ones grow in, dropped ones fade, the count rolls; a save bar not yet showing waits for the release, so the week never reflows under the hand)
- day with no hours (a narrow column, No hours written out)
- leftover (a hatched remnant at the window's end; N min left written from 25 minutes)
- times in practice time or their own (stored hours never move)
- schedule off (kept, said, its hours disabled)
- recurring hours, date exception (in the shared editor)
- overlap (both cards carry Overlaps <other> and mark the clashing hours on their strips; the open card names the other with the day and times, and the name opens it)
- one time format on the screen (timeFormat: 24h by default, 12h; the schedule editor follows it)
- schedule with no service (warning)
- no schedules (empty state with Add schedule)
- saving (Save shows progress)
- save refused (the reason, the draft kept)
- narrow room: the list and the open item take turns with Back; the week as words per practitioner
- held time before and after, start step (said and drawn)
- a place switched between room and video (the other type's words kept until saved, dropped on save)
- policy edited (Saving makes version N; waiting requests stay waiting, said when approval changes)
- policy saved (Version N, since the day)
- requests waiting (made under version N, never called booked)
- share scoped (the audience sentence before the link)
- share as button or embed (the app's code)
- publishing off (no link, said)
- no link from the app for a choice (said, nothing invented)
- copy refused (the link already selected; the copy button says it failed)
Keyboard
- Tab through the header, the list and the open item
- a list row: Enter opens it
- the length grip is a slider: ←/→ 5 minutes, Shift 15, Home and End the limits, said with the week's sessions; the minutes field is its exact twin
- a person in Who gives it: Enter or Space toggles them (pressed state)
- Practice time / Their own time: arrows
- the save bar's Discard and Save are buttons in the tab order
- the runway's two grips are sliders: ←/→ an hour under 3 days and a day beyond, Shift a bigger step, said in words
- Type, Approved by hand: arrows choose a card
- share chips: Enter or Space toggles; the link field selects on focus
Motion
The length bar follows the hand per step while held and eases to a key's step in the fast duration. On every step the week re-packs on the shared flip: sessions resize and glide to their new starts, new ones grow in, dropped ones fade where they stood, the remnant's box follows (its words sit outside the flip so they never stretch) and the week's count rolls. The save bar arrives with the motion tokens' fade and rise (after the release when a drag starts from a saved service), and the week's axis and folded days glide to the room left. Selections in the list and the segmented control glide. Reduced motion places at once. Nothing runs at rest.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
practitioners (required) | Practitioner[] | — | |
places | Place[] | — | Where services happen: rooms with an address, video rooms with a link. |
currency (required) | string | — | ISO 4217 code prices are in ("GBP"). |
timezone (required) | string | — | The practice's IANA zone: the week shows in it unless each practitioner's own is chosen. |
services | Service[] | — | The practice's services. |
defaultServices | Service[] | — | |
onServicesChange | ((services: Service[]) => void) | — | The whole list after a save succeeds. |
onServiceChange | ((service: Service, previous: Service | null) => void | Promise<unknown>) | — | Saves one service (`previous` null: a new one). Return a promise to show progress; reject with the reason to keep the draft and say why. |
schedules | Schedule[] | — | Every practitioner's schedules. |
defaultSchedules | Schedule[] | — | |
onSchedulesChange | ((schedules: Schedule[]) => void) | — | |
onScheduleChange | ((schedule: Schedule, previous: Schedule | null) => void | Promise<unknown>) | — | Saves one schedule (`previous` null: a new one); reject with the reason to keep the draft. |
weekOf | string | — | A day of the week the bookable week shows (schedules in effect then count). Default: today. |
view | "booking" | "places" | "practitioners" | "services" | — | |
defaultView | "booking" | "places" | "practitioners" | "services" | — | |
onViewChange | ((view: BookingSetupView) => void) | — | |
defaultService | string | — | The service open beside the list. Default: the first. |
defaultPractitioner | string | — | The practitioner open beside the list. Default: the first. |
className | string | — | Classes on the documented visual root; no override by default. |
style | CSSProperties | — | Inline styles on the documented visual root; no overrides by default. |
ref | Ref<HTMLDivElement> | — | Receives the documented native target or imperative handle; omitted by default. |
timeFormat | "12h" | "24h" | — | How times of day are written, here and in the schedules' editor. Default "24h". |
defaultPlaces | Place[] | — | |
onPlacesChange | ((places: Place[]) => void) | — | |
onPlaceChange | ((place: Place, previous: Place | null) => void | Promise<unknown>) | — | Saves one place (`previous` null: a new one); it keeps only its own type's field. Reject with the reason to keep the draft. |
policy | BookingPolicy | — | How clients may book online (how far ahead, who is approved by hand), the saved version. |
defaultPolicy | BookingPolicy | — | |
onPolicyChange | ((policy: BookingPolicy) => void) | — | |
onPolicySave | ((policy: BookingPolicy, previous: BookingPolicy) => void | Promise<unknown>) | — | Saves the next version of the policy; reject with the reason to keep the edit. |
pendingRequests | PendingRequest[] | — | Requests waiting for the desk, each with the version it was made under. |
published | boolean | — | Online booking is published. When false (the default) no link is shown. |
bookingLink | ((scope: ShareScope) => string | null) | — | The app's own link (or button or embed code) for a share; nothing is invented when it is missing. |
now | Date | — | The moment "how far ahead" is read from. Default: now. |
defaultPlace | string | — | The place open beside the list. Default: the first. |
Built from
- value-grip
- availability-rule-editor
- segmented-control
- number-field
- multi-select
- switch
- checkbox
- date-picker
- select
- input
- avatar
- avatar-group
- button
- empty-state
- radio-cards
- chip-group
- copy-button
- alert
Dependencies
lucide-react
Import
import { BookingSetup } from "@beamline/booking-setup";
Get it
Services and hours is part of Beamline UI. Install the parts you need in your React app as compiled packages or editable source. Get Beamline · Connect your agent
More in Clinic
- Appointment book: The desk's day with the appointment editor beside it. The appointment being edited is a block on the day like any other, drawn in the accent while it is…
- Book an appointment: A client books an appointment on the practice's own site: a service (length and price beside it), who gives it or anyone free, and where; a day with…
- Patient overview: One patient's record on a page. A search finds patients by name, chart number or birth date (recent patients when empty; same names told apart by birth…
- Patient chart: A clinic's chart for one client and their animals. The client's header (phone, email, contact preferences, the balance with what it means and as of when…
- Health questionnaire: A patient's medical history as a versioned questionnaire. Every question takes Yes, No or I don't know, and one not answered says so, never read as No; a…
- Case workspace: A patient's clinical cases and how cases work at the practice. A case shows its status, type, teeth, staff, location and start; its checklist counts…
- Care plan: A client's care plan composed beside what the client will see of it. Every field says who sees it (the care team only, or the client too) and the…
- Treatment plan: A patient's treatment plan in named phases. Each procedure shows its tooth, status and fee beside the patient's and the insurer's estimate (not known…
- Plan templates: Reusable treatment-plan templates. A template reads as words at rest and changes on demand: its name edits in place; the status its procedures start as…
- Plan acceptance: A treatment plan presented for acceptance, from both sides. The practice chooses the agreement (the account's default or this location's own) and how…
- Plan follow-up: The practice's follow-up lists, for the person who turns accepted treatment into booked chairs. Treatment plans lead with what's at stake: the value…
- Session notes and tasks: A session's note beside its follow-up tasks, each task tied to the exact words it came from: choose a task and its words light in the note or the…
- Note templates: A practitioner's note templates in one library with three ways in. My templates lists their own by kind, favourites first, each with when it was last…
- Probing chart: A periodontal probing chart. Six sites per tooth (three buccal, three lingual) across both arches as you face the patient, each depth in its cell and…
- Procedure picker: Procedures, conditions and bundles put on a patient's teeth from the chair. The mode (existing, planned, completed) is chosen before adding and each…
- Results notebook: A notebook entry's structured results. The entry's text above a bounded results table with typed columns: a sample link, numbers in their unit with their…
- Insurance claims: Insurance claims as a queue: every claim at first, in the order a person works them (rejected by the clearinghouse, denied by the payer, partly paid, not…
- Account ledger: A patient's account. All transactions or the ones needing attention, each dated with its type in a word, its amount and the running balance; entries that…
- Dispensing: Dispensing from the shelf by lot. A product is found with its stock and prices (consumables left out unless asked); the quantity is drawn across its…
- Record sharing: Records sent out of a clinic. Several visits' records and their documents (record, invoice, plan, lab result, image) are chosen separately, with how much…
- Client journal: A client's journal from both ends. The client's side is their home, built around their day: today's goals with how many are done and a mark each, done…