Resource Pages
Authenticated resource workflows — searchable and selectable index, create, edit, and detail pages with activity history, plus the operator set: an alert-led index, a waiting-for work queue, a stepped record and one create/edit form with save blockers.
Preview
{{--
Admin Form — create and edit as one page with two starting points.
Creating a record and correcting one are the same set of decisions with
a different starting point, so they share this page rather than each
growing a field set that drifts. `mode="create"` renders it empty;
`mode="edit"` renders it on an existing record.
The composition follows the order the decisions are actually made: what
kind of thing this is (which decides which fields exist AT ALL), what it
does, who it applies to, what limits it, and when it runs. Each of those
is one `admin/record-section`; the whole form reads top to bottom as a
sentence. The end-side rail carries the consequence beside the work: a
live summary of what will be written, then what still blocks saving,
then the terminal actions — which never disappear, only disable with the
reason beside them (`admin/save-blockers`, which also docks itself to the
viewport bottom under `lg`, where the rail would otherwise sit a full
page-scroll below the field being typed).
Readiness is stated twice on purpose — as a pill in the page header and
in the rail — because on a long form one of the two is always off screen.
Server-first: every field is a real named control inside one POST form,
validation errors come back through the shared `$errors` bag into
`<x-ui.field>`, and the blockers list is whatever the host computes.
A Livewire or Alpine host can bind the same props live.
Generalised from the Noord-C admin's `discounts/form/page.blade.php`,
`customers/form/page.blade.php` and `bundles/form/page.blade.php`.
Left out as domain logic: the discount rule builder, the customer
address lookup, the bundle component picker.
--}}
@props([
'mode' => 'create',
'title' => null,
'subtitle' => null,
'action' => '#',
'method' => 'POST',
'cancelHref' => '#',
'recordHref' => '#',
'duplicatedFrom' => null,
'values' => null,
'types' => null,
'blockers' => null,
'summary' => null,
])
@php
// Shapes documented in item.json knowledge.props:
// duplicatedFrom: ['title', 'href'] — where a copy came from, and what was not copied, said before the operator goes looking.
// values: the record's current field values, keyed by input name.
// types: [['value', 'label', 'hint']] — the "what kind of thing" choice that decides which fields exist.
// blockers: the save-blockers `blockers` array; [] means the form is ready.
// summary: the fact-list `facts` array for the live summary card.
$isEdit = $mode === 'edit';
$title ??= $isEdit ? __('Edit discount') : __('New discount');
$subtitle ??= $isEdit
? __('Changes apply to new orders only; orders that already used this code keep what they were given.')
: __('A discount is a rule the checkout applies. Decide what kind it is first — that decides which fields exist.');
$types ??= [
['value' => 'percentage', 'label' => __('Percentage off'), 'hint' => __('A share of the line price.')],
['value' => 'fixed', 'label' => __('Fixed amount off'), 'hint' => __('A sum off the order.')],
['value' => 'shipping', 'label' => __('Free shipping'), 'hint' => __('Waives the shipping fee.')],
];
$values ??= $isEdit ? [
'title' => 'Spring welcome',
'type' => 'percentage',
'code' => 'SPRING10',
'value' => '10',
'audience' => 'everyone',
'minimum' => '25.00',
'uses' => '500',
'starts_at' => '2026-09-01',
'ends_at' => '2026-10-31',
'active' => true,
] : [
'title' => '',
'type' => 'percentage',
'code' => '',
'value' => '',
'audience' => 'everyone',
'minimum' => '',
'uses' => '',
'starts_at' => '',
'ends_at' => '',
'active' => false,
];
$blockers ??= $isEdit ? [] : [
['field' => 'title', 'text' => __('Give the discount a name.'), 'target' => 'title'],
['field' => 'code', 'text' => __('Set the code customers will type.'), 'target' => 'code'],
['field' => 'value', 'text' => __('Set how much comes off.'), 'target' => 'value'],
];
$ready = $blockers === [];
$summary ??= [
['label' => __('Rule'), 'value' => $isEdit ? __('10% off orders over €25.00') : __('Not decided yet')],
['label' => __('Audience'), 'value' => __('Everyone')],
['label' => __('Ceiling'), 'value' => $isEdit ? __('500 uses') : __('No limit'), 'tone' => $isEdit ? null : 'attention'],
['label' => __('Window'), 'value' => $isEdit ? '1 Sep – 31 Oct 2026' : __('Open-ended')],
];
$banners = $duplicatedFrom
? [[
'tone' => 'info',
'title' => __('Duplicated from :name', ['name' => $duplicatedFrom['title'] ?? '']),
'body' => __('Everything was copied except the code, which has to be unique.'),
'actionLabel' => $duplicatedFrom['title'] ?? __('Open original'),
'actionHref' => $duplicatedFrom['href'] ?? '#',
]]
: [];
$trail = array_values(array_filter([
['label' => __('Discounts'), 'href' => '#'],
$isEdit ? ['label' => $values['title'] ?? '', 'href' => $recordHref] : null,
['label' => $title],
]));
@endphp
<main
id="main-content"
data-slot="admin-form-page"
data-surface="admin"
data-mode="{{ $isEdit ? 'edit' : 'create' }}"
{{ $attributes->merge(['class' => 'min-h-screen bg-background text-foreground']) }}
>
<x-ui.admin.page-bar :title="$title" :trail="$trail" :record="[]" :nav-toggle="false" />
<div class="mx-auto flex max-w-screen-2xl flex-col gap-6 px-4 py-6 pb-24 sm:px-6 lg:pb-6">
<x-ui.admin.page-header :title="$title" :scope="$subtitle">
<x-slot:actions>
<x-ui.badge :tone="$ready ? 'positive' : 'attention'" size="status">
{{ $ready ? __('Ready to save') : trans_choice(':count thing missing|:count things missing', count($blockers), ['count' => count($blockers)]) }}
</x-ui.badge>
@if ($isEdit)
<x-ui.button variant="ghost" size="sm" :href="$recordHref">{{ __('Back to record') }}</x-ui.button>
@endif
</x-slot:actions>
</x-ui.admin.page-header>
<x-ui.admin.attention-banners :banners="$banners" />
<x-ui.form :action="$action" :method="$method" class="grid items-start gap-6 !space-y-0 lg:grid-cols-[minmax(0,1fr)_20rem]">
<div class="flex min-w-0 flex-col gap-6">
<x-ui.admin.record-section id="section-kind" :title="__('What kind')" :hint="__('Decides which of the fields below exist. Locked once the discount has been used.')">
<x-ui.field name="title" :label="__('Name')" :hint="__('Internal. Customers see the code, not the name.')" required>
<x-ui.input id="title" :value="$values['title'] ?? ''" autocomplete="off" />
</x-ui.field>
<fieldset class="mt-6">
<legend class="text-sm font-medium">{{ __('Type') }}</legend>
<div class="mt-2 grid gap-2 sm:grid-cols-3">
@foreach ($types as $type)
<x-ui.choicebox
type="radio"
name="type"
:value="$type['value']"
:title="$type['label']"
:description="$type['hint'] ?? null"
:checked="($values['type'] ?? null) === $type['value']"
/>
@endforeach
</div>
</fieldset>
</x-ui.admin.record-section>
<x-ui.admin.record-section id="section-value" :title="__('What the customer gets')" :hint="__('The code and the amount. A percentage over 50% is legitimate, but it is chosen on purpose.')">
<div class="grid gap-6 sm:grid-cols-2">
<x-ui.field name="code" :label="__('Code')" :hint="__('Upper-case letters and digits, 4–16 characters.')" required>
<x-ui.input id="code" :value="$values['code'] ?? ''" autocomplete="off" spellcheck="false" class="font-mono uppercase" />
</x-ui.field>
<x-ui.field name="value" :label="__('Value')" :hint="__('Percentage or amount, depending on the type.')" required>
<x-ui.input id="value" type="number" inputmode="decimal" min="0" step="0.01" :value="$values['value'] ?? ''" />
</x-ui.field>
</div>
</x-ui.admin.record-section>
<x-ui.admin.record-section id="section-audience" :title="__('Who may use it')" :hint="__('Everyone, or a segment. A minimum order keeps a code from being applied to a single low-value line.')">
<div class="grid gap-6 sm:grid-cols-2">
<x-ui.field name="audience" :label="__('Audience')">
<x-ui.select id="audience">
<option value="everyone" @selected(($values['audience'] ?? '') === 'everyone')>{{ __('Everyone') }}</option>
<option value="new" @selected(($values['audience'] ?? '') === 'new')>{{ __('First-time customers') }}</option>
<option value="segment" @selected(($values['audience'] ?? '') === 'segment')>{{ __('A segment…') }}</option>
</x-ui.select>
</x-ui.field>
<x-ui.field name="minimum" :label="__('Minimum order')" :hint="__('Leave empty for no minimum.')">
<x-ui.input id="minimum" type="number" inputmode="decimal" min="0" step="0.01" :value="$values['minimum'] ?? ''" />
</x-ui.field>
</div>
</x-ui.admin.record-section>
<x-ui.admin.record-section id="section-limits" :title="__('What limits it')" :hint="__('Without a ceiling a campaign can run away. State it here as a figure, before it runs.')">
<div class="grid gap-6 sm:grid-cols-2">
<x-ui.field name="uses" :label="__('Total uses')" :hint="__('Across all customers.')">
<x-ui.input id="uses" type="number" inputmode="numeric" min="0" step="1" :value="$values['uses'] ?? ''" />
</x-ui.field>
<x-ui.field name="notes" :label="__('Internal note')" :hint="__('Why this discount exists. Shown on the record, never to customers.')">
<x-ui.textarea id="notes" rows="3"></x-ui.textarea>
</x-ui.field>
</div>
</x-ui.admin.record-section>
<x-ui.admin.record-section id="section-window" :title="__('When it runs')" :hint="__('Time moves the window on its own; the switch is a decision a person makes. They are kept apart so a paused campaign is never confused with an expired one.')">
<div class="grid gap-6 sm:grid-cols-2">
<x-ui.field name="starts_at" :label="__('Starts')">
<x-ui.input id="starts_at" type="date" :value="$values['starts_at'] ?? ''" />
</x-ui.field>
<x-ui.field name="ends_at" :label="__('Ends')" :hint="__('Leave empty for an open-ended window.')">
<x-ui.input id="ends_at" type="date" :value="$values['ends_at'] ?? ''" />
</x-ui.field>
</div>
<div class="mt-6">
<x-ui.switch name="active" id="active" :label="__('Switched on')" :checked="(bool) ($values['active'] ?? false)" />
</div>
</x-ui.admin.record-section>
</div>
<aside class="flex min-w-0 flex-col gap-6 lg:sticky lg:top-20" aria-label="{{ __('Summary and actions') }}">
<x-ui.admin.record-section :title="__('Summary')" :hint="__('What saving would write.')">
<x-ui.fact-list :facts="$summary" />
</x-ui.admin.record-section>
<x-ui.admin.save-blockers
:blockers="$blockers"
:actions="[
['label' => $isEdit ? __('Save changes') : __('Create discount'), 'primary' => true],
['label' => __('Save as draft'), 'variant' => 'outline', 'requiresValid' => false],
]"
:cancel-href="$cancelHref"
:note="__('⌘S saves. Blockers clear as each field is fixed.')"
:dock-label="__('Form actions')"
/>
</aside>
</x-ui.form>
</div>
</main>
Installation
php artisan ui:add pages/admin-form
Registry contract
Install confidence
php artisan ui:add pages/admin-form
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.3
- License
- open
- Stability
- stable
- Contract
- v4
- Foundation
- ≥ 1.0.0
| Type | Generated target |
|---|---|
| blade | resources/views/pages/admin-form.blade.php |
Registry dependencies
Package dependencies
composer: jml/brok:^0.2
Use with AI
A brief for your coding agent: what the page is, the install command, how to render it, its props and the rules. Copy it, or open a prompt about this page in an assistant.
# Brok UI page: Admin Form (`admin-form`)
Create and edit as one page — a page header with a readiness pill, attention banners, decision-ordered record sections of real fields, and an end-side rail with a live summary and save blockers that dock on mobile.
Brok UI is a Laravel Blade registry. `ui:add` copies this page into the app as plain Blade the app owns; it composes installed `<brok:*>` primitives and semantic design tokens.
## Install
```bash
php artisan ui:add pages/admin-form
```
## Render it
```blade
<x-pages.admin-form />
```
## Props
- `mode` ('create'|'edit', default `create`) — Which starting point the page renders.
- `title` (string|null, default `null`) — Page title; derived from mode.
- `subtitle` (string|null, default `null`) — Scope sentence; derived from mode.
- `action` (string, default `#`) — Form POST target.
- `method` (string, default `POST`) — Form method.
- `cancel-href` (string, default `#`) — Cancel link.
- `record-href` (string, default `#`) — Back-to-record link in edit mode.
- `duplicated-from` (array|null, default `null`) — ['title','href'] renders the duplicated-from banner.
- `values` (array|null, default `null`) — Field values keyed by input name.
- `types` (array|null, default `null`) — [['value','label','hint']] for the kind choice.
- `blockers` (array|null, default `null`) — Save-blockers array; `[]` means ready.
- `summary` (array|null, default `null`) — Fact-list rows for the summary card.
## Use when
- Any create/edit screen in an admin: the same page renders empty or on an existing record.
- Save must stay visible with the reason it is disabled, on desktop and on a tablet.
## Avoid when
- A multi-step wizard — use `form-wizard`.
- Settings pages with many independent groups — use `pages/settings-layout`.
## Rules
- Render the installed page with `<x-pages.admin-form />` 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/pages/admin-form
- Registry JSON (files, props, contract): https://brokui.dev/r/open/pages/admin-form.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
- Any create/edit screen in an admin: the same page renders empty or on an existing record.
- Save must stay visible with the reason it is disabled, on desktop and on a tablet.
Avoid when
- A multi-step wizard — use `form-wizard`.
- Settings pages with many independent groups — use `pages/settings-layout`.
Anti-patterns
- Do not replace semantic props with conflicting utility classes.
- Do not remove labels, focus styles, or state attributes.
- Anatomy
- Theming hooks
Usage
Render the page as a component, or wire it to a route. Pass data where useful:
<x-pages.admin-form />
Built from blocks & primitives
This page 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 pages.
{{--
Admin Form — create and edit as one page with two starting points.
Creating a record and correcting one are the same set of decisions with
a different starting point, so they share this page rather than each
growing a field set that drifts. `mode="create"` renders it empty;
`mode="edit"` renders it on an existing record.
The composition follows the order the decisions are actually made: what
kind of thing this is (which decides which fields exist AT ALL), what it
does, who it applies to, what limits it, and when it runs. Each of those
is one `admin/record-section`; the whole form reads top to bottom as a
sentence. The end-side rail carries the consequence beside the work: a
live summary of what will be written, then what still blocks saving,
then the terminal actions — which never disappear, only disable with the
reason beside them (`admin/save-blockers`, which also docks itself to the
viewport bottom under `lg`, where the rail would otherwise sit a full
page-scroll below the field being typed).
Readiness is stated twice on purpose — as a pill in the page header and
in the rail — because on a long form one of the two is always off screen.
Server-first: every field is a real named control inside one POST form,
validation errors come back through the shared `$errors` bag into
`<x-ui.field>`, and the blockers list is whatever the host computes.
A Livewire or Alpine host can bind the same props live.
Generalised from the Noord-C admin's `discounts/form/page.blade.php`,
`customers/form/page.blade.php` and `bundles/form/page.blade.php`.
Left out as domain logic: the discount rule builder, the customer
address lookup, the bundle component picker.
--}}
@props([
'mode' => 'create',
'title' => null,
'subtitle' => null,
'action' => '#',
'method' => 'POST',
'cancelHref' => '#',
'recordHref' => '#',
'duplicatedFrom' => null,
'values' => null,
'types' => null,
'blockers' => null,
'summary' => null,
])
@php
// Shapes documented in item.json knowledge.props:
// duplicatedFrom: ['title', 'href'] — where a copy came from, and what was not copied, said before the operator goes looking.
// values: the record's current field values, keyed by input name.
// types: [['value', 'label', 'hint']] — the "what kind of thing" choice that decides which fields exist.
// blockers: the save-blockers `blockers` array; [] means the form is ready.
// summary: the fact-list `facts` array for the live summary card.
$isEdit = $mode === 'edit';
$title ??= $isEdit ? __('Edit discount') : __('New discount');
$subtitle ??= $isEdit
? __('Changes apply to new orders only; orders that already used this code keep what they were given.')
: __('A discount is a rule the checkout applies. Decide what kind it is first — that decides which fields exist.');
$types ??= [
['value' => 'percentage', 'label' => __('Percentage off'), 'hint' => __('A share of the line price.')],
['value' => 'fixed', 'label' => __('Fixed amount off'), 'hint' => __('A sum off the order.')],
['value' => 'shipping', 'label' => __('Free shipping'), 'hint' => __('Waives the shipping fee.')],
];
$values ??= $isEdit ? [
'title' => 'Spring welcome',
'type' => 'percentage',
'code' => 'SPRING10',
'value' => '10',
'audience' => 'everyone',
'minimum' => '25.00',
'uses' => '500',
'starts_at' => '2026-09-01',
'ends_at' => '2026-10-31',
'active' => true,
] : [
'title' => '',
'type' => 'percentage',
'code' => '',
'value' => '',
'audience' => 'everyone',
'minimum' => '',
'uses' => '',
'starts_at' => '',
'ends_at' => '',
'active' => false,
];
$blockers ??= $isEdit ? [] : [
['field' => 'title', 'text' => __('Give the discount a name.'), 'target' => 'title'],
['field' => 'code', 'text' => __('Set the code customers will type.'), 'target' => 'code'],
['field' => 'value', 'text' => __('Set how much comes off.'), 'target' => 'value'],
];
$ready = $blockers === [];
$summary ??= [
['label' => __('Rule'), 'value' => $isEdit ? __('10% off orders over €25.00') : __('Not decided yet')],
['label' => __('Audience'), 'value' => __('Everyone')],
['label' => __('Ceiling'), 'value' => $isEdit ? __('500 uses') : __('No limit'), 'tone' => $isEdit ? null : 'attention'],
['label' => __('Window'), 'value' => $isEdit ? '1 Sep – 31 Oct 2026' : __('Open-ended')],
];
$banners = $duplicatedFrom
? [[
'tone' => 'info',
'title' => __('Duplicated from :name', ['name' => $duplicatedFrom['title'] ?? '']),
'body' => __('Everything was copied except the code, which has to be unique.'),
'actionLabel' => $duplicatedFrom['title'] ?? __('Open original'),
'actionHref' => $duplicatedFrom['href'] ?? '#',
]]
: [];
$trail = array_values(array_filter([
['label' => __('Discounts'), 'href' => '#'],
$isEdit ? ['label' => $values['title'] ?? '', 'href' => $recordHref] : null,
['label' => $title],
]));
@endphp
<main
id="main-content"
data-slot="admin-form-page"
data-surface="admin"
data-mode="{{ $isEdit ? 'edit' : 'create' }}"
{{ $attributes->merge(['class' => 'min-h-screen bg-background text-foreground']) }}
>
<x-ui.admin.page-bar :title="$title" :trail="$trail" :record="[]" :nav-toggle="false" />
<div class="mx-auto flex max-w-screen-2xl flex-col gap-6 px-4 py-6 pb-24 sm:px-6 lg:pb-6">
<x-ui.admin.page-header :title="$title" :scope="$subtitle">
<x-slot:actions>
<x-ui.badge :tone="$ready ? 'positive' : 'attention'" size="status">
{{ $ready ? __('Ready to save') : trans_choice(':count thing missing|:count things missing', count($blockers), ['count' => count($blockers)]) }}
</x-ui.badge>
@if ($isEdit)
<x-ui.button variant="ghost" size="sm" :href="$recordHref">{{ __('Back to record') }}</x-ui.button>
@endif
</x-slot:actions>
</x-ui.admin.page-header>
<x-ui.admin.attention-banners :banners="$banners" />
<x-ui.form :action="$action" :method="$method" class="grid items-start gap-6 !space-y-0 lg:grid-cols-[minmax(0,1fr)_20rem]">
<div class="flex min-w-0 flex-col gap-6">
<x-ui.admin.record-section id="section-kind" :title="__('What kind')" :hint="__('Decides which of the fields below exist. Locked once the discount has been used.')">
<x-ui.field name="title" :label="__('Name')" :hint="__('Internal. Customers see the code, not the name.')" required>
<x-ui.input id="title" :value="$values['title'] ?? ''" autocomplete="off" />
</x-ui.field>
<fieldset class="mt-6">
<legend class="text-sm font-medium">{{ __('Type') }}</legend>
<div class="mt-2 grid gap-2 sm:grid-cols-3">
@foreach ($types as $type)
<x-ui.choicebox
type="radio"
name="type"
:value="$type['value']"
:title="$type['label']"
:description="$type['hint'] ?? null"
:checked="($values['type'] ?? null) === $type['value']"
/>
@endforeach
</div>
</fieldset>
</x-ui.admin.record-section>
<x-ui.admin.record-section id="section-value" :title="__('What the customer gets')" :hint="__('The code and the amount. A percentage over 50% is legitimate, but it is chosen on purpose.')">
<div class="grid gap-6 sm:grid-cols-2">
<x-ui.field name="code" :label="__('Code')" :hint="__('Upper-case letters and digits, 4–16 characters.')" required>
<x-ui.input id="code" :value="$values['code'] ?? ''" autocomplete="off" spellcheck="false" class="font-mono uppercase" />
</x-ui.field>
<x-ui.field name="value" :label="__('Value')" :hint="__('Percentage or amount, depending on the type.')" required>
<x-ui.input id="value" type="number" inputmode="decimal" min="0" step="0.01" :value="$values['value'] ?? ''" />
</x-ui.field>
</div>
</x-ui.admin.record-section>
<x-ui.admin.record-section id="section-audience" :title="__('Who may use it')" :hint="__('Everyone, or a segment. A minimum order keeps a code from being applied to a single low-value line.')">
<div class="grid gap-6 sm:grid-cols-2">
<x-ui.field name="audience" :label="__('Audience')">
<x-ui.select id="audience">
<option value="everyone" @selected(($values['audience'] ?? '') === 'everyone')>{{ __('Everyone') }}</option>
<option value="new" @selected(($values['audience'] ?? '') === 'new')>{{ __('First-time customers') }}</option>
<option value="segment" @selected(($values['audience'] ?? '') === 'segment')>{{ __('A segment…') }}</option>
</x-ui.select>
</x-ui.field>
<x-ui.field name="minimum" :label="__('Minimum order')" :hint="__('Leave empty for no minimum.')">
<x-ui.input id="minimum" type="number" inputmode="decimal" min="0" step="0.01" :value="$values['minimum'] ?? ''" />
</x-ui.field>
</div>
</x-ui.admin.record-section>
<x-ui.admin.record-section id="section-limits" :title="__('What limits it')" :hint="__('Without a ceiling a campaign can run away. State it here as a figure, before it runs.')">
<div class="grid gap-6 sm:grid-cols-2">
<x-ui.field name="uses" :label="__('Total uses')" :hint="__('Across all customers.')">
<x-ui.input id="uses" type="number" inputmode="numeric" min="0" step="1" :value="$values['uses'] ?? ''" />
</x-ui.field>
<x-ui.field name="notes" :label="__('Internal note')" :hint="__('Why this discount exists. Shown on the record, never to customers.')">
<x-ui.textarea id="notes" rows="3"></x-ui.textarea>
</x-ui.field>
</div>
</x-ui.admin.record-section>
<x-ui.admin.record-section id="section-window" :title="__('When it runs')" :hint="__('Time moves the window on its own; the switch is a decision a person makes. They are kept apart so a paused campaign is never confused with an expired one.')">
<div class="grid gap-6 sm:grid-cols-2">
<x-ui.field name="starts_at" :label="__('Starts')">
<x-ui.input id="starts_at" type="date" :value="$values['starts_at'] ?? ''" />
</x-ui.field>
<x-ui.field name="ends_at" :label="__('Ends')" :hint="__('Leave empty for an open-ended window.')">
<x-ui.input id="ends_at" type="date" :value="$values['ends_at'] ?? ''" />
</x-ui.field>
</div>
<div class="mt-6">
<x-ui.switch name="active" id="active" :label="__('Switched on')" :checked="(bool) ($values['active'] ?? false)" />
</div>
</x-ui.admin.record-section>
</div>
<aside class="flex min-w-0 flex-col gap-6 lg:sticky lg:top-20" aria-label="{{ __('Summary and actions') }}">
<x-ui.admin.record-section :title="__('Summary')" :hint="__('What saving would write.')">
<x-ui.fact-list :facts="$summary" />
</x-ui.admin.record-section>
<x-ui.admin.save-blockers
:blockers="$blockers"
:actions="[
['label' => $isEdit ? __('Save changes') : __('Create discount'), 'primary' => true],
['label' => __('Save as draft'), 'variant' => 'outline', 'requiresValid' => false],
]"
:cancel-href="$cancelHref"
:note="__('⌘S saves. Blockers clear as each field is fixed.')"
:dock-label="__('Form actions')"
/>
</aside>
</x-ui.form>
</div>
</main>
Ownership & lifecycle
Owner, release state, review evidence and adoption for this item.
- Owner
- Platform UI (@JoshJML)
- Current version
-
1.0.3 - Status
- Stable
- License
-
open - Deprecation
- Not deprecated
- Contract
-
v4 - Foundation
-
≥ 1.0.0