Skip to content
Brok UI

Loading…

No results

Scheduling

Open source

Time and calendar views — a Gantt schedule, a weekly grid and a year overview.

Preview

resources/views/blocks/scheduling-weekly.blade.php Blade
@props([
    'heading' => 'Weekly availability',
    'subheading' => 'A weekly schedule designer — green spans mark open time, striped regions are unavailable.',
    'days' => null,
    'hours' => null,
    'spans' => null,
    'disabled' => null,
])

@php
    // Week-calendar availability designer (abui "Availability Designer" shape):
    // day columns × hour rows, multi-hour availability spans, striped disabled
    // regions and dimmed weekends. CSS-grid placement keeps it RTL-safe and the
    // Stripe uses a full-color alias with an HSL compatibility fallback (spec §18 — no raw hex).
    $days ??= ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
    $hours ??= ['8:00', '9:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00', '17:00', '18:00'];
    $weekend = [0, 6];

    // Availability spans: [dayIndex, startHourIndex, durationHours, startLabel, durationLabel, endLabel].
    $spans ??= [
        ['day' => 1, 'start' => 1, 'duration' => 3, 'from' => '09:00', 'length' => '3h', 'to' => '12:00'],
        ['day' => 3, 'start' => 6, 'duration' => 2, 'from' => '14:00', 'length' => '2h', 'to' => '16:00'],
        ['day' => 5, 'start' => 2, 'duration' => 2, 'from' => '10:00', 'length' => '2h', 'to' => '12:00'],
        ['day' => 2, 'start' => 8, 'duration' => 1, 'from' => '16:00', 'length' => '1h', 'to' => '17:00'],
    ];

    // Disabled regions: [dayIndex, startHourIndex, durationHours]. Weekends are
    // filled below; weekdays get a midday lunch block.
    $disabled ??= [
        ['day' => 1, 'start' => 4, 'duration' => 1],
        ['day' => 2, 'start' => 4, 'duration' => 1],
        ['day' => 3, 'start' => 4, 'duration' => 1],
        ['day' => 4, 'start' => 4, 'duration' => 1],
        ['day' => 5, 'start' => 4, 'duration' => 1],
    ];

    $rowCount = count($hours);
    $gridCols = 'grid-template-columns: minmax(3.5rem, auto) repeat('.count($days).', minmax(0, 1fr)); grid-template-rows: auto repeat('.$rowCount.', minmax(2.75rem, 1fr));';

    // Faint diagonal hatching from the muted-foreground token — inline so it can
    // reference the CSS variable without an arbitrary raw-colour utility.
    $stripe = 'background-image: repeating-linear-gradient(-45deg, color-mix(in oklab, var(--muted-foreground-color, hsl(var(--muted-foreground))) calc(0.18 * 100%), transparent) 0, color-mix(in oklab, var(--muted-foreground-color, hsl(var(--muted-foreground))) calc(0.18 * 100%), transparent) 1px, transparent 1px, transparent 7px);';
@endphp

