PHPackages                             gallardev/minimalist - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Admin Panels](/categories/admin)
4. /
5. gallardev/minimalist

ActiveLibrary[Admin Panels](/categories/admin)

gallardev/minimalist
====================

Minimalist — declarative monochrome admin panel for Laravel with Livewire and Tailwind

v0.12.1(today)01↑2900%MITPHPPHP ^8.2

Since Jun 19Pushed todayCompare

[ Source](https://github.com/gallardev98-blip/minimalist-panel)[ Packagist](https://packagist.org/packages/gallardev/minimalist)[ RSS](/packages/gallardev-minimalist/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (6)Versions (10)Used By (0)

Minimalist
==========

[](#minimalist)

Panel de administración declarativo y monocromático para Laravel. Alternativa moderna a AdminLTE, basado en **Livewire 3**, **Tailwind CSS** y una API de **Resources** al estilo Filament/Nova.

```
composer require gallardev/minimalist
```

Requisitos
----------

[](#requisitos)

- PHP 8.2+
- Laravel 11, 12 o 13
- Livewire 3.5+
- Tailwind CSS 3+ en la app host

Instalación
-----------

[](#instalación)

### 1. Composer

[](#1-composer)

```
composer require gallardev/minimalist
```

Repositorio local (desarrollo):

```
{
  "repositories": [
    { "type": "path", "url": "../minimalist-panel-library" }
  ],
  "require": {
    "gallardev/minimalist": "@dev"
  }
}
```

### 2. Publicar e instalar

[](#2-publicar-e-instalar)

```
php artisan panel:install
```

Esto publica `config/panel.php`, registra rutas en `/admin` y prepara la estructura. También publica `config/livewire.php` (si no existe) con `navigate.show_progress_bar = false` para usar solo el loader del panel.

El panel incluye **login y registro** en `/admin/login` y `/admin/register` usando la tabla `users` de Laravel. No necesitas Breeze salvo que quieras auth separada.

```
// config/panel.php
'auth' => [
    'enabled' => true,
    'register' => true,
    'register_role' => 'viewer', // opcional, con Spatie HasRoles
],
```

Con auth externa: `'enabled' => false` y `'login_route' => 'login'`.

Recuperar contraseña (activo por defecto):

```
'auth' => [
    'password_reset' => true, // /admin/forgot-password
],
```

### Perfil de usuario

[](#perfil-de-usuario)

Ruta `/admin/profile` — el usuario logueado edita su cuenta:

```
'profile' => [
    'enabled' => true,
],
```

Desactivar con `'enabled' => false` si no lo necesitas.

### 3. Tailwind (app host)

[](#3-tailwind-app-host)

Incluye las vistas del paquete en `tailwind.config.js`:

```
content: [
  './resources/views/**/*.blade.php',
  './vendor/gallardev/minimalist/resources/views/**/*.blade.php',
],
```

Activa modo oscuro por clase:

```
darkMode: 'class',
```

### 4. Alpine + Livewire

[](#4-alpine--livewire)

En `resources/js/app.js`, **no** llames `Alpine.start()` en rutas del panel (Livewire lo gestiona):

```
import Alpine from 'alpinejs';
window.Alpine = Alpine;

if (! window.location.pathname.startsWith('/admin')) {
    Alpine.start();
}
```

---

Primer Resource
---------------

[](#primer-resource)

```
php artisan panel:make-resource Product --model=Product
```

```
// app/Panel/Resources/ProductResource.php
final class ProductResource extends Resource
{
    protected static string $model = Product::class;
    protected static ?string $label = 'Productos';
    protected static ?string $icon = 'package'; // icono Lucide

    public static function form(): array
    {
        return [
            TextField::make('name')->label('Nombre')->required(),
            NumberField::make('price')->label('Precio')->min(0),
        ];
    }

    public static function table(): array
    {
        return [
            TextColumn::make('name')->label('Nombre')->searchable()->sortable(),
            TextColumn::make('price')->label('Precio')->sortable(),
        ];
    }
}
```

Auto-discovery en `app/Panel/Resources/` (configurable en `config/panel.php`).

---

Configuración (`config/panel.php`)
----------------------------------

[](#configuración-configpanelphp)

ClaveDescripciónDefault`path`Prefijo URL del panel`admin``middleware`Middleware de rutas`web` + `EnsurePanelAccess``guard`Guard de autenticación`web``brand.name`Nombre en sidebar`Panel``brand.logo`URL o ruta del logo (`null` = icono por defecto)`null``per_page`Registros por página`15``discovery`Auto-discovery de Resources`enabled``pages`Auto-discovery de Pages custom`enabled``permissions`Spatie/Gate (`enabled`, `panel_access`)`disabled``navigation`Menú lateral personalizado (`null` = auto desde resources)`null``widgets`Widgets del dashboard`[]`### Navegación con grupos desplegables

[](#navegación-con-grupos-desplegables)

Define `navigation` en `config/panel.php` o en un archivo dedicado:

```
// config/panel.php
'navigation' => require __DIR__.'/panel-navigation.php',
```

Formato de ítems:

```
return [
    // Enlace a un Resource (resuelve label, icono y URL automáticamente)
    ['resource' => ProductResource::class],

    // Enlace a una Page custom (informes, ajustes)
    ['page' => SettingsPage::class],

    // Enlace manual
    [
        'label' => 'Informe de ventas',
        'icon' => 'bar-chart',
        'route' => 'panel.dashboard',   // preferido (se resuelve en runtime)
        'badge' => 'Demo',              // opcional
    ],

    // Grupo desplegable (Alpine.js)
    [
        'type' => 'group',
        'label' => 'Catálogo',
        'icon' => 'package',
        'children' => [
            ['resource' => ProductResource::class],
            ['resource' => CategoryResource::class],
        ],
    ],
];
```

- Los grupos se abren automáticamente si contienen la ruta activa.
- La búsqueda global (`Cmd/Ctrl+K`) indexa todos los enlaces (aplanando grupos).
- Iconos: nombres Lucide soportados en `resources/views/components/icon.blade.php`.
- **No uses `route()` al cargar el config**; usa la clave `route` para enlaces nombrados.

### Configuración (`config/panel.php`)

[](#configuración-configpanelphp-1)

Tras `panel:install`, edita `config/panel.php`. **No hace falta usar variables `.env`** — todo vive en el archivo de config (buena práctica Laravel; compatible con `config:cache`).

```
'path' => 'admin',

'brand' => [
    'name' => 'Mi Panel',
    'logo' => '/images/logo.svg',
],

'theme' => [
    'default' => 'dark',
    'colors' => [
        'primary' => '#000000',
        'primary_dark' => '#ffffff',
        'accent' => '#525252',
    ],
],
```

Si prefieres `.env`, puedes envolver valores en el config publicado: `'path' => env('PANEL_PATH', 'admin')`.

---

Páginas custom
--------------

[](#páginas-custom)

Para informes, ajustes o pantallas que no son CRUD:

```
php artisan panel:make-page Settings
```

```
use Panel\Minimalist\Pages\Page;

final class SettingsPage extends Page
{
    protected static ?string $label = 'General';
    protected static ?string $slug = 'settings-general';
    protected static ?string $permission = 'manage settings';

    public static function view(): string
    {
        return 'panel.pages.settings-general';
    }

    public static function data(): array
    {
        return ['storeName' => config('app.name')];
    }
}
```

- Ruta: `/admin/pages/{slug}`
- Auto-discovery en `app/Panel/Pages`
- Añade al menú: `['page' => SettingsPage::class]`

Vista Blade con cabecera unificada (título + miga de pan en la misma fila):

```

    {{ $pageClass::label() }}
    Descripción opcional.

```

---

Layout y cabecera de página
---------------------------

[](#layout-y-cabecera-de-página)

Desde **v0.13.0** no hay barra superior global. Cada pantalla usa ``:

- **Izquierda:** título (y subtítulo, enlace «volver», etc.)
- **Derecha:** miga de pan automática
- **Móvil:** botón menú a la izquierda del título

El **sidebar** incluye en el footer: enlace al perfil, toggle claro/oscuro, versión del panel (`panel.version`) e icono de cerrar sesión.

```
// config/panel.php — versión mostrada en el sidebar (null = v{Package::VERSION})
'version' => null,
```

---

Tema y colores
--------------

[](#tema-y-colores)

Paleta **monocromática** por defecto (blanco/negro). Totalmente personalizable vía config.

### Estructura

[](#estructura)

```
'theme' => [
    'default' => 'dark',           // dark | light
    'font' => 'Plus Jakarta Sans',
    'radius' => '0.75rem',
    'sidebar_width' => '16rem',

    'colors' => [
        'primary' => '#000000',              // modo claro: botones, acentos
        'primary_hover' => '#262626',
        'primary_dark' => '#ffffff',         // modo oscuro
        'primary_hover_dark' => '#e5e5e5',
        'accent' => '#525252',
        'accent_dark' => '#a3a3a3',
        'success' => '#16a34a',
        'danger' => '#dc2626',
        'warning' => '#ca8a04',
    ],

    'light' => [
        'bg' => '#ffffff',
        'surface' => '#fafafa',
        'card' => '#ffffff',
        'elevated' => '#f5f5f5',
        'border' => '#e5e5e5',
        'heading' => '#0a0a0a',
        'text' => '#404040',
        'muted' => '#737373',
        'input_bg' => '#ffffff',
        'input_border' => '#d4d4d4',
    ],

    'dark' => [
        'bg' => '#0a0a0a',
        'surface' => '#111111',
        'card' => '#141414',
        'elevated' => '#1a1a1a',
        'border' => '#262626',
        'heading' => '#fafafa',
        'text' => '#d4d4d4',
        'muted' => '#737373',
        'input_bg' => '#0a0a0a',
        'input_border' => '#404040',
    ],
],
```

Los colores se inyectan como variables CSS (`--panel-primary`, `--panel-bg`, etc.) mediante `ThemeResolver`. El contraste del texto en botones primarios se calcula automáticamente.

### Toggle claro/oscuro

[](#toggle-clarooscuro)

Botón en el **footer del sidebar**. Persistencia en `localStorage` (`panel-theme`).

### Clases semánticas

[](#clases-semánticas)

ClaseUso`.panel-body`Fondo y texto base`.panel-card`Tarjetas`.panel-heading` / `.panel-text` / `.panel-muted`Tipografía`.panel-input`Formularios`.panel-btn-primary`Botón principal`.panel-nav-link-active`Nav activo`.panel-table`Tablas---

Iconos (Lucide)
---------------

[](#iconos-lucide)

```
protected static ?string $icon = 'users';
```

```

```

Disponibles: `layout-dashboard`, `package`, `folder`, `users`, `plus`, `pencil`, `trash-2`, `eye`, `search`, `download`, `layers`, `check-circle`, `loader-2`, etc.

---

Permisos
--------

[](#permisos)

### Spatie Laravel Permission (opcional)

[](#spatie-laravel-permission-opcional)

```
composer require spatie/laravel-permission
```

```
// config/panel.php
'permissions' => [
    'enabled' => true,
    'panel_access' => 'access panel',
    'resources' => true,              // RoleResource + PermissionResource integrados
    'manage_permission' => 'manage users', // permiso para CRUD roles/permisos
],
```

- `EnsurePanelAccess` exige el permiso `panel_access` para entrar al panel
- Con `resources => true` y Spatie instalado, se registran **`RoleResource`** y **`PermissionResource`** en `/admin/resources/roles` y `/admin/resources/permissions`
- Asigna permisos a roles con `PermissionsField`; asigna roles a usuarios con `RolesField`
- Si defines tus propios resources con slug `roles` o `permissions` en `app/Panel/Resources`, prevalecen sobre los integrados
- En **Pages**: `protected static ?string $permission = 'view reports'`
- En **navegación**: `'permission' => 'manage settings'` en enlaces manuales
- Resources y Pages en el menú se ocultan si el usuario no tiene acceso

Usa Spatie en tus Policies: `$user->can('manage products')`.

### Roles en usuarios

[](#roles-en-usuarios)

Con Spatie instalado y `HasRoles` en tu modelo `User`:

```
use Panel\Minimalist\Fields\RolesField;
use Panel\Minimalist\Columns\RolesColumn;

RolesField::make('roles')->label('Roles'),
RolesColumn::make('roles')->label('Roles'),
```

Los roles se sincronizan con `syncRoles()` al crear o editar (no van en `$fillable`).

### Permisos en roles

[](#permisos-en-roles)

```
use Panel\Minimalist\Fields\PermissionsField;
use Panel\Minimalist\Columns\PermissionsColumn;

PermissionsField::make('permissions')->label('Permisos'),
PermissionsColumn::make('permissions')->label('Permisos'),
```

Los permisos se sincronizan con `syncPermissions()` al guardar el rol.

---

Dos capas en **Resources** combinadas con **AND** (ambas deben permitir):

1. **Hooks en el Resource** — `canViewAny()`, `canCreate()`, `canEdit()`, etc.
2. **Policy de Laravel** — si existe para el modelo

### Hooks rápidos

[](#hooks-rápidos)

```
public static function canViewAny(): bool { return true; }
public static function canCreate(): bool { return true; }
public static function canEdit(Model $record): bool { return true; }
public static function canDelete(Model $record): bool { return true; }
```

### Policies (recomendado)

[](#policies-recomendado)

```
php artisan panel:make-policy Product
```

Genera `App\Policies\ProductPolicy` extendiendo `Panel\Minimalist\Policies\ResourcePolicy`.

Las policies hijas deben mantener `$user` y `$record` **sin type-hint** (restricción de PHP al heredar). Usa `instanceof` en el cuerpo si necesitas tu modelo:

```
public function delete($user, $record): bool
{
    return $user instanceof User && $user->isPanelAdmin();
}
```

```
use App\Policies\ProductPolicy;

protected static ?string $policy = ProductPolicy::class;
```

Sin `$policy` explícita, auto-detecta `App\Policies\{Model}Policy` si `panel.policies.auto_register` es `true`. La base niega todo por defecto.

---

Filtros
-------

[](#filtros)

```
public static function filters(): array
{
    return [
        SelectFilter::make('category_id')
            ->label('Categoría')
            ->relationship(Category::class, 'name'),
        BooleanFilter::make('is_active')->label('Activo'),
    ];
}
```

---

Acciones masivas
----------------

[](#acciones-masivas)

```
public static function bulkActions(): array
{
    return [
        BulkAction::make('delete', 'Eliminar')
            ->action(fn ($records) => $records->each->delete())
            ->color('rose')
            ->requiresConfirmation(),
    ];
}
```

Incluye `exportSelection` para CSV.

---

Soft deletes
------------

[](#soft-deletes)

Si el modelo usa `SoftDeletes`, el listado muestra papelera, restaurar y eliminar permanente.

---

Vista detalle
-------------

[](#vista-detalle)

```
public static function detail(): array
{
    return static::table(); // o columnas propias
}
```

Ruta: `GET /admin/resources/{slug}/{id}`

---

Form Sections
-------------

[](#form-sections)

```
use Panel\Minimalist\Forms\Section;

public static function form(): array
{
    return [
        Section::make('Información', [
            TextField::make('name')->required(),
        ])->description('Datos básicos'),
        TextField::make('email')->required(),
    ];
}
```

BelongsToMany
-------------

[](#belongstomany)

```
public static function relations(): array
{
    return [
        RelationManager::belongsToMany('tags', TagResource::class)
            ->title('Etiquetas'),
    ];
}
```

En la vista **Ver** del registro padre: crear etiqueta y vincular, o desvincular.

RelationManager (HasMany)
-------------------------

[](#relationmanager-hasmany)

Gestiona relaciones **HasMany** desde la vista detalle:

```
public static function relations(): array
{
    return [
        RelationManager::make('products', ProductResource::class)
            ->title('Productos de esta categoría'),
    ];
}
```

---

Widgets
-------

[](#widgets)

```
// config/panel.php
'widgets' => [
    ResourceCountWidget::make(ProductResource::class),
    StatWidget::make('Activos', fn () => Product::where('is_active', true)->count())
        ->icon('check-circle'),
],
```

---

Export CSV
----------

[](#export-csv)

- Botón **Exportar CSV** en listados (respeta búsqueda y filtros)
- Acción masiva **Exportar selección**

---

Navegación SPA
--------------

[](#navegación-spa)

- `wire:navigate` en enlaces internos
- Sidebar persistente (`@persist` en toasts)
- Loader a pantalla completa del área de contenido con **porcentaje entero** (`0%`–`100%`) en el anillo
- Prefetch con `wire:navigate.hover` (páginas cacheadas saltan a `100%`)

Desactiva la barra nativa de Livewire para no duplicar indicadores:

```
// config/livewire.php
'navigate' => [
    'show_progress_bar' => false,
],
```

`panel:install` publica `config/livewire.php` y desactiva `show_progress_bar` automáticamente si aún está en `true`.

---

Fields disponibles
------------------

[](#fields-disponibles)

`TextField`, `EmailField`, `PasswordField`, `TextareaField`, `BooleanField`, `SelectField`, `BelongsToField`, `NumberField`, `ImageField`

Columns disponibles
-------------------

[](#columns-disponibles)

`TextColumn`, `BooleanColumn`, `DateTimeColumn`, `BadgeColumn`, `BelongsToColumn`, `ImageColumn`

---

Comandos
--------

[](#comandos)

ComandoDescripción`php artisan panel:install`Instalar panel`php artisan panel:make-resource Name`Crear Resource`php artisan panel:make-page Name`Crear página custom`php artisan panel:make-policy Name`Crear Policy para un modelo`php artisan vendor:publish --tag=panel-config`Publicar config`php artisan vendor:publish --tag=panel-views`Publicar vistas---

Demo
----

[](#demo)

Proyecto de prueba en `panel-demo/`:

```
admin@panel.test / password → /admin

```

---

RowAction (acciones por fila)
-----------------------------

[](#rowaction-acciones-por-fila)

```
use Panel\Minimalist\Actions\RowAction;

public static function rowActions(): array
{
    return [
        RowAction::view(),
        RowAction::edit(),
        RowAction::make('duplicate')
            ->label('Duplicar')
            ->icon('copy')
            ->handle(fn (Model $record) => /* ... */),
    ];
}
```

Por defecto: `view`, `edit`, `delete` (+ `restore` / `forceDelete` con soft deletes).

Breadcrumbs
-----------

[](#breadcrumbs)

Automáticos según la ruta (`Panel / Productos / Editar`). Se renderizan dentro de `` a la derecha del título.

Título del registro en show/edit:

```
protected static ?string $recordTitleAttribute = 'name';
// o automático desde la primera columna searchable
```

---

Tests
-----

[](#tests)

```
cd minimalist-panel-library
composer test
```

Incluye tests de layout SPA (`SpaLoaderTest`): markup del loader con `%`, script de progreso y `page-header`.

Filtros avanzados
-----------------

[](#filtros-avanzados)

```
DateRangeFilter::make('published_at')->label('Publicado entre'),
MultiSelectFilter::make('category_id')->relationship(Category::class, 'name'),
```

Export Excel
------------

[](#export-excel)

```
// En el listado: botones "Exportar CSV" y "Exportar Excel"
// Bulk actions por defecto incluyen exportSelection y exportSelectionExcel
```

Requiere `phpoffice/phpspreadsheet` (incluido en el paquete).

Búsqueda global
---------------

[](#búsqueda-global)

- **Cmd+K** / **Ctrl+K** abre la paleta de búsqueda
- Busca en navegación y registros (columnas `searchable()`)
- Componente: `panel.global-search`

Nuevos Fields
-------------

[](#nuevos-fields)

```
use Panel\Minimalist\Fields\DateField;
use Panel\Minimalist\Fields\FileField;
use Panel\Minimalist\Fields\RichTextField;

DateField::make('published_at')->label('Publicación')->time(),
FileField::make('brochure')->directory('docs')->acceptedMimes(['pdf']),
RichTextField::make('description')->label('Descripción'),
```

Internacionalización
--------------------

[](#internacionalización)

```
__('panel::panel.save')
```

Traducciones en `lang/es/panel.php` y `lang/en/panel.php` (namespace `panel::panel.*`).

Formularios en modal
--------------------

[](#formularios-en-modal)

Por defecto, crear y editar se abren en un modal sobre el listado sin salir de la página:

```
// config/panel.php
'forms_in_modal' => true,
```

Con `false`, se usan las rutas de página completa (`panel.resources.create` / `edit`).

Tabs en formularios
-------------------

[](#tabs-en-formularios)

Organiza secciones en pestañas con `Tab::make()`:

```
use Panel\Minimalist\Forms\Section;
use Panel\Minimalist\Forms\Tab;

public static function form(): array
{
    return [
        Tab::make('General', [
            Section::make('Datos', [
                TextField::make('name')->required(),
            ]),
        ]),
        Tab::make('Precio', [
            NumberField::make('price')->required(),
        ]),
    ];
}
```

Export PDF
----------

[](#export-pdf)

Botones **CSV**, **XLSX** y **PDF** en la toolbar del listado. Si hay filas seleccionadas, exportan solo la selección; si no, el listado filtrado completo.

Publicar en Packagist
---------------------

[](#publicar-en-packagist)

Ver [PUBLISHING.md](PUBLISHING.md) para subir el paquete a Packagist y etiquetar releases.

---

Roadmap
-------

[](#roadmap)

- Fases 1–5 (CRUD, SPA, Excel, búsqueda global, i18n, tests, CI)
- Fase 6: RowAction, confirm modal, skeletons, DateRange/MultiSelect, breadcrumbs con título
- Fase 7: crear/editar en modal, tabs en formularios, export PDF
- Fase 8: Policies Laravel, `panel:make-policy`, `ResourcePolicy`
- Fase 9: páginas custom (`Page`) y permisos Spatie/Gate
- Fase 10: autenticación integrada (login, registro, logout)
- Fase 11: recuperar contraseña, `RolesField` / `RolesColumn`
- Fase 12: perfil de usuario (`/admin/profile`)
- Fase 13: layout sin header, ``, loader SPA con `%`
- Fase 14: `RoleResource` / `PermissionResource` integrados, `PermissionsField` / `PermissionsColumn`
- Packagist — `gallardev/minimalist`

Licencia
--------

[](#licencia)

MIT

###  Health Score

38

—

LowBetter than 83% of packages

Maintenance100

Actively maintained with recent releases

Popularity2

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity41

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

9

Last Release

0d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/a103f400886d72d37704ff55520cf743da84b047aa0d4dc2802f30cb9309dd7b?d=identicon)[Gallardev98](/maintainers/Gallardev98)

---

Tags

laravellivewiretailwindadminpanelminimalistmonochrome

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/gallardev-minimalist/health.svg)

```
[![Health](https://phpackages.com/badges/gallardev-minimalist/health.svg)](https://phpackages.com/packages/gallardev-minimalist)
```

###  Alternatives

[tomshaw/electricgrid

A feature-rich Livewire package designed for projects that require dynamic, interactive data tables.

119.2k](/packages/tomshaw-electricgrid)[yajra/laravel-datatables-export

Laravel DataTables Queued Export Plugin.

352.1M4](/packages/yajra-laravel-datatables-export)[2lenet/crudit-bundle

The easy like Crud'it Bundle.

1715.6k12](/packages/2lenet-crudit-bundle)[a2insights/filament-saas

Filament Saas for A2Insights

171.5k](/packages/a2insights-filament-saas)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
