Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions resources/lang/en/errors.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

return [
'back_to_dashboard' => 'Back to dashboard',
'try_again' => 'Try again',
'401' => [
'title' => 'Authentication required',
'message' => 'Sign in to continue to the admin area.',
],
'403' => [
'headline' => 'This section is restricted',
'title' => 'Access denied',
'message' => 'You do not have permission to access this section.',
'hint' => 'If you need access, contact an administrator.',
'eyebrow' => 'RESTRICTED ACCESS',
],
'404' => [
'headline' => 'We couldn\'t find that page',
'title' => 'Page not found',
'message' => 'We could not find the admin page you requested.',
'hint' => 'The address may be incorrect, or the page may have been moved.',
'eyebrow' => 'PAGE UNAVAILABLE',
],
'419' => [
'title' => 'Session expired',
'message' => 'Your session has expired. Refresh the page and try again.',
],
'429' => [
'title' => 'Too many requests',
'message' => 'You have made too many requests. Please try again shortly.',
],
'500' => [
'title' => 'Something went wrong',
'message' => 'An unexpected error occurred. Please try again later.',
],
'503' => [
'title' => 'Temporarily unavailable',
'message' => 'The admin area is temporarily unavailable. Please try again soon.',
],
];
40 changes: 40 additions & 0 deletions resources/lang/uk/errors.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

