vormiaphp/ui-livewireflux-admin
Laravel admin panel package for Vormia apps with Livewire 4 + Flux. Ships prebuilt routes, views, and Livewire components for managing categories, inheritance, locations, availability, and admin users, plus automatic sidebar integration and role assignment support.
description: Vormia UI Livewire Flux Admin - developer guide (structure, UI, AI prompts, Fortify, roles)
Single reference for package layout, admin UI flow and style, AI prompt workflow, Laravel Fortify (password stubs and active users), and assigning roles on registration. The YAML frontmatter above is the same scope as the former docs/ui-rules.md (Cursor-style hints for Blade and PHP); add this file to Project Rules if you want it applied automatically.
resources/views/livewire/admin/livewire/admin/control/<module>/livewire/admin/admins/.blade.php (new class extends Component), default Livewire layout — no #[Layout(...)]. Not Volt-specific.resources/views/layouts/app/sidebar.blade.phpresources/views/components/layouts/app/sidebar.blade.phpflux:sidebar.group, before </flux:sidebar.group>flux:sidebar.item with wire:navigate where the stubs dosrc/stubs/reference/sidebar-menu-to-add.blade.phpresources/views/components/admin-panel.blade.phpapp/View/Components/AdminPanel.phpheader, desc, button, default slot<x-admin-panel>; use WithNotifications and {!! $this->renderNotification() !!}dark:* classes) — see UI: flow and styleVormia\Vormia\Traits\Livewire\WithNotifications; pagination / #[Validate] / #[Computed] as in stubsadmin.<section>.index, .create, .editApp\Actions\Fortify\PasswordValidationRules after Fortify publish — see Fortify: passwords, publish, and active usersThis section is for developers consuming vormiaphp/ui-livewireflux-admin in their Laravel app and building new admin pages that match the package’s flow and visual style.
When you add a new admin module, it should follow the same shape as the existing stubs in:
src/stubs/resources/views/livewire/admin/**The consistent “look & flow” comes from:
#[Layout(...)] required)<x-admin-panel> with header, desc, and an optional “action” button slotindex pages: search, pagination, table list, row actions (edit / activate / deactivate / delete)create and edit pages: form + validation + notification feedback + “Go back” buttonWithNotifications trait and render notifications via {!! $this->renderNotification() !!}flux:sidebar.item entries (see src/stubs/reference/sidebar-menu-to-add.blade.php)Most modules follow this exact 3-page flow:
Routes are Livewire routes (see src/stubs/reference/routes-to-add.php) with names like:
admin.<module>.indexadmin.<module>.createadmin.<module>.editIn your app (after install/copy), keep the same structure:
resources/views/livewire/admin/
control/
<module>/
index.blade.php
create.blade.php
edit.blade.php
And add the 3 routes in routes/web.php under the admin prefix group.
Every page begins with a PHP block that declares an anonymous Livewire component. It relies on your app’s default Livewire layout:
<?php
use Livewire\Component;
use Vormia\Vormia\Traits\Livewire\WithNotifications;
new class extends Component {
use WithNotifications;
// ...
}; ?>
Wrap your page content like:
<x-admin-panel>
<x-slot name="header">{{ __('My Module') }}</x-slot>
<x-slot name="desc">
{{ __('Explain what the module manages.') }}
</x-slot>
<x-slot name="button">
<a href="{{ route('admin.my-module.create') }}"
class="bg-blue-500 dark:bg-blue-600 text-white hover:bg-blue-600 dark:hover:bg-blue-700 px-3 py-2 rounded-md float-right text-sm font-bold">
Add New
</a>
</x-slot>
{!! $this->renderNotification() !!}
<!-- Your content -->
</x-admin-panel>
The component class stub lives in src/stubs/app/View/Components/AdminPanel.php (it renders components.admin-panel in the consuming app).
The stubs consistently use:
bg-white dark:bg-gray-800 shadow-sm sm:rounded-lgbg-gray-50 dark:bg-gray-700even:bg-gray-50 dark:even:bg-gray-800/50divide-y divide-gray-300 dark:divide-gray-600rounded-md bg-white dark:bg-gray-700 ... outline-gray-300 dark:outline-gray-600 focus:outline-indigo-600bg-indigo-600 hover:bg-indigo-500bg-green-600 hover:bg-green-500bg-yellow-400 hover:bg-yellow-500bg-red-600 hover:bg-red-500Use this as your base. It matches the patterns used by e.g. control/categories/index.blade.php.
<?php
use Livewire\Attributes\Computed;
use Livewire\Component;
use Livewire\WithPagination;
use Vormia\Vormia\Traits\Livewire\WithNotifications;
new class extends Component {
use WithPagination;
use WithNotifications;
public $search = '';
public $perPage = 10;
public function updatedSearch(): void
{
$this->resetPage();
}
public function updatedPerPage(): void
{
$this->resetPage();
}
#[Computed]
public function results()
{
// Replace this query with your model query.
$query = \Illuminate\Database\Eloquent\Model::query();
if (! empty($this->search)) {
$query->where(function ($q) {
$q->where('name', 'like', '%'.$this->search.'%');
});
}
return $query->orderBy('created_at', 'desc')->paginate($this->perPage);
}
public function delete($id): void
{
try {
$row = \Illuminate\Database\Eloquent\Model::find($id);
if (! $row) {
$this->notifyError(__('Record not found!'));
return;
}
$row->delete();
$this->notifySuccess(__('Deleted successfully!'));
} catch (\Throwable $e) {
$this->notifyError(__('Delete failed: ').$e->getMessage());
}
}
}; ?>
<div>
<x-admin-panel>
<x-slot name="header">{{ __('My Module') }}</x-slot>
<x-slot name="desc">{{ __('Manage items for My Module.') }}</x-slot>
<x-slot name="button">
<a href="{{ route('admin.my-module.create') }}"
class="bg-blue-500 dark:bg-blue-600 text-white hover:bg-blue-600 dark:hover:bg-blue-700 px-3 py-2 rounded-md float-right text-sm font-bold">
Add New
</a>
</x-slot>
{{-- Search --}}
<div class="my-4">
<div class="bg-white dark:bg-gray-800 shadow-sm sm:rounded-lg">
<div class="px-4 py-5 sm:p-6">
<h3 class="text-base font-semibold text-gray-900 dark:text-gray-100">Search</h3>
<div class="w-full sm:max-w-xs">
<input type="text" wire:model.live.debounce.300ms="search"
class="block w-full rounded-md bg-white dark:bg-gray-700 px-3 py-1.5 text-base text-gray-900 dark:text-gray-100 outline-1 -outline-offset-1 outline-gray-300 dark:outline-gray-600 placeholder:text-gray-400 dark:placeholder:text-gray-500 focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6"
placeholder="Search..." />
</div>
</div>
</div>
</div>
{!! $this->renderNotification() !!}
{{-- List --}}
<div class="overflow-hidden shadow-sm ring-1 ring-black/5 dark:ring-white/10 sm:rounded-lg mt-2">
<table class="min-w-full divide-y divide-gray-300 dark:divide-gray-600">
<thead class="bg-gray-50 dark:bg-gray-700">
<tr>
<th class="py-3.5 pr-3 pl-4 text-left text-sm font-semibold text-gray-900 dark:text-gray-100 sm:pl-3">#</th>
<th class="py-3.5 pr-3 pl-4 text-left text-sm font-semibold text-gray-900 dark:text-gray-100 sm:pl-3">Name</th>
<th class="relative py-3.5 pr-4 pl-3 sm:pr-3"><span class="sr-only">Actions</span></th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800">
[@forelse](https://github.com/forelse) ($this->results as $row)
<tr class="even:bg-gray-50 dark:even:bg-gray-800/50">
<td class="py-4 pr-3 pl-4 text-sm font-medium whitespace-nowrap text-gray-900 dark:text-gray-100 sm:pl-3">{{ $row->id }}</td>
<td class="py-4 pr-3 pl-4 text-sm font-medium whitespace-nowrap text-gray-900 dark:text-gray-100 sm:pl-3">{{ $row->name ?? '-' }}</td>
<td class="relative py-4 pr-4 pl-3 text-right text-sm font-medium whitespace-nowrap sm:pr-3">
<a href="{{ route('admin.my-module.edit', $row->id) }}"
class="inline-flex items-center gap-x-1.5 rounded-md bg-indigo-600 px-2.5 py-1 text-xs font-semibold text-white shadow-xs hover:bg-indigo-500">
Edit
</a>
<button type="button" wire:click="delete({{ $row->id }})"
class="inline-flex items-center gap-x-1.5 rounded-md bg-red-600 px-2.5 py-1 text-xs font-semibold text-white shadow-xs hover:bg-red-500">
Delete
</button>
</td>
</tr>
[@empty](https://github.com/empty)
<tr>
<td colspan="3" class="py-8 text-center text-gray-500 dark:text-gray-400 font-bold">
No results found
</td>
</tr>
[@endforelse](https://github.com/endforelse)
</tbody>
</table>
<div class="p-4">
{{ $this->results->links() }}
</div>
</div>
</x-admin-panel>
</div>
Use the same building blocks the stubs use:
#[Validate(...)] for properties$this->validate() in save() / update()notifySuccess() / notifyError()If the form includes password fields (for example admin user create/edit), follow resources/views/livewire/admin/admins/create.blade.php and edit.blade.php: use Fortify’s PasswordValidationRules trait from App\Actions\Fortify\PasswordValidationRules (requires Fortify stubs in app/Actions/Fortify/ — see Fortify: passwords, publish, and active users).
To add your module to the sidebar, follow the same style as:
src/stubs/reference/sidebar-menu-to-add.blade.phpUse wire:navigate so navigation stays snappy with Livewire.
index/create/edit route names that match the existing conventions#[Layout(...)] attribute)<x-admin-panel> with header, desc, and a top-right action button (where relevant){!! $this->renderNotification() !!} is present and WithNotifications is useddark:* classes similar to existing stubsdivide-*, even:* zebra rows, and consistent action button colorsThis section is a copy/paste promptbook plus a repeatable workflow for using an AI assistant to build new admin UI screens that match the flow and style of vormiaphp/ui-livewireflux-admin.
It aligns with UI: flow and style above and the real stub patterns under:
src/stubs/resources/views/livewire/admin/**Your generated screens should match these conventions:
#[Layout(...)] attribute)<x-admin-panel> slots (header, desc, button)use WithNotifications; and {!! $this->renderNotification() !!}flux:sidebar.item with wire:navigateBefore you ask the AI to code, define these details (1 minute):
amenities, property-types, currenciesadmin.<module>.indexadmin.<module>.createadmin.<module>.editauth only, or role restricted?Always provide:
src/stubs/resources/views/livewire/admin/control/categories/index.blade.php)create.blade.php / edit.blade.php)This keeps the AI from inventing a different layout or CSS style.
Ask the AI to build in this order:
index.blade.phpcreate.blade.phpedit.blade.phproutes/web.phpBefore you accept the AI output, check:
#[Layout(...)])<x-admin-panel>{!! $this->renderNotification() !!} exists and trait is useddark:* classes like the stubsuse Vormia\Vormia\Models\Taxonomy;)App\Models\User and App\Actions\Fortify\PasswordValidationRules (Fortify must be published into app/Actions/Fortify/ — see Fortify: passwords, publish, and active users). For brand-new modules under control/<module>/, prefer Vormia package models unless the feature truly belongs in App\....Paste this prompt and fill the placeholders.
You are helping me extend a Laravel admin UI built with Livewire 4 + Flux.
Goal: Create a new admin module named: <module_slug>
It MUST match these conventions:
- Use Livewire anonymous components at the top of each Blade file.
- Use the app’s default Livewire layout (do NOT add `#[Layout(...)]`).
- Wrap all content with <x-admin-panel> and provide header/desc/button slots.
- Use the WithNotifications trait and render notifications using: {!! $this->renderNotification() !!}.
- Follow the same Tailwind classes and table/list patterns used in these reference stubs:
- <paste path to an existing index stub>
- <paste path to an existing create stub>
- <paste path to an existing edit stub>
Data model:
- Model class: <FQCN_or_import_name>
- Fields:
- <field_1> (validation: <rules>)
- <field_2> (validation: <rules>)
- ...
- List columns for index: <columns...>
- Search fields: <fields...>
Output:
1) resources/views/livewire/admin/control/<module_slug>/index.blade.php
2) resources/views/livewire/admin/control/<module_slug>/create.blade.php
3) resources/views/livewire/admin/control/<module_slug>/edit.blade.php
4) The Route::livewire(...) snippet (admin prefix) with the correct route names:
admin.<module_slug>.index / create / edit
5) Optional: Flux sidebar item snippet that matches existing menu style.
Constraints:
- Don’t invent new layouts or components. Match the reference stubs.
- Keep code direct and readable.
Use this when AI output looks “close but not consistent”.
Refactor the following Blade+Livewire anonymous component file to match the UI conventions used in this package’s stubs.
Must match:
- <x-admin-panel> shell with header/desc/button slots
- Search card styling + table styling (header bg, zebra rows, dividers)
- Action button colors and spacing consistent with existing stubs
- Dark mode classes present
- Notification rendering: {!! $this->renderNotification() !!}
Here is the reference stub to match:
<paste the relevant stub file content or point to it>
Here is my file to refactor:
<paste file content>
Generate only:
1) the Route::group(['prefix' => 'admin'], ...) entries using Route::livewire(...)
2) the flux:sidebar.item entries for the sidebar
Follow the exact naming conventions used by this package, similar to:
- src/stubs/reference/routes-to-add.php
- src/stubs/reference/sidebar-menu-to-add.blade.php
Module: <module_slug>
Index route name: admin.<module_slug>.index
Create route name: admin.<module_slug>.create
Edit route name: admin.<module_slug>.edit
Use this to plan quickly before prompting the AI:
Module slug:
Model class:
Fields:
- name: required|string|max:255
- description: nullable|string|max:1000
Index list columns:
- id, name, created_at, is_active
Actions:
- activate(id), deactivate(id), delete(id)
This package optionally copies EnsureUserIsActive.php into your app when Laravel Fortify is installed. This action checks the is_active flag on the User model (used by the Vormia package) and blocks inactive users from logging in.
The admin Livewire stubs expect App\Actions\Fortify\PasswordValidationRules (a trait published by Fortify). The ui-livewireflux-admin:install command runs vendor:publish for Laravel\Fortify\FortifyServiceProvider with --tag=fortify-support when PasswordValidationRules.php is not present yet. That publishes action stubs under app/Actions/Fortify/ (and the app FortifyServiceProvider stub) but does not publish Fortify database migrations.
| Tag | What it publishes |
|---|---|
fortify-support |
app/Actions/Fortify/* stubs (including PasswordValidationRules) and app/Providers/FortifyServiceProvider.php stub |
fortify-migrations |
Migrations for two-factor columns on users and the passkeys table (and related) — opt-in; run only when your schema does not already include them |
fortify-config |
config/fortify.php |
Install uses fortify-support only. For optional 2FA/passkeys schema, see README.md (Fortify database section) and publish fortify-migrations manually when needed.
If files under app/Actions/Fortify/ are missing or incomplete, publish the support tag:
php artisan vendor:publish --provider="Laravel\Fortify\FortifyServiceProvider" --tag=fortify-support
If files exist but are wrong or truncated and you intend to overwrite Fortify-published stubs (back up or commit first):
php artisan vendor:publish --provider="Laravel\Fortify\FortifyServiceProvider" --tag=fortify-support --force
Use **`--tag=fortify-support --force...
How can I help you explore Laravel packages today?