<x-ui.card {{ $attributes->merge(['class' => 'overflow-hidden']) }}>
    <x-ui.card.header>
        <div class="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
            <div>
                <x-ui.card.title class="text-lg">{{ __($heading) }}</x-ui.card.title>
                @if ($subheading)
                    <x-ui.card.description class="mt-2">{{ __($subheading) }}</x-ui.card.description>
                @endif
            </div>
            <div class="flex flex-wrap items-center gap-2">
                <x-ui.badge variant="outline" class="gap-2">
                    <span class="size-2 rounded-full bg-success" aria-hidden="true"></span>
                    {{ __('Available') }}
                </x-ui.badge>
                <x-ui.badge variant="outline" class="gap-2">
                    <span class="size-2 rounded-[3px] border border-border" style="{{ $stripe }}" aria-hidden="true"></span>
                    {{ __('Unavailable') }}
                </x-ui.badge>
            </div>
        </div>
    </x-ui.card.header>

    <x-ui.card.content>
        <div class="overflow-x-auto">
            <div role="region" aria-label="{{ __($heading) }}" class="relative grid min-w-[44rem] text-sm" style="{{ $gridCols }}">
                {{-- Day header row --}}
                <div class="border-b border-border" style="grid-row: 1; grid-column: 1;"></div>
                @foreach ($days as $d => $day)
                    <div
                        @class([
                            'border-b border-s border-border px-2 py-2 text-center text-xs font-semibold',
                            'text-muted-foreground' => in_array($d, $weekend, true),
                            'text-foreground' => ! in_array($d, $weekend, true),
                        ])
                        style="grid-row: 1; grid-column: {{ $d + 2 }};"
                    >{{ __($day) }}</div>
                @endforeach

                {{-- Time labels + base cells (gridlines + weekend tint) --}}
                @foreach ($hours as $h => $hour)
                    <div class="-mt-2 pe-2 text-end text-xs text-muted-foreground" style="grid-row: {{ $h + 2 }}; grid-column: 1;">{{ $hour }}</div>
                    @foreach ($days as $d => $day)
                        <div
                            @class([
                                'border-s border-t border-border',
                                'bg-muted/40' => in_array($d, $weekend, true),
                            ])
                            style="grid-row: {{ $h + 2 }}; grid-column: {{ $d + 2 }};"
                        ></div>
                    @endforeach
                @endforeach

                {{-- Weekend columns: fully unavailable (striped overlay) --}}
                @foreach ($weekend as $d)
                    <div class="pointer-events-none" style="{{ $stripe }} grid-row: 2 / span {{ $rowCount }}; grid-column: {{ $d + 2 }};" aria-hidden="true"></div>
                @endforeach

                {{-- Disabled weekday blocks --}}
                @foreach ($disabled as $block)
                    @php $bd = (int) $block['day']; $bs = (int) $block['start']; $bn = max(1, (int) $block['duration']); @endphp
                    <div class="pointer-events-none" style="{{ $stripe }} grid-row: {{ $bs + 2 }} / span {{ $bn }}; grid-column: {{ $bd + 2 }};" aria-hidden="true"></div>
                @endforeach

                {{-- Availability spans --}}
                @foreach ($spans as $span)
                    @php
                        $sd = (int) $span['day'];
                        $ss = (int) $span['start'];
                        $sn = max(1, min((int) $span['duration'], $rowCount - $ss));
                    @endphp
                    <div
                        class="z-10 m-0.5 flex flex-col rounded-md border border-success bg-success/15 p-2 shadow-sm"
                        style="grid-row: {{ $ss + 2 }} / span {{ $sn }}; grid-column: {{ $sd + 2 }};"
                    >
                        <div class="flex items-center justify-between gap-2">
                            <span class="text-xs font-semibold text-success">{{ $span['from'] }}</span>
                            <span class="flex size-4 items-center justify-center rounded-sm text-muted-foreground" aria-hidden="true">
                                <svg class="size-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18M6 6l12 12"/></svg>
                            </span>
                        </div>
                        <span class="flex items-center gap-2 text-xs text-muted-foreground">
                            <svg class="size-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>
                            {{ $span['length'] }}
                        </span>
                        <span class="mt-auto text-xs font-medium text-foreground">{{ $span['to'] }}</span>
                    </div>
                @endforeach
            </div>
        </div>
        <p class="mt-4 text-xs text-muted-foreground">{{ __('Striped regions are unavailable — spans cannot be moved into lunch breaks or the weekend.') }}</p>
    </x-ui.card.content>
</x-ui.card>

Installation

terminal
php artisan ui:add blocks/scheduling-weekly

Installs these dependencies automatically:

Registry contract

Install confidence

php artisan ui:add blocks/scheduling-weekly writes only the generated targets below. The CLI validates each file hash before writing and prompts before replacing local changes unless --force is used.

Version
1.0.5
License
open
Stability
stable
Contract
v1
Foundation
≥ 1.0.0
Type Generated target
blade resources/views/blocks/scheduling-weekly.blade.php

Registry dependencies

card badge

Package dependencies

composer: jml/brok:^0.2

Use with AI

A brief for your coding agent: what the block is, the install command, how to render it, its props and the rules. Copy it, or open a prompt about this block in an assistant.

scheduling-weekly.md
# Brok UI block: Availability (`scheduling-weekly`)

A weekly availability designer: day columns and hour rows with multi-hour green availability spans, diagonal-striped unavailable regions and dimmed weekends.

Brok UI is a Laravel Blade registry. `ui:add` copies this block into the app as plain Blade the app owns; it composes installed `<brok:*>` primitives and semantic design tokens.

## Install

```bash
php artisan ui:add blocks/scheduling-weekly
```

## Render it

```blade
<x-blocks.scheduling-weekly />
```

## Props

- `heading` (string, default `Weekly availability`) — Section heading.
- `subheading` (string, default `A weekly schedule designer — green spans mark open time, striped regions are unavailable.`) — Supporting copy under the heading.
- `days` (mixed|null, default `null`) — Weekday column labels; null falls back to Sun-Sat.
- `hours` (mixed|null, default `null`) — Hour row labels; null falls back to an 8am-6pm sample range.
- `spans` (mixed|null, default `null`) — Multi-hour availability spans placed on the grid; null falls back to sample data.
- `disabled` (mixed|null, default `null`) — Striped unavailable regions placed on the grid; null falls back to a sample midday block per weekday.

