Add M3 error pages and the Markdown mail theme
tests / feature (8.5) (push) Successful in 1m8s
tests / browser (safari, webkit) (push) Has been cancelled
tests / browser (firefox, firefox) (push) Has been cancelled
tests / lint (push) Successful in 1m5s
tests / feature (8.4) (push) Successful in 1m11s
tests / browser (chrome, chromium) (push) Failing after 6m9s
tests / feature (8.5) (push) Successful in 1m8s
tests / browser (safari, webkit) (push) Has been cancelled
tests / browser (firefox, firefox) (push) Has been cancelled
tests / lint (push) Successful in 1m5s
tests / feature (8.4) (push) Successful in 1m11s
tests / browser (chrome, chromium) (push) Failing after 6m9s
Error pages for 401 to 503 in an error-view root appended to view.paths, with an inline fallback when the Vite build is missing, and a mail theme rendered from the application's scheme JSON, with opt-in header and message components. Completes Phase 9. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
This commit is contained in:
co-authored by
Claude Opus 5
parent
ab692b66bb
commit
8f174520eb
@@ -0,0 +1,10 @@
|
||||
{{-- Forbidden. A message the app passed along (`abort(403, '…')`, a policy's `Response::deny('…')`)
|
||||
is the sentence, as in the framework's own page; otherwise a general one. --}}
|
||||
|
||||
@extends('errors::minimal')
|
||||
|
||||
@section('title', __('Forbidden'))
|
||||
@section('code', '403')
|
||||
@section('shape', 'gem')
|
||||
@section('headline', __('You don’t have access'))
|
||||
@section('message', isset($exception) && $exception->getMessage() !== '' ? __($exception->getMessage()) : __('Your account isn’t allowed to open this page.'))
|
||||
@@ -0,0 +1,9 @@
|
||||
{{-- Not found. --}}
|
||||
|
||||
@extends('errors::minimal')
|
||||
|
||||
@section('title', __('Not Found'))
|
||||
@section('code', '404')
|
||||
@section('shape', 'cookie-9')
|
||||
@section('headline', __('Page not found'))
|
||||
@section('message', __('The page you’re looking for doesn’t exist or has moved.'))
|
||||
@@ -0,0 +1,16 @@
|
||||
{{-- Page expired: the form's CSRF token outlived the session. Reloading the page the form was on
|
||||
issues a new one, so that is the action — a link to it, never a reload of this response,
|
||||
which would post the stale form again. --}}
|
||||
|
||||
@extends('errors::minimal')
|
||||
|
||||
@section('title', __('Page Expired'))
|
||||
@section('code', '419')
|
||||
@section('shape', 'clover-4')
|
||||
@section('headline', __('This page has expired'))
|
||||
@section('message', __('It was open for a while. Refresh it, then try again.'))
|
||||
|
||||
@section('actions')
|
||||
<x-button :link="url()->previous()" :label="__('Refresh the page')" variant="filled" size="md" no-wire-navigate />
|
||||
<x-button :link="url('/')" :label="__('Go home')" variant="text" size="md" no-wire-navigate />
|
||||
@endsection
|
||||
@@ -0,0 +1,9 @@
|
||||
{{-- Too many requests. --}}
|
||||
|
||||
@extends('errors::minimal')
|
||||
|
||||
@section('title', __('Too Many Requests'))
|
||||
@section('code', '429')
|
||||
@section('shape', 'cookie-12')
|
||||
@section('headline', __('Slow down a little'))
|
||||
@section('message', __('There were too many requests in a short time. Wait a moment, then try again.'))
|
||||
@@ -0,0 +1,9 @@
|
||||
{{-- Server error. Nothing from the exception is shown: its message is for the log, not the visitor. --}}
|
||||
|
||||
@extends('errors::minimal')
|
||||
|
||||
@section('title', __('Server Error'))
|
||||
@section('code', '500')
|
||||
@section('shape', 'soft-burst')
|
||||
@section('headline', __('Something went wrong'))
|
||||
@section('message', __('An error on our side stopped this page from loading. Please try again in a moment.'))
|
||||
@@ -0,0 +1,22 @@
|
||||
{{-- Service unavailable, usually maintenance mode. A message the app passed (`abort(503, '…')`) is
|
||||
the sentence; maintenance mode's own "Service Unavailable" is not a message, so it is not.
|
||||
|
||||
`php artisan down --render="errors::503"` renders this once, in the console, with no
|
||||
`$exception` and no request — so the one action reloads whatever address the visitor is
|
||||
on, in the browser, rather than a URL decided here. --}}
|
||||
|
||||
@extends('errors::minimal')
|
||||
|
||||
@php
|
||||
$reason = isset($exception) ? $exception->getMessage() : '';
|
||||
@endphp
|
||||
|
||||
@section('title', __('Service Unavailable'))
|
||||
@section('code', '503')
|
||||
@section('shape', 'puffy')
|
||||
@section('headline', __('We’ll be right back'))
|
||||
@section('message', $reason !== '' && $reason !== 'Service Unavailable' ? __($reason) : __('We’re making some improvements. Please check back soon.'))
|
||||
|
||||
@section('actions')
|
||||
<x-button :label="__('Try again')" variant="filled" size="md" onclick="location.reload()" />
|
||||
@endsection
|
||||
@@ -0,0 +1,83 @@
|
||||
{{-- The layout every error page extends: this package's 403, 404, 419, 429, 500 and 503, and the
|
||||
framework's own 401 and 402, which extend `errors::minimal` and find this file before the
|
||||
framework's (the provider appends this folder's parent to `view.paths`).
|
||||
|
||||
Sections — the framework layout's `title`, `code` and `message`, plus three of its own, so
|
||||
either layout renders the other's pages:
|
||||
`title` the browser tab, followed by the app name;
|
||||
`code` the status, drawn in display type over the shape;
|
||||
`headline` what happened, in a few words (without it, `message` is the headline);
|
||||
`message` one sentence on what to do about it;
|
||||
`shape` an M3 Expressive shape name (`cookie-7` by default), in primary-container;
|
||||
`actions` the buttons; by default a filled Home and, when the visitor came from a page on
|
||||
the way here, a text Back.
|
||||
|
||||
The app's own Vite entries (`livewire-material.showcase.vite`) bring its scheme, font and
|
||||
utilities. But an error page is also what shows while a deploy has no build yet, so when
|
||||
those tags cannot be made the page brings a small stylesheet of its own: the app's scheme
|
||||
from its scheme data, drawn onto the `data-error-*` hooks. Keep the hooks when changing
|
||||
the markup. The shape turns once a minute, unless the visitor asks for reduced motion. --}}
|
||||
|
||||
@php
|
||||
$assets = \NoNameWeb\LivewireMaterial\Support\ErrorPage::assets();
|
||||
$back = \NoNameWeb\LivewireMaterial\Support\ErrorPage::backUrl();
|
||||
@endphp
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="robots" content="noindex" />
|
||||
|
||||
<title>@yield('title') · {{ config('app.name') }}</title>
|
||||
|
||||
<x-theme-script />
|
||||
|
||||
@if ($assets !== null)
|
||||
{{ $assets }}
|
||||
@else
|
||||
<style data-error-fallback>{{ \NoNameWeb\LivewireMaterial\Support\ErrorPage::fallbackStyles() }}</style>
|
||||
@endif
|
||||
|
||||
<style>
|
||||
@keyframes material-error-turn { to { transform: rotate(1turn); } }
|
||||
@media (prefers-reduced-motion: no-preference) { [data-error-shape] { animation: material-error-turn 60s linear infinite; } }
|
||||
</style>
|
||||
</head>
|
||||
<body class="min-h-dvh bg-surface font-sans text-on-surface antialiased">
|
||||
<main data-error-page class="mx-auto flex min-h-dvh max-w-xl flex-col items-center justify-center px-6 py-12 text-center">
|
||||
<div data-error-art class="relative grid size-48 shrink-0 place-items-center sm:size-60">
|
||||
<div data-error-shape class="absolute inset-0">
|
||||
<x-shape :name="trim($__env->yieldContent('shape', 'cookie-7'))" class="size-full text-primary-container" />
|
||||
</div>
|
||||
|
||||
<p data-error-code class="relative type-emphasized-display-lg text-on-primary-container tabular-nums">@yield('code')</p>
|
||||
</div>
|
||||
|
||||
<h1 data-error-headline class="mt-10 type-headline-md text-balance sm:type-headline-lg">
|
||||
@hasSection('headline')
|
||||
@yield('headline')
|
||||
@else
|
||||
@yield('message')
|
||||
@endif
|
||||
</h1>
|
||||
|
||||
@hasSection('headline')
|
||||
<p data-error-message class="mt-3 max-w-md type-body-lg text-balance text-on-surface-variant">@yield('message')</p>
|
||||
@endif
|
||||
|
||||
<div data-error-actions class="mt-10 flex flex-wrap items-center justify-center gap-3">
|
||||
@hasSection('actions')
|
||||
@yield('actions')
|
||||
@else
|
||||
<x-button :link="url('/')" :label="__('Go home')" variant="filled" size="md" no-wire-navigate />
|
||||
|
||||
@if ($back !== null)
|
||||
<x-button :link="$back" :label="__('Go back')" variant="text" size="md" no-wire-navigate />
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user