# Scheduling

> Bookings with a request and confirm workflow, custom fields, share links and reminders before the start.

Source: https://bettersupabase.com/docs/presets/scheduling

A booking is requested, then confirmed or cancelled. Custom fields hold what
each organization asks for (a room, a phone number), a share link lets the
guest open the booking, and reminders go out before it starts.

## Table [#table]

```sql title="supabase/schemas/bookings.sql"
create table public.bookings (
  id uuid primary key default gen_random_uuid(),
  organization_id uuid not null,
  title text not null,
  status text not null default 'requested',
  starts_at timestamptz not null
);
alter table public.bookings enable row level security;
```

## Config [#config]

The `scheduling` preset brings the SaaS blocks plus calendars, bookings and
reminders. This app tracks its own `bookings` table, so it adds custom
fields, share links and a state machine for the booking status:

```ts title="better-supabase.config.ts"
import { defineConfig } from "better-supabase/config";

export default defineConfig({
  preset: "scheduling",
  subjects: {
    booking: {
      table: "public.bookings",
      idType: "uuid",
      title: "title",
      url: "/bookings/{id}",
    },
  },
  blocks: {
    "custom-fields": {},
    "share-links": {},
    "state-machine": {
      options: {
        machines: {
          booking: {
            table: "public.bookings",
            column: "status",
            initial: "requested",
            tenant: "organization_id",
            transitions: {
              requested: ["confirmed", "cancelled"],
              confirmed: ["cancelled"],
              cancelled: [],
            },
          },
        },
      },
    },
  },
});
```

## Event flow [#event-flow]

| The user               | Call                                                                   | Event                | Watchers get            |
| ---------------------- | ---------------------------------------------------------------------- | -------------------- | ----------------------- |
| Confirms a booking     | `stateMachine.transition("booking", id, "confirmed")`                  | `status.changed`     | `subject.moved`         |
| Sets a field           | `customFields.setValue(organizationId, "booking", id, fieldId, value)` | `field_value.set`    | `subject.field_changed` |
| Shares it with a guest | `shareLinks.create(organizationId, "booking", id, "viewer")`           | `share_link.created` | `subject.shared`        |
| Is reminded            | `reminders.deliver(payload)`                                           | `reminder.sent`      | `reminder.due`          |

A cancelled booking stays cancelled: `cancelled` has no outgoing
transitions, so `transition` returns `STATE_TRANSITION_INVALID`.

This recipe has no overlap check. For resources, weekly hours, open slots
and an exclusion constraint against double bookings, use the
[bookings block](/docs/blocks/bookings).

## With ReUI [#with-reui]

* Event Calendar for the booking calendar, from `starts_at`.
* Data Grid for the booking list, with Filters on a `defineListQuery`.
* Stepper for the request and confirm workflow on the booking page.

Pro scheduling blocks are licensed per user; copy them from
[reui.io](https://reui.io).