## Use when

- Letting someone review or design a weekly availability grid of open and unavailable hours across days.
- Wanting a single calendar of day columns and hour rows rather than multiple resources at once.

## Avoid when

- Scheduling multiple rooms or resources at once; use scheduling-gantt instead.

## Rules

- Render the installed block with `<x-blocks.scheduling-weekly />` and pass data through its props; edit the copied file only for structural changes.
- Keep the semantic design tokens (`bg-background`, `text-muted-foreground`); never swap in raw colour utilities.
- Keep the `data-slot` attributes and the logical (start/end) spacing so the markup still mirrors under `dir="rtl"`.

## Links

- Docs: https://brokui.dev/blocks/scheduling-weekly
- Registry JSON (files, props, contract): https://brokui.dev/r/open/blocks/scheduling-weekly.json

Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.

Guidance

Use when

  • Letting someone review or design a weekly availability grid of open and unavailable hours across days.
  • Wanting a single calendar of day columns and hour rows rather than multiple resources at once.

Avoid when

  • Scheduling multiple rooms or resources at once; use scheduling-gantt instead.

Anti-patterns

  • Do not replace semantic props with conflicting utility classes.
  • Do not remove labels, focus styles, or state attributes.

Usage

Render the block as a component, passing data where useful:

resources/views/welcome.blade.php Blade
<x-blocks.scheduling-weekly />

Built from primitives

Composes <brok:*> Semantic tokens Re-themeable RTL-ready

This block composes installed <brok:*> primitives and semantic design tokens only — it does not reimplement any primitive. Re-theme it (light, dark, admin, customer) by editing CSS variables; flip the preview to RTL to confirm it mirrors.

Source

The exact, editable file ui:add writes into your app. The preview above renders this same source — there are no preview-only blocks.

resources/views/blocks/scheduling-weekly.blade.php Blade
@props([
    'heading' => 'Weekly availability',
    'subheading' => 'A weekly schedule designer — green spans mark open time, striped regions are unavailable.',
    'days' => null,
    'hours' => null,
    'spans' => null,
    'disabled' => null,
])

@php
    // Week-calendar availability designer (abui "Availability Designer" shape):
    // day columns × hour rows, multi-hour availability spans, striped disabled
    // regions and dimmed weekends. CSS-grid placement keeps it RTL-safe and the
    // Stripe uses a full-color alias with an HSL compatibility fallback (spec §18 — no raw hex).
    $days ??= ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
    $hours ??= ['8:00', '9:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00', '17:00', '18:00'];
    $weekend = [0, 6];

    // Availability spans: [dayIndex, startHourIndex, durationHours, startLabel, durationLabel, endLabel].
    $spans ??= [
        ['day' => 1, 'start' => 1, 'duration' => 3, 'from' => '09:00', 'length' => '3h', 'to' => '12:00'],
        ['day' => 3, 'start' => 6, 'duration' => 2, 'from' => '14:00', 'length' => '2h', 'to' => '16:00'],
        ['day' => 5, 'start' => 2, 'duration' => 2, 'from' => '10:00', 'length' => '2h', 'to' => '12:00'],
        ['day' => 2, 'start' => 8, 'duration' => 1, 'from' => '16:00', 'length' => '1h', 'to' => '17:00'],
    ];

    // Disabled regions: [dayIndex, startHourIndex, durationHours]. Weekends are
    // filled below; weekdays get a midday lunch block.
    $disabled ??= [
        ['day' => 1, 'start' => 4, 'duration' => 1],
        ['day' => 2, 'start' => 4, 'duration' => 1],
        ['day' => 3, 'start' => 4, 'duration' => 1],
        ['day' => 4, 'start' => 4, 'duration' => 1],
        ['day' => 5, 'start' => 4, 'duration' => 1],
    ];

    $rowCount = count($hours);
    $gridCols = 'grid-template-columns: minmax(3.5rem, auto) repeat('.count($days).', minmax(0, 1fr)); grid-template-rows: auto repeat('.$rowCount.', minmax(2.75rem, 1fr));';

    // Faint diagonal hatching from the muted-foreground token — inline so it can
    // reference the CSS variable without an arbitrary raw-colour utility.
    $stripe = 'background-image: repeating-linear-gradient(-45deg, color-mix(in oklab, var(--muted-foreground-color, hsl(var(--muted-foreground))) calc(0.18 * 100%), transparent) 0, color-mix(in oklab, var(--muted-foreground-color, hsl(var(--muted-foreground))) calc(0.18 * 100%), transparent) 1px, transparent 1px, transparent 7px);';
@endphp

