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
@props([
'title' => 'Projects',
'description' => 'Search, review, and manage active work.',
'rows' => null,
'queryAction' => null,
'searchValue' => '',
'filterValue' => '',
'sortValue' => 'updated_desc',
'activeFilters' => null,
'clearHref' => null,
'createHref' => '#',
'bulkAction' => '#',
'pagination' => null,
// Opt-in operational enrichments below. Every one defaults to null (or
// false), so a caller who passes none of them gets the exact worklist
// this page has always rendered — see CHANGELOG for the byte-identical
// proof. Generalised from the Noord-C admin's orders/fulfilment/returns
// index screens (`v2/orders/index.blade.php` and siblings).
//
// [['label','value','delta'=>null,'tone'=>null,'description'=>null,'href'=>null], ...]
// — composes the `stat` primitive; a tile with an href filters into the list it summarises.
'kpis' => null,
// [['label','href'=>null,'linkLabel'=>null,'tone'=>'attention'|'negative'|'info'], ...]
// — an operational banner zone above the KPIs. Renders nothing when empty.
'alerts' => null,
// ['syncedAt'=>'12 minutes ago','stale'=>false,'source'=>null] — an honest
// "synced N minutes ago" signal for data owned by an upstream system.
'freshness' => null,
// Forwarded to the `saved-views` primitive — see that item for the view shape.
'savedViews' => null,
// Forwarded to the `active-filters` primitive — distinct from `activeFilters`
// above, which feeds the header's own inline filter chips
// (`label`/`removeHref`). This is the standalone readout row rendered by
// the new `active-filters` primitive (`key`/`label`/`value`/`href`).
'appliedFilters' => null,
// Forwarded to the `bulk-job` primitive for async bulk-action progress. See that
// item for the full prop shape (action/state/done/total/failed/…).
'bulkJob' => null,
// compact (default) | comfortable | dense — forwarded to the table.
'density' => null,
])
@php
$rows ??= [
['id' => 'project_101', 'name' => 'Mobile application', 'owner' => 'Maya Chen', 'status' => 'Active', 'tone' => 'success', 'updated' => '12 minutes ago'],
['id' => 'project_102', 'name' => 'Design system', 'owner' => 'Noah Williams', 'status' => 'Active', 'tone' => 'success', 'updated' => 'Yesterday'],
['id' => 'project_103', 'name' => 'Partner portal', 'owner' => 'Ava Patel', 'status' => 'Draft', 'tone' => 'secondary', 'updated' => '3 days ago'],
['id' => 'project_104', 'name' => 'Research archive', 'owner' => 'Leo Martin', 'status' => 'Archived', 'tone' => 'outline', 'updated' => '8 days ago'],
];
$columns = [
['key' => 'name', 'label' => __('Name'), 'sortable' => false],
['key' => 'owner', 'label' => __('Owner'), 'sortable' => false],
['key' => 'status', 'label' => __('Status'), 'sortable' => false],
['key' => 'updated', 'label' => __('Updated'), 'type' => 'date', 'sortable' => false, 'tier' => 2],
['key' => 'actions', 'label' => __('Actions'), 'align' => 'end', 'sortable' => false],
];
$tableDensity = $density ?? 'compact';
$freshnessStale = $freshness ? (bool) ($freshness['stale'] ?? false) : false;
$freshnessSyncedAt = $freshness['syncedAt'] ?? '';
@endphp
<main
id="main-content"
data-slot="resource-index-page"
data-surface="admin"
data-visual-contracts="VC-005"
{{ $attributes->merge(['class' => 'min-h-screen bg-background text-foreground']) }}
>
<x-blocks.app-header-resource-toolbar
:title="$title"
:count="trans_choice(':count record|:count records', count($rows), ['count' => count($rows)])"
:description="$description"
:action="$queryAction"
search-placeholder="Search projects…"
:search-value="$searchValue"
:filter-value="$filterValue"
:sort-value="$sortValue"
:filters="$activeFilters"
:clear-href="$clearHref"
:create-href="$createHref"
create-label="New project"
/>
<div class="mx-auto max-w-screen-2xl px-4 py-6 sm:px-6">
@if ($savedViews || $freshness)
<div data-slot="resource-index-meta" class="mb-4 flex flex-wrap items-center justify-between gap-4">
<div class="flex min-w-0 flex-wrap items-center gap-2">
@if ($savedViews)
<x-ui.saved-views :views="$savedViews" />
@endif
</div>
@if ($freshness)
<span
data-slot="resource-index-freshness"
data-stale="{{ $freshnessStale ? 'true' : 'false' }}"
title="{{ $freshness['source'] ?? '' ? __(':source synced :time', ['source' => $freshness['source'], 'time' => $freshnessSyncedAt]) : __('Synced :time', ['time' => $freshnessSyncedAt]) }}"
@class([
'inline-flex h-8 shrink-0 items-center gap-2 rounded-md border px-2 text-xs',
'border-warning-line bg-warning-soft text-warning-text' => $freshnessStale,
'border-border text-muted-foreground' => ! $freshnessStale,
])
>
<span
@class(['size-2 shrink-0 rounded-full', 'bg-warning' => $freshnessStale, 'bg-success' => ! $freshnessStale])
aria-hidden="true"
></span>
{{ $freshnessStale
? __('Data may be stale — synced :time', ['time' => $freshnessSyncedAt])
: __('Synced :time', ['time' => $freshnessSyncedAt]) }}
</span>
@endif
</div>
@endif
@if (! empty($alerts))
<div data-slot="resource-index-alerts" class="mb-4 space-y-2">
@foreach ($alerts as $alert)
@php
$alertTone = in_array($alert['tone'] ?? null, ['negative', 'info', 'attention'], true) ? $alert['tone'] : 'attention';
$alertClasses = match ($alertTone) {
'negative' => 'border-destructive-line bg-destructive-soft text-destructive-text',
'info' => 'border-info-line bg-info-soft text-info-text',
default => 'border-warning-line bg-warning-soft text-warning-text',
};
@endphp
<div data-slot="resource-index-alert" data-tone="{{ $alertTone }}" class="{{ $alertClasses }} flex flex-wrap items-center gap-4 rounded-md border px-4 py-2 text-sm">
<span class="min-w-0 break-words font-medium">{{ $alert['label'] ?? '' }}</span>
@if (! empty($alert['href']))
<x-ui.button :href="$alert['href']" variant="ghost" size="sm" class="ms-auto shrink-0">
{{ $alert['linkLabel'] ?? __('Review') }}
</x-ui.button>
@endif
</div>
@endforeach
</div>
@endif
@if (! empty($kpis))
<div data-slot="resource-index-kpis" class="mb-6 grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5">
@foreach ($kpis as $kpi)
<x-ui.stat
:label="$kpi['label'] ?? null"
:value="$kpi['value'] ?? null"
:delta="$kpi['delta'] ?? null"
:tone="$kpi['tone'] ?? null"
:description="$kpi['description'] ?? null"
:href="$kpi['href'] ?? null"
/>
@endforeach
</div>
@endif
@if ($bulkJob)
<div class="mb-4">
<x-ui.bulk-job
:action="$bulkJob['action'] ?? ''"
:state="$bulkJob['state'] ?? 'running'"
:done="$bulkJob['done'] ?? 0"
:total="$bulkJob['total'] ?? 0"
:failed="$bulkJob['failed'] ?? []"
:message="$bulkJob['message'] ?? null"
:retry-url="$bulkJob['retryUrl'] ?? null"
:download-url="$bulkJob['downloadUrl'] ?? null"
:report-url="$bulkJob['reportUrl'] ?? null"
/>
</div>
@endif
@if (! empty($appliedFilters))
<div class="mb-4">
<x-ui.active-filters :filters="$appliedFilters" :clear-href="$clearHref" />
</div>
@endif
<x-ui.admin-table
:columns="$columns"
:selectable="true"
:pinned="true"
:density="$tableDensity"
:summary="trans_choice('Showing :count record|Showing :count records', count($rows), ['count' => count($rows)])"
>
@forelse ($rows as $row)
<x-ui.admin-table.row :key="$row['id']" select-label="Select :record">
<x-ui.admin-table.cell column="name" :pinned="true" class="py-4 font-medium">
{{ $row['name'] }}
</x-ui.admin-table.cell>
<x-ui.admin-table.cell column="owner" class="py-4 text-muted-foreground">
{{ $row['owner'] }}
</x-ui.admin-table.cell>
<x-ui.admin-table.cell column="status" class="py-4">
<x-ui.badge :variant="$row['tone'] ?? 'secondary'">{{ __($row['status']) }}</x-ui.badge>
</x-ui.admin-table.cell>
<x-ui.admin-table.cell column="updated" :tier="2" class="py-4 text-muted-foreground">
{{ __($row['updated']) }}
</x-ui.admin-table.cell>
<x-ui.admin-table.cell column="actions" align="end" class="py-4">
<x-ui.button :href="$row['href'] ?? '#'" variant="ghost" size="sm">{{ __('View') }}</x-ui.button>
</x-ui.admin-table.cell>
</x-ui.admin-table.row>
@empty
<x-ui.admin-table.empty>
<x-ui.empty variant="plain" size="sm">
<x-ui.empty.header>
<x-ui.empty.title as="h2">{{ __('No projects found') }}</x-ui.empty.title>
<x-ui.empty.description>{{ __('Change the search or filters, or create the first project.') }}</x-ui.empty.description>
</x-ui.empty.header>
<x-ui.empty.content>
<x-ui.button :href="$createHref">{{ __('New project') }}</x-ui.button>
</x-ui.empty.content>
</x-ui.empty>
</x-ui.admin-table.empty>
@endforelse
<x-slot:footer>
@if ($pagination && method_exists($pagination, 'links'))
{{ $pagination->links() }}
@else
<x-ui.button variant="ghost" size="sm" disabled>{{ __('Previous') }}</x-ui.button>
<span aria-current="page" class="text-foreground">{{ __('Page 1') }}</span>
<x-ui.button variant="ghost" size="sm" disabled>{{ __('Next') }}</x-ui.button>
@endif
</x-slot:footer>
<x-slot:bulk>
<x-ui.button type="button" variant="secondary" size="sm" class="min-h-11 sm:min-h-9" x-on:click="$dispatch('resource:bulk-archive', { ids: Array.from(selected) })">
{{ __('Archive') }}
</x-ui.button>
<x-ui.alert-dialog>
<x-ui.alert-dialog.trigger class="min-h-11 rounded-md bg-destructive px-4 text-sm font-medium text-destructive-foreground hover:bg-destructive/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring sm:min-h-9">
{{ __('Delete') }}
</x-ui.alert-dialog.trigger>
<x-ui.alert-dialog.content>
<x-ui.alert-dialog.header>
<x-ui.alert-dialog.title>{{ __('Delete selected resources?') }}</x-ui.alert-dialog.title>
<x-ui.alert-dialog.description>{{ __('This action cannot be undone. Only the selected resources will be deleted.') }}</x-ui.alert-dialog.description>
</x-ui.alert-dialog.header>
<x-ui.alert-dialog.footer>
<x-ui.alert-dialog.cancel>{{ __('Cancel') }}</x-ui.alert-dialog.cancel>
<x-ui.form method="DELETE" :action="$bulkAction" class="!space-y-0">
<template x-for="key in Array.from(selected)" :key="key">
<input type="hidden" name="selected[]" :value="key" />
</template>
<x-ui.button type="submit" variant="destructive">{{ __('Delete selected') }}</x-ui.button>
</x-ui.form>
</x-ui.alert-dialog.footer>
</x-ui.alert-dialog.content>
</x-ui.alert-dialog>
</x-slot:bulk>
</x-ui.admin-table>
</div>
</main>
Installation
php artisan ui:add pages/resource-page-index
Registry contract
Install confidence
php artisan ui:add pages/resource-page-index
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.2.2
- License
- open
- Stability
- stable
- Contract
- v2
- Foundation
- ≥ 1.0.0
| Type | Generated target |
|---|---|
| blade | resources/views/pages/resource-page-index.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: Resource Index (`resource-page-index`)
An authenticated resource index with a server-ready search, filter, and sort toolbar, a selectable paginated admin table, bulk actions, and an empty state. Optionally composes KPI tiles, an operational alert zone, a data-freshness indicator, saved views, an applied-filters readout, async bulk-job progress, and table density.
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/resource-page-index
```
## Render it
```blade
<x-pages.resource-page-index />
```
## Props
- `title` (string, default `Projects`) — Page heading.
- `description` (string, default `Search, review, and manage active work.`) — Page description.
- `rows` (array|null, default `null`) — Resource records with stable id, name, owner, status, tone, updated, and optional href values.
- `query-action` (string|null, default `null`) — GET action for search, filter, and sort.
- `search-value` (string) — Current search query.
- `filter-value` (string) — Current status filter.
- `sort-value` (string, default `updated_desc`) — Current sort order.
- `active-filters` (array|null, default `null`) — Active filter labels and removal links rendered inline in the resource toolbar header. Distinct from `appliedFilters` below.
- `clear-href` (string|null, default `null`) — Destination that clears all query state. Also used as the clear-all target for `appliedFilters`.
- `create-href` (string, default `#`) — Create-page destination.
- `bulk-action` (string, default `#`) — DELETE action that receives selected[] identifiers.
- `pagination` (object|null, default `null`) — Optional Laravel paginator whose links are rendered in the table footer.
- `kpis` (array|null, default `null`) — Summary tiles rendered above the table: [['label','value','delta'=>null,'tone'=>null,'description'=>null,'href'=>null]]. Composes <x-ui.stat>; a tile with an href filters into the list it summarises.
- `alerts` (array|null, default `null`) — Operational banner zone above the KPIs, for conditions needing attention: [['label','href'=>null,'linkLabel'=>null,'tone'=>'attention'|'negative'|'info']]. Renders nothing when null/empty.
- `freshness` (array|null, default `null`) — ['syncedAt','stale'=>false,'source'=>null] — an honest "synced N minutes ago" signal, with a stale state, for data owned by an upstream system.
- `saved-views` (array|null, default `null`) — Forwarded to <x-ui.saved-views> as `views`.
- `applied-filters` (array|null, default `null`) — Forwarded to <x-ui.active-filters> as `filters` (shape: key/label/value/href). Distinct from `activeFilters`, which feeds the header's own inline chips.
- `bulk-job` (array|null, default `null`) — Forwarded to <x-ui.bulk-job> for async bulk-action progress: ['action','state'=>'running','done'=>0,'total'=>0,'failed'=>[],'message'=>null,'retryUrl'=>null,'downloadUrl'=>null,'reportUrl'=>null].
- `density` (string|null, default `null`) — Table density tier forwarded to the admin table: compact (the implicit default when null), comfortable, or dense.
## Use when
- Use as the index route for an application resource that operators search, filter, sort, select, and manage.
## Avoid when
- Use a simple list when records do not need operational table controls or bulk actions.
## Rules
- Render the installed page with `<x-pages.resource-page-index />` 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/resource-page-index
- Registry JSON (files, props, contract): https://brokui.dev/r/open/pages/resource-page-index.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
- Use as the index route for an application resource that operators search, filter, sort, select, and manage.
Avoid when
- Use a simple list when records do not need operational table controls or bulk actions.
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.resource-page-index />
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.
@props([
'title' => 'Projects',
'description' => 'Search, review, and manage active work.',
'rows' => null,
'queryAction' => null,
'searchValue' => '',
'filterValue' => '',
'sortValue' => 'updated_desc',
'activeFilters' => null,
'clearHref' => null,
'createHref' => '#',
'bulkAction' => '#',
'pagination' => null,
// Opt-in operational enrichments below. Every one defaults to null (or
// false), so a caller who passes none of them gets the exact worklist
// this page has always rendered — see CHANGELOG for the byte-identical
// proof. Generalised from the Noord-C admin's orders/fulfilment/returns
// index screens (`v2/orders/index.blade.php` and siblings).
//
// [['label','value','delta'=>null,'tone'=>null,'description'=>null,'href'=>null], ...]
// — composes the `stat` primitive; a tile with an href filters into the list it summarises.
'kpis' => null,
// [['label','href'=>null,'linkLabel'=>null,'tone'=>'attention'|'negative'|'info'], ...]
// — an operational banner zone above the KPIs. Renders nothing when empty.
'alerts' => null,
// ['syncedAt'=>'12 minutes ago','stale'=>false,'source'=>null] — an honest
// "synced N minutes ago" signal for data owned by an upstream system.
'freshness' => null,
// Forwarded to the `saved-views` primitive — see that item for the view shape.
'savedViews' => null,
// Forwarded to the `active-filters` primitive — distinct from `activeFilters`
// above, which feeds the header's own inline filter chips
// (`label`/`removeHref`). This is the standalone readout row rendered by
// the new `active-filters` primitive (`key`/`label`/`value`/`href`).
'appliedFilters' => null,
// Forwarded to the `bulk-job` primitive for async bulk-action progress. See that
// item for the full prop shape (action/state/done/total/failed/…).
'bulkJob' => null,
// compact (default) | comfortable | dense — forwarded to the table.
'density' => null,
])
@php
$rows ??= [
['id' => 'project_101', 'name' => 'Mobile application', 'owner' => 'Maya Chen', 'status' => 'Active', 'tone' => 'success', 'updated' => '12 minutes ago'],
['id' => 'project_102', 'name' => 'Design system', 'owner' => 'Noah Williams', 'status' => 'Active', 'tone' => 'success', 'updated' => 'Yesterday'],
['id' => 'project_103', 'name' => 'Partner portal', 'owner' => 'Ava Patel', 'status' => 'Draft', 'tone' => 'secondary', 'updated' => '3 days ago'],
['id' => 'project_104', 'name' => 'Research archive', 'owner' => 'Leo Martin', 'status' => 'Archived', 'tone' => 'outline', 'updated' => '8 days ago'],
];
$columns = [
['key' => 'name', 'label' => __('Name'), 'sortable' => false],
['key' => 'owner', 'label' => __('Owner'), 'sortable' => false],
['key' => 'status', 'label' => __('Status'), 'sortable' => false],
['key' => 'updated', 'label' => __('Updated'), 'type' => 'date', 'sortable' => false, 'tier' => 2],
['key' => 'actions', 'label' => __('Actions'), 'align' => 'end', 'sortable' => false],
];
$tableDensity = $density ?? 'compact';
$freshnessStale = $freshness ? (bool) ($freshness['stale'] ?? false) : false;
$freshnessSyncedAt = $freshness['syncedAt'] ?? '';
@endphp
<main
id="main-content"
data-slot="resource-index-page"
data-surface="admin"
data-visual-contracts="VC-005"
{{ $attributes->merge(['class' => 'min-h-screen bg-background text-foreground']) }}
>
<x-blocks.app-header-resource-toolbar
:title="$title"
:count="trans_choice(':count record|:count records', count($rows), ['count' => count($rows)])"
:description="$description"
:action="$queryAction"
search-placeholder="Search projects…"
:search-value="$searchValue"
:filter-value="$filterValue"
:sort-value="$sortValue"
:filters="$activeFilters"
:clear-href="$clearHref"
:create-href="$createHref"
create-label="New project"
/>
<div class="mx-auto max-w-screen-2xl px-4 py-6 sm:px-6">
@if ($savedViews || $freshness)
<div data-slot="resource-index-meta" class="mb-4 flex flex-wrap items-center justify-between gap-4">
<div class="flex min-w-0 flex-wrap items-center gap-2">
@if ($savedViews)
<x-ui.saved-views :views="$savedViews" />
@endif
</div>
@if ($freshness)
<span
data-slot="resource-index-freshness"
data-stale="{{ $freshnessStale ? 'true' : 'false' }}"
title="{{ $freshness['source'] ?? '' ? __(':source synced :time', ['source' => $freshness['source'], 'time' => $freshnessSyncedAt]) : __('Synced :time', ['time' => $freshnessSyncedAt]) }}"
@class([
'inline-flex h-8 shrink-0 items-center gap-2 rounded-md border px-2 text-xs',
'border-warning-line bg-warning-soft text-warning-text' => $freshnessStale,
'border-border text-muted-foreground' => ! $freshnessStale,
])
>
<span
@class(['size-2 shrink-0 rounded-full', 'bg-warning' => $freshnessStale, 'bg-success' => ! $freshnessStale])
aria-hidden="true"
></span>
{{ $freshnessStale
? __('Data may be stale — synced :time', ['time' => $freshnessSyncedAt])
: __('Synced :time', ['time' => $freshnessSyncedAt]) }}
</span>
@endif
</div>
@endif
@if (! empty($alerts))
<div data-slot="resource-index-alerts" class="mb-4 space-y-2">
@foreach ($alerts as $alert)
@php
$alertTone = in_array($alert['tone'] ?? null, ['negative', 'info', 'attention'], true) ? $alert['tone'] : 'attention';
$alertClasses = match ($alertTone) {
'negative' => 'border-destructive-line bg-destructive-soft text-destructive-text',
'info' => 'border-info-line bg-info-soft text-info-text',
default => 'border-warning-line bg-warning-soft text-warning-text',
};
@endphp
<div data-slot="resource-index-alert" data-tone="{{ $alertTone }}" class="{{ $alertClasses }} flex flex-wrap items-center gap-4 rounded-md border px-4 py-2 text-sm">
<span class="min-w-0 break-words font-medium">{{ $alert['label'] ?? '' }}</span>
@if (! empty($alert['href']))
<x-ui.button :href="$alert['href']" variant="ghost" size="sm" class="ms-auto shrink-0">
{{ $alert['linkLabel'] ?? __('Review') }}
</x-ui.button>
@endif
</div>
@endforeach
</div>
@endif
@if (! empty($kpis))
<div data-slot="resource-index-kpis" class="mb-6 grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5">
@foreach ($kpis as $kpi)
<x-ui.stat
:label="$kpi['label'] ?? null"
:value="$kpi['value'] ?? null"
:delta="$kpi['delta'] ?? null"
:tone="$kpi['tone'] ?? null"
:description="$kpi['description'] ?? null"
:href="$kpi['href'] ?? null"
/>
@endforeach
</div>
@endif
@if ($bulkJob)
<div class="mb-4">
<x-ui.bulk-job
:action="$bulkJob['action'] ?? ''"
:state="$bulkJob['state'] ?? 'running'"
:done="$bulkJob['done'] ?? 0"
:total="$bulkJob['total'] ?? 0"
:failed="$bulkJob['failed'] ?? []"
:message="$bulkJob['message'] ?? null"
:retry-url="$bulkJob['retryUrl'] ?? null"
:download-url="$bulkJob['downloadUrl'] ?? null"
:report-url="$bulkJob['reportUrl'] ?? null"
/>
</div>
@endif
@if (! empty($appliedFilters))
<div class="mb-4">
<x-ui.active-filters :filters="$appliedFilters" :clear-href="$clearHref" />
</div>
@endif
<x-ui.admin-table
:columns="$columns"
:selectable="true"
:pinned="true"
:density="$tableDensity"
:summary="trans_choice('Showing :count record|Showing :count records', count($rows), ['count' => count($rows)])"
>
@forelse ($rows as $row)
<x-ui.admin-table.row :key="$row['id']" select-label="Select :record">
<x-ui.admin-table.cell column="name" :pinned="true" class="py-4 font-medium">
{{ $row['name'] }}
</x-ui.admin-table.cell>
<x-ui.admin-table.cell column="owner" class="py-4 text-muted-foreground">
{{ $row['owner'] }}
</x-ui.admin-table.cell>
<x-ui.admin-table.cell column="status" class="py-4">
<x-ui.badge :variant="$row['tone'] ?? 'secondary'">{{ __($row['status']) }}</x-ui.badge>
</x-ui.admin-table.cell>
<x-ui.admin-table.cell column="updated" :tier="2" class="py-4 text-muted-foreground">
{{ __($row['updated']) }}
</x-ui.admin-table.cell>
<x-ui.admin-table.cell column="actions" align="end" class="py-4">
<x-ui.button :href="$row['href'] ?? '#'" variant="ghost" size="sm">{{ __('View') }}</x-ui.button>
</x-ui.admin-table.cell>
</x-ui.admin-table.row>
@empty
<x-ui.admin-table.empty>
<x-ui.empty variant="plain" size="sm">
<x-ui.empty.header>
<x-ui.empty.title as="h2">{{ __('No projects found') }}</x-ui.empty.title>
<x-ui.empty.description>{{ __('Change the search or filters, or create the first project.') }}</x-ui.empty.description>
</x-ui.empty.header>
<x-ui.empty.content>
<x-ui.button :href="$createHref">{{ __('New project') }}</x-ui.button>
</x-ui.empty.content>
</x-ui.empty>
</x-ui.admin-table.empty>
@endforelse
<x-slot:footer>
@if ($pagination && method_exists($pagination, 'links'))
{{ $pagination->links() }}
@else
<x-ui.button variant="ghost" size="sm" disabled>{{ __('Previous') }}</x-ui.button>
<span aria-current="page" class="text-foreground">{{ __('Page 1') }}</span>
<x-ui.button variant="ghost" size="sm" disabled>{{ __('Next') }}</x-ui.button>
@endif
</x-slot:footer>
<x-slot:bulk>
<x-ui.button type="button" variant="secondary" size="sm" class="min-h-11 sm:min-h-9" x-on:click="$dispatch('resource:bulk-archive', { ids: Array.from(selected) })">
{{ __('Archive') }}
</x-ui.button>
<x-ui.alert-dialog>
<x-ui.alert-dialog.trigger class="min-h-11 rounded-md bg-destructive px-4 text-sm font-medium text-destructive-foreground hover:bg-destructive/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring sm:min-h-9">
{{ __('Delete') }}
</x-ui.alert-dialog.trigger>
<x-ui.alert-dialog.content>
<x-ui.alert-dialog.header>
<x-ui.alert-dialog.title>{{ __('Delete selected resources?') }}</x-ui.alert-dialog.title>
<x-ui.alert-dialog.description>{{ __('This action cannot be undone. Only the selected resources will be deleted.') }}</x-ui.alert-dialog.description>
</x-ui.alert-dialog.header>
<x-ui.alert-dialog.footer>
<x-ui.alert-dialog.cancel>{{ __('Cancel') }}</x-ui.alert-dialog.cancel>
<x-ui.form method="DELETE" :action="$bulkAction" class="!space-y-0">
<template x-for="key in Array.from(selected)" :key="key">
<input type="hidden" name="selected[]" :value="key" />
</template>
<x-ui.button type="submit" variant="destructive">{{ __('Delete selected') }}</x-ui.button>
</x-ui.form>
</x-ui.alert-dialog.footer>
</x-ui.alert-dialog.content>
</x-ui.alert-dialog>
</x-slot:bulk>
</x-ui.admin-table>
</div>
</main>
Ownership & lifecycle
Owner, release state, review evidence and adoption for this item.
- Owner
- Platform UI (@JoshJML)
- Current version
-
1.2.2 - Status
- Stable
- License
-
open - Deprecation
- Not deprecated
- Contract
-
v2 - Foundation
-
≥ 1.0.0