Adminix Documentation Help

Detail

DetailModule renders one record as a read-only presentation block. Use it when an admin user needs to inspect data without opening an edit form.

use AlexKudrya\Adminix\Modules\Detail\DetailModule; use AlexKudrya\Adminix\Modules\Detail\DetailField; use AlexKudrya\Adminix\Modules\Detail\DetailSection; use AlexKudrya\Adminix\Modules\Detail\DetailTab; use AlexKudrya\Adminix\Modules\Audit\AuditPanelModule; use AlexKudrya\Adminix\Modules\List\ListField; use AlexKudrya\Adminix\Modules\List\ListModule; use AlexKudrya\Adminix\Modules\Resource\RelationManager; use AlexKudrya\Adminix\Modules\Resource\ResourceProperty; use App\Adminix\Audit\DatabaseAuditProvider; use App\Models\Order; DetailModule::name('order-detail') ->title('Order details') ->dataSource(Order::class) ->addProp(ResourceProperty::key('id')->value('param:0')) ->addFields( DetailField::make('Number', 'number')->link('/adminix/orders/{value}'), DetailField::make('Status', 'status')->badge('#754195'), DetailField::make('Paid at', 'paid_at')->dateTime('d.m.Y H:i'), DetailField::make('Total', 'total')->money('USD', 2), DetailField::make('Receipt', 'receipt_url')->link(), DetailField::make('Payload', 'payload')->json(), );
Detail module

Generate a starter detail page provider:

php artisan make:adminix_resource ProductsShowPage --model="App\Models\Product" --with-detail

The generated page uses ResourceProperty::key('id')->value('param:0'), so open it with a route tail such as /adminix/products-show/15. Review generated fields and route names before registering it in a consuming app.

Detail Vs Resource Edit

Use DetailModule for read-only record inspection, support screens, order/customer overviews, audit-adjacent pages, and pages where the admin should compare data without editing it. It renders presentation-first fields, grouped sections, tabs, relation snippets, and optional audit panels. It never submits writable fields and does not expose create/update behavior.

Use ResourceModule when the page exists to edit an existing record. It renders a server-owned form, validates submitted writable fields, handles uploads, signs hidden param:* context, redirects after save, and records package audit events.

ResourceModule::readonly() can lock an edit form, but it still belongs to the form/edit surface. Prefer DetailModule when the page should look and behave like a polished read-only view instead of a disabled form.

Contract

DetailModule intentionally reuses the Resource server-side read contract:

  • name() is required and must be unique on the page;

  • title() is shown as the card heading;

  • dataSource() can be an Eloquent model class or table name;

  • resource() can map the loaded record through a Laravel JSON resource;

  • criteria() applies server-side filters and supports param:n;

  • addProp()/addProps() identify the record, commonly by id = param:0;

  • addField()/addFields() declare visible flat fields;

  • addSection()/addSections() group fields inside the card;

  • addTab()/addTabs() render Bootstrap tabs inside the card;

  • addRelation()/addRelations() render scoped relation snippets after the detail card.

  • auditPanel()/withAuditPanel() attaches an AuditPanelModule below the detail card and relation snippets.

The module is always read-only. readonly() is a no-op because the module never renders a write form.

Fields And Rendering

DetailModule accepts normal ResourceField instances and detail-specific DetailField instances. Use ResourceField when the existing resource input type is enough. Use DetailField when a read-only page needs richer presentation without changing persistence behavior.

Common ResourceField display behavior:

  • string-like values render as escaped text;

  • SELECT fields render the configured option label when src() records are available;

  • BOOLEAN fields render as compact yes/no badges;

  • JSON fields render pretty printed escaped JSON;

  • KEY_VALUE fields render escaped key/value rows from JSON objects;

  • DATE_RANGE fields render a compact from - to label from JSON range values;

  • IMAGE and FILE fields render display-only links/previews;

  • text/editor fields render escaped text with line breaks.

Hidden fields are not rendered.