<x-ui.card {{ $attributes->merge(['class' => 'overflow-hidden']) }}>
    <x-ui.card.header>
        <div class="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
            <div>
                <x-ui.card.title class="text-lg">{{ __($heading) }}</x-ui.card.title>
                @if ($subheading)
                    <x-ui.card.description class="mt-2">{{ __($subheading) }}</x-ui.card.description>
                @endif
            </div>
            <div class="flex flex-wrap items-center gap-2">
                <x-ui.badge variant="outline" class="gap-2">
                    <span class="size-2 rounded-full bg-success" aria-hidden="true"></span>
                    {{ __('Available') }}
                </x-ui.badge>
                <x-ui.badge variant="outline" class="gap-2">
                    <span class="size-2 rounded-[3px] border border-border" style="{{ $stripe }}" aria-hidden="true"></span>
                    {{ __('Unavailable') }}
                </x-ui.badge>
            </div>
        </div>
    </x-ui.card.header>

    <x-ui.card.content>
        <div class="overflow-x-auto">
            <div role="region" aria-label="{{ __($heading) }}" class="relative grid min-w-[44rem] text-sm" style="{{ $gridCols }}">
                {{-- Day header row --}}
                <div class="border-b border-border" style="grid-row: 1; grid-column: 1;"></div>
                @foreach ($days as $d => $day)
                    <div
                        @class([
                            'border-b border-s border-border px-2 py-2 text-center text-xs font-semibold',
                            'text-muted-foreground' => in_array($d, $weekend, true),
                            'text-foreground' => ! in_array($d, $weekend, true),
                        ])
                        style="grid-row: 1; grid-column: {{ $d + 2 }};"
                    >{{ __($day) }}</div>
                @endforeach

                {{-- Time labels + base cells (gridlines + weekend tint) --}}
                @foreach ($hours as $h => $hour)
                    <div class="-mt-2 pe-2 text-end text-xs text-muted-foreground" style="grid-row: {{ $h + 2 }}; grid-column: 1;">{{ $hour }}</div>
                    @foreach ($days as $d => $day)
                        <div
                            @class([
                                'border-s border-t border-border',
                                'bg-muted/40' => in_array($d, $weekend, true),
                            ])
                            style="grid-row: {{ $h + 2 }}; grid-column: {{ $d + 2 }};"
                        ></div>
                    @endforeach
                @endforeach

                {{-- Weekend columns: fully unavailable (striped overlay) --}}
                @foreach ($weekend as $d)
                    <div class="pointer-events-none" style="{{ $stripe }} grid-row: 2 / span {{ $rowCount }}; grid-column: {{ $d + 2 }};" aria-hidden="true"></div>
                @endforeach

                {{-- Disabled weekday blocks --}}
                @foreach ($disabled as $block)
                    @php $bd = (int) $block['day']; $bs = (int) $block['start']; $bn = max(1, (int) $block['duration']); @endphp
                    <div class="pointer-events-none" style="{{ $stripe }} grid-row: {{ $bs + 2 }} / span {{ $bn }}; grid-column: {{ $bd + 2 }};" aria-hidden="true"></div>
                @endforeach

                {{-- Availability spans --}}
                @foreach ($spans as $span)
                    @php
                        $sd = (int) $span['day'];
                        $ss = (int) $span['start'];
                        $sn = max(1, min((int) $span['duration'], $rowCount - $ss));
                    @endphp
                    <div
                        class="z-10 m-0.5 flex flex-col rounded-md border border-success bg-success/15 p-2 shadow-sm"
                        style="grid-row: {{ $ss + 2 }} / span {{ $sn }}; grid-column: {{ $sd + 2 }};"
                    >
                        <div class="flex items-center justify-between gap-2">
                            <span class="text-xs font-semibold text-success">{{ $span['from'] }}</span>
                            <span class="flex size-4 items-center justify-center rounded-sm text-muted-foreground" aria-hidden="true">
                                <svg class="size-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18M6 6l12 12"/></svg>
                            </span>
                        </div>
                        <span class="flex items-center gap-2 text-xs text-muted-foreground">
                            <svg class="size-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>
                            {{ $span['length'] }}
                        </span>
                        <span class="mt-auto text-xs font-medium text-foreground">{{ $span['to'] }}</span>
                    </div>
                @endforeach
            </div>
        </div>
        <p class="mt-4 text-xs text-muted-foreground">{{ __('Striped regions are unavailable — spans cannot be moved into lunch breaks or the weekend.') }}</p>
    </x-ui.card.content>
</x-ui.card>

Ownership & lifecycle

Owner, release state, review evidence and adoption for this item.
Owner
Platform UI (@JoshJML)
Current version
1.0.5
Status
Stable
License
open
Deprecation
Not deprecated
Contract
v1
Foundation
≥ 1.0.0