return [
'back_to_dashboard' => 'До панелі керування',
'try_again' => 'Спробувати знову',
'401' => [
'title' => 'Потрібна авторизація',
'message' => 'Увійдіть, щоб продовжити роботу з адмінпанеллю.',
],
'403' => [
'headline' => 'Доступ до розділу обмежено',
'title' => 'Доступ заборонено',
'message' => 'У вас немає прав для перегляду цього розділу.',
'hint' => 'Якщо вам потрібен доступ, зверніться до адміністратора.',
'eyebrow' => 'ОБМЕЖЕНИЙ ДОСТУП',
],
'404' => [
'headline' => 'Не вдалося знайти сторінку',
'title' => 'Сторінку не знайдено',
'message' => 'Запитану сторінку адмінпанелі не знайдено.',
'hint' => 'Можливо, адреса неправильна або сторінку було переміщено.',
'eyebrow' => 'СТОРІНКА НЕДОСТУПНА',
],
'419' => [
'title' => 'Сесію завершено',
'message' => 'Термін дії сесії минув. Оновіть сторінку та повторіть спробу.',
],
'429' => [
'title' => 'Забагато запитів',
'message' => 'Зачекайте трохи та повторіть спробу.',
],
'500' => [
'title' => 'Внутрішня помилка',
'message' => 'Сталася непередбачена помилка. Спробуйте пізніше.',
],
'503' => [
'title' => 'Сервіс тимчасово недоступний',
'message' => 'Адмінпанель тимчасово недоступна. Спробуйте пізніше.',
],
];
7 changes: 7 additions & 0 deletions resources/views/errors/401.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@php
$status = 401;
$title = __('lazy-admin::errors.401.title');
$message = __('lazy-admin::errors.401.message');
$icon = 'lock';
@endphp
@include('lazy::errors.partials.standalone')
14 changes: 14 additions & 0 deletions resources/views/errors/403.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
@php
$status = 403;
$title = __('lazy-admin::errors.403.title');
$headline = __('lazy-admin::errors.403.headline');
$message = __('lazy-admin::errors.403.message');
$hint = __('lazy-admin::errors.403.hint');
$eyebrow = __('lazy-admin::errors.403.eyebrow');
$icon = 'shield';
@endphp
@if($useAdminLayout)
@include('lazy::errors.partials.admin-layout')
@else
@include('lazy::errors.partials.standalone')
@endif
14 changes: 14 additions & 0 deletions resources/views/errors/404.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
@php
$status = 404;
$title = __('lazy-admin::errors.404.title');
$headline = __('lazy-admin::errors.404.headline');
$message = __('lazy-admin::errors.404.message');
$hint = __('lazy-admin::errors.404.hint');
$eyebrow = __('lazy-admin::errors.404.eyebrow');
$icon = 'search';
@endphp
@if($useAdminLayout)
@include('lazy::errors.partials.admin-layout')
@else
@include('lazy::errors.partials.standalone')
@endif
7 changes: 7 additions & 0 deletions resources/views/errors/419.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@php
$status = 419;
$title = __('lazy-admin::errors.419.title');
$message = __('lazy-admin::errors.419.message');
$icon = 'clock';
@endphp
@include('lazy::errors.partials.standalone')
7 changes: 7 additions & 0 deletions resources/views/errors/429.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@php
$status = 429;
$title = __('lazy-admin::errors.429.title');
$message = __('lazy-admin::errors.429.message');
$icon = 'clock';
@endphp
@include('lazy::errors.partials.standalone')
7 changes: 7 additions & 0 deletions resources/views/errors/500.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@php
$status = 500;
$title = __('lazy-admin::errors.500.title');
$message = __('lazy-admin::errors.500.message');
$icon = 'alert';
@endphp
@include('lazy::errors.partials.standalone')
7 changes: 7 additions & 0 deletions resources/views/errors/503.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@php
$status = 503;
$title = __('lazy-admin::errors.503.title');
$message = __('lazy-admin::errors.503.message');
$icon = 'alert';
@endphp
@include('lazy::errors.partials.standalone')
78 changes: 2 additions & 76 deletions resources/views/errors/page.blade.php
Original file line number Diff line number Diff line change
@@ -1,76 +1,2 @@
{{-- Only authenticated 403/404 responses may render the full admin shell. --}}
@if(! $useAdminLayout)
@php
$pages = [
401 => ['title' => 'Authentication required', 'text' => 'Sign in to continue to the admin area.', 'icon' => 'lock'],
403 => ['title' => 'Access denied', 'text' => 'You do not have permission to view this page.', 'icon' => 'shield'],
404 => ['title' => 'Page not found', 'text' => 'This admin page may have moved, or the address may be incorrect.', 'icon' => 'search'],
419 => ['title' => 'Session expired', 'text' => 'Your session has expired. Refresh the page and try again.', 'icon' => 'clock'],
429 => ['title' => 'Too many requests', 'text' => 'You have made too many requests. Please try again shortly.', 'icon' => 'clock'],
500 => ['title' => 'Something went wrong', 'text' => 'An unexpected error occurred. Please try again later.', 'icon' => 'alert'],
503 => ['title' => 'Temporarily unavailable', 'text' => 'The admin area is temporarily unavailable. Please try again soon.', 'icon' => 'alert'],
];
$page = $pages[$status] ?? $pages[500];
@endphp
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, nofollow">
<title>{{ $status }} · {{ $page['title'] }} · {{ config('app.name', 'Lazy Admin') }}</title>
<style>
/* Self-contained DaisyUI-inspired palette for error handling without assets. */
:root { color-scheme: light; font-family: ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif;
--base-100:#fff;--base-200:#f5f6f9;--base-300:#e3e6ed;--content:#222737;--muted:#687385;--primary:#4f46e5;--primary-content:#fff; }
@media(prefers-color-scheme:dark){:root{color-scheme:dark;--base-100:#1a2030;--base-200:#121827;--base-300:#30394b;--content:#eef0f6;--muted:#a6adbd;--primary:#a5b4fc;--primary-content:#161b30}}
*{box-sizing:border-box}body{min-height:100vh;min-height:100dvh;margin:0;display:flex;flex-direction:column;background:var(--base-200);color:var(--content)}
.topbar{height:4rem;display:flex;align-items:center;gap:.7rem;padding:0 clamp(1rem,3vw,2rem);border-bottom:1px solid var(--base-300);font-size:.875rem;font-weight:600}
.brand{display:grid;place-items:center;width:2.3rem;height:2.3rem;border-radius:.65rem;background:var(--primary);color:var(--primary-content)}
.brand svg{width:1.4rem;height:1.4rem}.subtle{color:var(--muted);font-weight:400}
main{flex:1;display:grid;place-items:center;padding:2rem 1rem}
.card{width:min(100%,37rem);padding:clamp(1.5rem,6vw,3.25rem);background:var(--base-100);border:1px solid var(--base-300);border-radius:1rem;text-align:center;box-shadow:0 8px 30px #00000008}
.symbol{width:4.5rem;height:4.5rem;margin:0 auto 1.5rem;border-radius:1rem;display:grid;place-items:center;background:var(--base-200);border:1px solid var(--base-300);color:var(--primary)}
.symbol svg{width:2rem;height:2rem;stroke:currentColor;fill:none;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.status{font-size:clamp(4.25rem,14vw,6rem);font-weight:800;letter-spacing:-.075em;line-height:1;color:var(--primary)}
h1{font-size:clamp(1.4rem,4vw,1.875rem);letter-spacing:-.04em;margin:1.25rem 0 .75rem}
p{color:var(--muted);line-height:1.65;max-width:26rem;margin:0 auto 1.75rem;font-size:.925rem}
.actions{display:flex;flex-wrap:wrap;justify-content:center;gap:.75rem}
.btn{appearance:none;display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;padding:.65rem 1.1rem;border:1px solid var(--base-300);border-radius:.5rem;background:var(--base-100);color:var(--content);text-decoration:none;font:inherit;font-size:.875rem;font-weight:600;cursor:pointer}
.btn:hover{background:var(--base-200)}.btn-primary{background:var(--primary);border-color:var(--primary);color:var(--primary-content)}.btn-primary:hover{filter:brightness(.92);background:var(--primary)}
.btn:focus-visible{outline:2px solid var(--primary);outline-offset:3px}
footer{text-align:center;color:var(--muted);padding:1rem;font-size:.75rem}
</style>
</head>
<body>
<header class="topbar"><div class="brand" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2 21 7v10l-9 5-9-5V7l9-5Z"/><path d="m8 15 4-8 4 8M9.5 12h5"/></svg></div><span>{{ config('app.name', 'Lazy Admin') }}</span><span class="subtle">/ Admin</span></header>
<main><section class="card" aria-labelledby="error-title">
<div class="symbol" aria-hidden="true">
@if($page['icon'] === 'search')
<svg viewBox="0 0 24 24"><circle cx="10.5" cy="10.5" r="6.5"/><path d="m16 16 5 5M8 9h.01"/></svg>
@elseif($page['icon'] === 'lock')
<svg viewBox="0 0 24 24"><rect x="5" y="10" width="14" height="11" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></svg>
@elseif($page['icon'] === 'shield')
<svg viewBox="0 0 24 24"><path d="M12 2 20 6v6c0 5-3.5 8-8 10-4.5-2-8-5-8-10V6l8-4Z"/><path d="m9 12 2 2 4-4"/></svg>
@elseif($page['icon'] === 'clock')
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg>
@else
<svg viewBox="0 0 24 24"><path d="M12 3 2 21h20L12 3Z"/><path d="M12 9v5M12 18h.01"/></svg>
@endif
</div>
<div class="status">{{ $status }}</div>
<h1 id="error-title">{{ $page['title'] }}</h1>
<p>{{ $page['text'] }}</p>
<div class="actions">
<a class="btn btn-primary" href="{{ $homeUrl }}">Back to dashboard</a>
<button class="btn" type="button" onclick="window.location.reload()">Try again</button>
</div>
</section>
</main>
<footer>Lazy Admin · {{ $status }}</footer>
</body>
</html>

@else
@include('lazy::errors.admin-shell')
@endif
{{-- Explicit per-status views are independently publishable and customizable. --}}
@include('lazy::errors.'.(string) $status)
31 changes: 31 additions & 0 deletions resources/views/errors/partials/admin-layout.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<x-lazy-layout :title="$title" :routes="['create' => null, 'show' => null, 'edit' => null, 'destroy' => null]">
<section class="mx-auto w-full max-w-5xl py-6 sm:py-10" aria-labelledby="admin-error-title">
<div class="grid overflow-hidden rounded-xl border border-base-300 bg-base-100 shadow-sm lg:grid-cols-[minmax(0,1fr)_minmax(0,0.8fr)]">
<div class="flex flex-col justify-center p-7 sm:p-10 lg:p-14">
<div class="mb-7 flex items-center gap-3">
<span class="flex size-10 items-center justify-center rounded-lg bg-primary/10 text-primary">
@if($status === 404)
<svg xmlns="http://www.w3.org/2000/svg" class="size-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><circle cx="11" cy="11" r="7"/><path d="m16 16 5 5M8 11h6"/></svg>
@else
<svg xmlns="http://www.w3.org/2000/svg" class="size-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><path d="M12 2 20 6v6c0 5-3.5 8-8 10-4.5-2-8-5-8-10V6l8-4Z"/><path d="M12 8v5m0 4h.01"/></svg>
@endif
</span>
<span class="text-xs font-bold tracking-widest text-base-content/60">{{ $eyebrow }}</span>
</div>
<h2 id="admin-error-title" class="text-3xl font-bold tracking-tight text-base-content sm:text-4xl">{{ $headline }}</h2>
<p class="mt-4 text-base leading-7 text-base-content/70">{{ $message }}</p>
<p class="mt-2 text-sm leading-6 text-base-content/50">{{ $hint }}</p>
<div class="mt-8 flex flex-wrap items-center gap-3">
<x-lazy-btn :href="$homeUrl" primary :label="__('lazy-admin::errors.back_to_dashboard')" />
<x-lazy-btn type="button" outline :label="__('lazy-admin::errors.try_again')" onclick="window.location.reload()" />
</div>
</div>
<div class="flex min-h-48 flex-col items-center justify-center border-t border-base-300 bg-base-200/60 px-6 py-8 lg:min-h-80 lg:border-l lg:border-t-0">
<div class="flex size-20 items-center justify-center rounded-2xl border border-base-300 bg-base-100 text-primary shadow-sm">
<svg xmlns="http://www.w3.org/2000/svg" class="size-9" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 4h16v16H4zM4 9h16M9 9v11"/><path d="m12 13 2 2 4-4" opacity=".45"/></svg>
</div>
<span class="mt-5 text-7xl font-extrabold tracking-tight text-primary sm:text-8xl" aria-label="HTTP {{ $status }}">{{ $status }}</span>
</div>
</div>
</section>
</x-lazy-layout>
58 changes: 58 additions & 0 deletions resources/views/errors/partials/standalone.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, nofollow">
<title>{{ $status }} · {{ $title }} · {{ config('app.name', 'Lazy Admin') }}</title>
<style>
/* Self-contained DaisyUI-inspired palette for error handling without assets. */
:root { color-scheme: light; font-family: ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif;
--base-100:#fff;--base-200:#f5f6f9;--base-300:#e3e6ed;--content:#222737;--muted:#687385;--primary:#4f46e5;--primary-content:#fff; }
@media(prefers-color-scheme:dark){:root{color-scheme:dark;--base-100:#1a2030;--base-200:#121827;--base-300:#30394b;--content:#eef0f6;--muted:#a6adbd;--primary:#a5b4fc;--primary-content:#161b30}}
*{box-sizing:border-box}body{min-height:100vh;min-height:100dvh;margin:0;display:flex;flex-direction:column;background:var(--base-200);color:var(--content)}
.topbar{height:4rem;display:flex;align-items:center;gap:.7rem;padding:0 clamp(1rem,3vw,2rem);border-bottom:1px solid var(--base-300);font-size:.875rem;font-weight:600}
.brand{display:grid;place-items:center;width:2.3rem;height:2.3rem;border-radius:.65rem;background:var(--primary);color:var(--primary-content)}
.brand svg{width:1.4rem;height:1.4rem}.subtle{color:var(--muted);font-weight:400}
main{flex:1;display:grid;place-items:center;padding:2rem 1rem}
.card{width:min(100%,37rem);padding:clamp(1.5rem,6vw,3.25rem);background:var(--base-100);border:1px solid var(--base-300);border-radius:1rem;text-align:center;box-shadow:0 8px 30px #00000008}
.symbol{width:4.5rem;height:4.5rem;margin:0 auto 1.5rem;border-radius:1rem;display:grid;place-items:center;background:var(--base-200);border:1px solid var(--base-300);color:var(--primary)}
.symbol svg{width:2rem;height:2rem;stroke:currentColor;fill:none;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.status{font-size:clamp(4.25rem,14vw,6rem);font-weight:800;letter-spacing:-.075em;line-height:1;color:var(--primary)}
h1{font-size:clamp(1.4rem,4vw,1.875rem);letter-spacing:-.04em;margin:1.25rem 0 .75rem}
p{color:var(--muted);line-height:1.65;max-width:26rem;margin:0 auto 1.75rem;font-size:.925rem}
.actions{display:flex;flex-wrap:wrap;justify-content:center;gap:.75rem}
.btn{appearance:none;display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;padding:.65rem 1.1rem;border:1px solid var(--base-300);border-radius:.5rem;background:var(--base-100);color:var(--content);text-decoration:none;font:inherit;font-size:.875rem;font-weight:600;cursor:pointer}
.btn:hover{background:var(--base-200)}.btn-primary{background:var(--primary);border-color:var(--primary);color:var(--primary-content)}.btn-primary:hover{filter:brightness(.92);background:var(--primary)}
.btn:focus-visible{outline:2px solid var(--primary);outline-offset:3px}
footer{text-align:center;color:var(--muted);padding:1rem;font-size:.75rem}
</style>
</head>
<body>
<header class="topbar"><div class="brand" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2 21 7v10l-9 5-9-5V7l9-5Z"/><path d="m8 15 4-8 4 8M9.5 12h5"/></svg></div><span>{{ config('app.name', 'Lazy Admin') }}</span><span class="subtle">/ Admin</span></header>
<main><section class="card" aria-labelledby="error-title">
<div class="symbol" aria-hidden="true">
@if($icon === 'search')
<svg viewBox="0 0 24 24"><circle cx="10.5" cy="10.5" r="6.5"/><path d="m16 16 5 5M8 9h.01"/></svg>
@elseif($icon === 'lock')
<svg viewBox="0 0 24 24"><rect x="5" y="10" width="14" height="11" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></svg>
@elseif($icon === 'shield')
<svg viewBox="0 0 24 24"><path d="M12 2 20 6v6c0 5-3.5 8-8 10-4.5-2-8-5-8-10V6l8-4Z"/><path d="m9 12 2 2 4-4"/></svg>
@elseif($icon === 'clock')
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg>
@else
<svg viewBox="0 0 24 24"><path d="M12 3 2 21h20L12 3Z"/><path d="M12 9v5M12 18h.01"/></svg>
@endif
</div>
<div class="status">{{ $status }}</div>
<h1 id="error-title">{{ $title }}</h1>
<p>{{ $message }}</p>
<div class="actions">
<a class="btn btn-primary" href="{{ $homeUrl }}">{{ __('lazy-admin::errors.back_to_dashboard') }}</a>
<button class="btn" type="button" onclick="window.location.reload()">{{ __('lazy-admin::errors.try_again') }}</button>
</div>
</section>
</main>
<footer>Lazy Admin · {{ $status }}</footer>
</body>
</html>
Loading
Loading