Skip to content

Configuración de un Panel ​

Un Panel es la unidad de incrustación de Arrel: agrupa los Resources, las páginas y los widgets que quieres exponer bajo una URL, con su propia marca. Una aplicación puede registrar más de uno (por ejemplo, un panel admin y un panel branded-demo con colores distintos para un cliente concreto).

Registrar un Panel ​

Se registra contra el singleton PanelRegistry que el ServiceProvider de Arrel ya enlaza, normalmente desde el boot() de un ServiceProvider propio de tu aplicación:

php
use Arrel\Panels\Panel;
use Arrel\Panels\PanelRegistry;

public function boot(PanelRegistry $panels): void
{
    $panels->register(
        Panel::make('admin')
            ->resources([
                PostResource::class,
            ])
            ->pages([
                SiteSettingsPage::class,
            ])
            ->customPages([
                CustomPage::make('about', 'About'),
            ])
            ->widgets([
                Widget::make('total_posts')
                    ->label('Total Posts')
                    ->value(fn (): int => Post::count()),
            ])
    );
}

Panel::make($id) toma el identificador que aparecerá en la URL. Los métodos resources(), pages(), widgets() y customPages() son todos opcionales y se pueden combinar libremente. Pedir un panel que no está registrado responde 404.

Prefijo y paneles ​

Por defecto, el panel admin vive en /arrel/admin. Se puede cambiar con config/arrel.php:

php
return [
    'prefix' => 'arrel',
    'panels' => [],
    'middleware' => [],
    'authenticated_middleware' => [],
    'extra_scripts' => [],
];
ClaveDescripción
prefixPrimer segmento de la URL de la SPA: /{prefix}/{panel}. Puede ser cualquier cadena, o vacío.
panelsLista de identificadores de panel. Solo hace falta cuando el prefix está vacío.
middlewareMiddleware que se ejecuta antes de la autenticación. Véase Middleware.
authenticated_middlewareMiddleware que se ejecuta después de autenticar.
extra_scriptsURLs de scripts que se cargan en todos los paneles. Véase Scripts y hojas de estilo.

Un panel en la raíz del dominio necesita el prefijo vacío y la lista de sus identificadores, para que Arrel solo responda a estos y no se trague el resto de rutas de la aplicación:

php
'prefix' => '',
'panels' => ['admin'],

Con esto, el panel admin vive en /admin. La API continúa bajo /arrel/api/{panel}/..., sea cual sea el prefijo.

Para construir la URL de una página del panel desde tu código, usa Arrel\Support\PanelUrl:

php
use Arrel\Support\PanelUrl;

PanelUrl::to('admin', 'posts'); // "/arrel/admin/posts"

Marca propia ​

php
Panel::make('branded-demo')
    ->brandName('Branded Demo')
    ->logo('/images/branded-demo-logo.svg')
    ->colors(['brand' => '#2E7D32', 'brand-strong' => '#1B5E20'])
    ->resources([PostResource::class]);

brandName(), logo() y colors() se exponen al frontend vía window.Arrel (nombre de la marca en el <title> y en la interfaz, logo, y los tokens de color que sobrescriben la paleta por defecto). Sin brandName(), el panel se llama «Arrel».

Para tipografía propia, fonts() y stylesheet() funcionan igual que colors() pero para familias de letra:

php
Panel::make('branded-demo')
    ->fonts(['sans' => '"Poppins", sans-serif'])
    ->stylesheet('https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap');

fonts() sobrescribe los tokens de familia de letra (sans, display...) igual que colors() hace con los de color. stylesheet() carga una hoja de estilos externa (típicamente un enlace de Google Fonts) en el <head>, antes del propio CSS de Arrel, para que el @font-face ya esté registrado cuando haga falta.

Tema ​

php
Panel::make('admin')->theme('light');

theme() acepta auto (por defecto, sigue el sistema), light o dark. Cualquier otro valor lanza una InvalidArgumentException.

Pie de página ​

php
Panel::make('admin')->footer(fn (): array => [
    'text' => '© '.now()->year.' Acme',
    'links' => [
        ['label' => 'Cookies', 'url' => '/cookies'],
    ],
]);

footer() toma un array, o una closure que lo devuelve, con text y una lista de links. Se muestra bajo todas las páginas del panel.

Textos de la interfaz ​

Los textos de la interfaz (botones, estados, pantallas de seguridad...) están en inglés. Se pueden traducir con translations(), que toma un array de clave => texto o una closure que lo devuelve. La closure se evalúa en cada petición, así que puede depender del idioma activo:

php
Panel::make('admin')->translations(fn (): array => (array) __('arrel'));

Las claves que Arrel reconoce son:

save, cancel, remove, saved, loading, search_placeholder, searching, no_results, clear_filters, select_all, clear_selection, sign_in, sign_out, dashboard, email, password, invalid_credentials, something_went_wrong, any, new_prefix, edit_prefix, main_navigation, active, all_with_trashed, trashed_only, selected_suffix, columns, select_row_prefix, home, date_placeholder, today, clear, previous_month, next_month, two_factor_code, two_factor_code_hint, verify, use_another_account, security, two_factor_title, two_factor_on, two_factor_off, two_factor_required, two_factor_enable, two_factor_disable, two_factor_scan, two_factor_secret, two_factor_confirm, recovery_codes_title, recovery_codes_hint, continue, two_factor_disable_hint, verify_email_title, verify_email_hint, verify_email_send, verify_email_resend, verify_email_sent, verify_email_continue, verify_email_pending, notifications, mark_all_as_read y no_notifications.

Una clave que no declaras conserva el texto por defecto.

Los mensajes de error que genera el servidor (inicio de sesión, segundo factor, verificación del correo) y los nombres de las acciones incluidas son traducciones de Laravel bajo el namespace arrel::, y se pueden sobrescribir con archivos en lang/vendor/arrel.

Página de perfil y página de inicio ​

php
Panel::make('admin')
    ->profile(AccountPage::class)
    ->home(fn (): ?string => auth()->user()?->is_teacher ? PanelUrl::to('admin', 'custom/pass-list') : null);
  • profile() toma una Page y la convierte en el enlace de perfil del usuario en la barra lateral. Solo se muestra si canView() de la página es cierto.
  • home() toma una closure que devuelve la ruta donde debe aterrizar el usuario al abrir el panel (al cargarlo, o al venir del inicio de sesión o de la página de seguridad). Con null, o con un valor vacío, el usuario aterriza en el dashboard. La closure se evalúa por usuario, así que cada uno puede tener su página de inicio.

Scripts y hojas de estilo ​

Para cargar el JavaScript de tus páginas personalizadas hay dos vías:

php
Panel::make('admin')
    ->scripts(fn (): array => [Vite::asset('resources/js/arrel/pages.ts')])
    ->stylesheets(fn (): array => [Vite::asset('resources/css/arrel.css')]);

scripts() añade <script type="module"> después de arrel.js, y stylesheets() añade <link rel="stylesheet"> después del CSS de Arrel. Ambos toman un array de URLs o una closure que lo devuelve, y son por panel. config('arrel.extra_scripts') hace lo mismo para todos los paneles.

Autenticación y seguridad ​

Todo lo que hace referencia a Sanctum, verificación del correo, segundo factor y notificaciones está en Autenticación. La autorización y los límites de intentos, en Seguridad y autorización.

Middleware ​

config('arrel.middleware') se añade, en orden, antes del propio middleware de Arrel en todas las rutas web y API. Es el punto de entrada para cualquier cosa que tu aplicación necesite resolver antes de que Arrel consulte nada. El caso más habitual es el multi-tenancy:

php
// config/arrel.php
'middleware' => [
    \App\Http\Middleware\IdentifyTenant::class,
],

config('arrel.authenticated_middleware') se ejecuta después de que Arrel ya haya resuelto al usuario, y después de la verificación del correo y del segundo factor. middleware se ejecuta antes de que Sanctum resuelva la sesión, así que todavía no ve ningún usuario autenticado: cualquier cosa que necesite uno debe ir en authenticated_middleware.

php
// config/arrel.php
'authenticated_middleware' => [
    \App\Http\Middleware\RecordLastSeen::class,
],

El orden completo de las rutas de la API es:

php
[
    ...config('arrel.middleware'),
    ForceJsonResponse::class,
    EnsureFrontendRequestsAreStateful::class,
    'web',
    'auth:sanctum',
    EnsureEmailIsVerified::class,
    EnsureTwoFactorIsSetUp::class,
    ...config('arrel.authenticated_middleware'),
]

Los endpoints de las páginas personalizadas que escribas en tu aplicación deben usar esta misma pila, porque heredan las mismas garantías (véase Páginas personalizadas).

Siguiente paso ​

Con el Panel registrado, define tu primer Resource.