DetailField helpers:

  • DetailField::make($label, $field) creates a display field;

  • badge($color = null) renders a compact status badge with an optional safe CSS color;

  • status([...])/badgeMap([...]) renders mapped status labels and colors through ResourceInputTypeEnum::BADGE;

  • link($href = null) renders the scalar value as a link; omit $href to use the field value;

  • image() and file() select image/file display modes;

  • json() renders escaped pretty JSON; invalid JSON strings are displayed as escaped text instead of executing content;

  • color() renders a color swatch and stored value;

  • slug() renders a slug as escaped scalar text while keeping the field type explicit for generated detail pages;

  • markdown() renders a safe markdown preview; raw HTML is escaped, while links, emphasis, inline code, and line breaks are rendered;

  • tags($separator = ',') renders chips from a separator-delimited string, JSON array string, or array value;

  • keyValue() renders escaped key/value rows from a JSON object or array of {key, value} pairs;

  • dateRange() renders JSON range values with from and to keys as a compact label;

  • date($format = 'Y-m-d') and dateTime($format = 'Y-m-d H:i') format date values through Carbon;

  • money($currency = 'USD', $decimals = 2) renders tabular currency values; ResourceField::money() works in detail views too;

  • emptyLabel($label) replaces the default empty marker for null/empty values.

Link hrefs accept /, #, http://, https://, mailto:, and tel: targets. The {value} placeholder is URL-encoded before substitution.

Sections, Columns, And Tabs

Use DetailSection for named groups and columns(2..4) for compact card grids. Use DetailTab when a detail page needs separate panes. Tabs can contain their own fields and nested sections.

DetailModule::name('order-detail') ->title('Order details') ->dataSource(Order::class) ->addProp(ResourceProperty::key('id')->value('param:0')) ->addTab( DetailTab::make('Summary', 'summary') ->description('Primary order fields') ->columns(2) ->addFields( DetailField::make('Number', 'number'), DetailField::make('Status', 'status')->badge('#754195'), ) ) ->addTab( DetailTab::make('System', 'system') ->addSection( DetailSection::make('Audit fields', 'audit') ->description('Server-owned metadata') ->columns(2) ->addFields( DetailField::make('Created', 'created_at')->dateTime('d.m.Y H:i'), DetailField::make('Payload', 'payload')->json(), ) ) );

When tabs or sections are configured, flat fields remain part of the data-provider contract but are not rendered in the default flat block. Keep fields in one visible layout path to avoid duplicate presentation.

Relation Snippets

DetailModule reuses the existing RelationManager contract from ResourceModule. Use it to show scoped child lists below the read-only detail card.

DetailModule::name('order-detail') ->title('Order details') ->dataSource(Order::class) ->addProp(ResourceProperty::key('id')->value('param:0')) ->addField(DetailField::make('Number', 'number')) ->addRelation( RelationManager::name('items') ->title('Items') ->relation('items') ->list( ListModule::name('order-items') ->addFields( ListField::name('SKU')->field('sku'), ListField::name('Qty')->field('quantity'), ) ) );

The relation list is prepared server-side from the configured relation and parent record. Optional relation create/edit modal modules can be configured through the same RelationManager::canCreate() and canEdit() methods when the consuming admin flow needs them.

Audit Panel

Attach the existing AuditPanelModule when the detail page should show the selected record timeline without registering a separate top-level module. The panel is prepared through the same audit provider contract and receives the detail page params, so subject(..., 'param:0') resolves to the current detail record id.

DetailModule::name('order-detail') ->title('Order details') ->dataSource(Order::class) ->addProp(ResourceProperty::key('id')->value('param:0')) ->addField(DetailField::make('Number', 'number')) ->auditPanel( AuditPanelModule::make('order-activity') ->title('Activity') ->subject(Order::class, 'param:0') ->provider(DatabaseAuditProvider::class) ->limit(25) );

If the provider fails, Adminix renders the controlled audit warning inside the attached panel instead of turning the detail page into a 500 response.

Safety Notes

The browser does not choose datasource, primary key, fields, criteria, or tenant context. Adminix resolves the record from server-side module configuration plus route params. Detail output escapes scalar, text, JSON, and date/money values; only safe link protocols are rendered as anchors. Relation snippets use signed relation context and server-side parent resolution; do not rebuild parent IDs from browser input. Attached audit panels use server-side AuditPanelModule configuration and page params; do not accept subject ids from query strings or hidden browser input. Do not use DetailModule as an authorization boundary by itself; keep page access, datasource criteria, policies, and tenant scoping server-side.

Last modified: 22 June 2026