# Availability editor

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

Source: https://bettersupabase.com/docs/registry/ui-recipes/availability-editor

{/* Generated by scripts/build-recipes.ts from the example apps. Edit the examples or the script, then run `pnpm recipes`. */}

It loads its data on the server through the block's client; the [bookings block](https://bettersupabase.com/blocks/bookings) must be installed (`npx better-supabase add bookings`). Its page on bettersupabase.com is [/recipes/availability-editor](https://bettersupabase.com/recipes/availability-editor).

Web, with shadcn components:

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

```tsx title="apps/examples/nextjs/src/features/bookings/components/availability-editor.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>
  );
}
```