# Availability editor

> Weekly working hours and date overrides that set which slots can be booked.

- Block: [bookings](https://bettersupabase.com/blocks/bookings.md)
- Kind: component
- Data: loaded on the server through the block's client
- Page: https://bettersupabase.com/recipes/availability-editor

## Install the block

```bash
npx better-supabase add bookings
```

## Web (Next.js)

Add the shadcn components:

```bash
npx shadcn add badge card empty skeleton
```

From `apps/examples/nextjs/src/features/bookings/components/availability-editor.tsx` (https://github.com/ScaleDockHQ/better-supabase/blob/main/apps/examples/nextjs/src/features/bookings/components/availability-editor.tsx):

```tsx
export async function AvailabilityEditor() {
  const session = await getSession();

  if (!can(session, "bookings.manage")) {
    forbidden();
  }

  const [t, setup] = await Promise.all([
    getExtracted("bookings"),
    settle(loadBookingSetup),
  ]);

  const page = setup.kind === "ok" ? setup.data.page : null;

  return (
    <Card data-recipe="availability-editor">
      <CardHeader>
        <CardTitle>{t("Weekly hours")}</CardTitle>
        <CardDescription>
          {page
            ? t(
                "Visitors can book 30 minute slots inside these hours, in {tz}.",
                {
                  tz: page.tz,
                },
              )
            : t("The hours visitors can book you in.")}
        </CardDescription>
        {page ? (
          <CardAction className="flex items-center gap-2">
            <Badge variant={page.liveLinks > 0 ? "success-light" : "secondary"}>
              {t(
                "{count, plural, =0 {No live links} one {# live link} other {# live links}}",
                {
                  count: page.liveLinks,
                },
              )}
            </Badge>
            {can(session, "share_links.create") ? <BookingLinkButton /> : null}
          </CardAction>
        ) : null}
      </CardHeader>
      <CardContent>
        {setup.kind === "error" ? (
          <BookingsLoadError title={t("Your booking page didn't load.")} />
        ) : page && setup.kind === "ok" ? (
          <WeeklyHoursForm hours={setup.data.hours} />
        ) : (
          <Empty className="border">
            <EmptyHeader>
              <EmptyMedia variant="icon">
                <CalendarClockIcon />
              </EmptyMedia>
              <EmptyTitle>{t("No booking page yet")}</EmptyTitle>
              <EmptyDescription>
                {setup.kind === "missing"
                  ? t("Pick an organization to set up a booking page.")
                  : t(
                      "Set it up with weekday hours from 09:00 to 17:00, then change them here.",
                    )}
              </EmptyDescription>
            </EmptyHeader>
            {setup.kind === "ok" ? (
              <EmptyContent>
                <SetUpBookingButton />
              </EmptyContent>
            ) : null}
          </Empty>
        )}
      </CardContent>
    </Card>
  );
}
```

## Prompt

Paste this into a coding agent to build the recipe in your app.

### Web

```text
Build "Availability editor" in a React web app that uses shadcn/ui components. Weekly working hours and date overrides that set which slots can be booked.

It uses the better-supabase `bookings` block (Bookings). It loads its data on the server through the block's client.

Steps:

1. If the app doesn't have the block yet, add the SQL with `npx better-supabase add bookings` and apply the migration.
2. Add the shadcn components the recipe uses: `npx shadcn add badge card empty skeleton`.
3. The recipe needs no glue: it calls the block's client on the server.
4. Write the component. Load the data on the server with the block's client, starting from the recipe's snippet (`apps/examples/nextjs/src/features/bookings/components/availability-editor.tsx`). Adapt it to the app's existing components, styling and data loading instead of copying it as is.
5. Check the result against the recipe and the block's docs: it typechecks, it reads the same block data, and it handles the loading and empty states the snippet handles.

Read the full code first:

- Recipe: https://bettersupabase.com/recipes/availability-editor.md
- Block docs: https://bettersupabase.com/docs/blocks/bookings
```
