承接 gallardev/minimalist 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

gallardev/minimalist

Composer 安装命令:

composer require gallardev/minimalist

包简介

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

README 文档

README

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

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

Instalación

1. Composer

composer require gallardev/minimalist

Repositorio local (desarrollo):

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

2. Publicar e instalar

php artisan panel:install

Esto publica config/panel.php, registra rutas en /admin y prepara la estructura.

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.

PANEL_AUTH_ENABLED=true
PANEL_AUTH_REGISTER=true
PANEL_AUTH_REGISTER_ROLE=viewer   # opcional, con Spatie HasRoles

Con auth externa: PANEL_AUTH_ENABLED=false y PANEL_LOGIN_ROUTE=login.

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

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

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)

Clave Descripción Default
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

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.

Variables de entorno

PANEL_PATH=admin
PANEL_BRAND_NAME="Mi Panel"
PANEL_BRAND_LOGO="/images/logo.svg"
PANEL_THEME=dark
PANEL_COLOR_PRIMARY=#000000
PANEL_COLOR_PRIMARY_DARK=#ffffff
PANEL_COLOR_ACCENT=#525252

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]

Tema y colores

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

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

Botón en el header. Persistencia en localStorage (panel-theme).

Clases semánticas

Clase Uso
.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)

protected static ?string $icon = 'users';
<x-panel::icon name="package" class="h-4 w-4" />

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

Permisos

Spatie Laravel Permission (opcional)

composer require spatie/laravel-permission
PANEL_PERMISSIONS_ENABLED=true
PANEL_PERMISSION_ACCESS="access panel"
  • EnsurePanelAccess exige el permiso panel_access para entrar al panel
  • 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').

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

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

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)

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

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

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

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

Vista detalle

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

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

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

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)

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

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

Export CSV

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

Navegación SPA

  • wire:navigate en enlaces internos
  • Sidebar y header persistentes (@persist)
  • Spinner con blur solo en el área de contenido (#panel-main)
  • Prefetch con wire:navigate.hover

Fields disponibles

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

Columns disponibles

TextColumn, BooleanColumn, DateTimeColumn, BadgeColumn, BelongsToColumn, ImageColumn

Comandos

Comando Descripción
php artisan panel:install Instalar panel
php artisan panel:make-resource Name Crear Resource
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

Proyecto de prueba en panel-demo/:

admin@panel.test / password → /admin

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 con título de registro

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

Filtros avanzados

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

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

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

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

__('panel::panel.save')

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

Formularios en modal

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

PANEL_FORMS_IN_MODAL=true
// config/panel.php
'forms_in_modal' => env('PANEL_FORMS_IN_MODAL', true),

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

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

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

Ver PUBLISHING.md para subir el paquete a Packagist y etiquetar releases.

Breadcrumbs

Automáticos en el header según la ruta (Dashboard / Productos / Editar).

Tests

cd minimalist-panel-library
composer test

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
  • Publicación Packagist — ver PUBLISHING.md

Licencia

MIT

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 1
  • 点击次数: 2
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-06-19

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固