Files
Mark Gregory b738ff908b Professional searchable page library.
* Clear Live, Draft and Homepage indicators.
* Direct Edit page, Edit content and View page actions.
* Page editor divided into:
    * Page details
    * SEO and URL
    * Publishing
* Automatic, unique URL slugs.
* Publish and expiry scheduling.
* Page templates: Default, Landing and Full Width.
* Homepage replacement handled automatically.
* Current homepage protected from accidental deletion.
* Page duplication includes its complete content structure.
* Content composer now works from the actual page record and page ID.
* Clearer section-based terminology for editors.
* Reusable content remains available as an advanced content library rather than dominating the workflow.

The package contains the eight complete changed files, preserving their project paths. It is not a patch collection.
2026-07-28 13:35:06 +01:00

76 lines
5.3 KiB
PHP

<x-layouts.app :title="__('Pages')">
<div class="space-y-6">
<div class="flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between">
<div>
<p class="text-xs font-semibold uppercase tracking-[0.18em] text-zinc-500">Website</p>
<h1 class="mt-1 text-3xl font-semibold tracking-tight">Pages</h1>
<p class="mt-2 max-w-2xl text-sm text-zinc-500">Create, organise, compose and publish every page from one place.</p>
</div>
<div class="flex flex-wrap gap-2">
<flux:button :href="route('content.index')" variant="ghost">Content library</flux:button>
<flux:button :href="route('administration.pages.create')" variant="primary">New page</flux:button>
</div>
</div>
@if(session('status'))
<div class="rounded-xl border border-emerald-200 bg-emerald-50 p-4 text-sm text-emerald-800 dark:border-emerald-900 dark:bg-emerald-950 dark:text-emerald-200">{{ session('status') }}</div>
@endif
@if($errors->any())
<div class="rounded-xl border border-red-200 bg-red-50 p-4 text-sm text-red-800 dark:border-red-900 dark:bg-red-950 dark:text-red-200">{{ $errors->first() }}</div>
@endif
<form method="GET" class="grid gap-3 rounded-2xl border border-zinc-200 bg-white p-4 md:grid-cols-[minmax(15rem,1fr)_13rem_auto] dark:border-zinc-800 dark:bg-zinc-900">
<flux:input name="search" label="Search pages" value="{{ request('search') }}" placeholder="Title, URL or summary" />
<flux:select name="status" label="Publishing status">
<option value="">All pages</option>
<option value="draft" @selected(request('status') === 'draft')>Draft</option>
<option value="published" @selected(request('status') === 'published')>Published</option>
</flux:select>
<div class="flex items-end gap-2"><flux:button type="submit">Filter</flux:button><flux:button :href="route('administration.pages.index')" variant="ghost">Reset</flux:button></div>
</form>
<div class="grid gap-4 xl:grid-cols-2">
@forelse($records as $record)
@php
$isLive = $record->status === 'published' && (!$record->published_at || $record->published_at->isPast()) && (!$record->expires_at || $record->expires_at->isFuture());
@endphp
<article class="group rounded-2xl border border-zinc-200 bg-white p-5 shadow-sm transition hover:border-zinc-400 hover:shadow-md dark:border-zinc-800 dark:bg-zinc-900 dark:hover:border-zinc-600">
<div class="flex items-start justify-between gap-4">
<div class="min-w-0">
<div class="flex flex-wrap items-center gap-2">
<h2 class="truncate text-lg font-semibold">{{ $record->title }}</h2>
@if($record->is_home)<span class="rounded-full bg-blue-100 px-2.5 py-1 text-xs font-medium text-blue-700 dark:bg-blue-950 dark:text-blue-200">Homepage</span>@endif
<span class="rounded-full px-2.5 py-1 text-xs font-medium {{ $isLive ? 'bg-emerald-100 text-emerald-700 dark:bg-emerald-950 dark:text-emerald-200' : 'bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300' }}">{{ $isLive ? 'Live' : ucfirst($record->status) }}</span>
</div>
<p class="mt-1 truncate text-sm text-zinc-500">/{{ $record->slug }}</p>
</div>
<div class="rounded-xl bg-zinc-100 px-3 py-2 text-center dark:bg-zinc-800">
<div class="text-lg font-semibold">{{ $record->content_placements_count }}</div>
<div class="text-[10px] uppercase tracking-wide text-zinc-500">sections</div>
</div>
</div>
<p class="mt-4 min-h-10 text-sm leading-6 text-zinc-600 dark:text-zinc-300">{{ Str::limit($record->summary ?: 'No page summary has been added yet.', 150) }}</p>
<div class="mt-5 flex flex-wrap items-center justify-between gap-3 border-t border-zinc-100 pt-4 dark:border-zinc-800">
<div class="text-xs text-zinc-500">Updated {{ $record->updated_at?->diffForHumans() }} · {{ ucfirst($record->template ?? 'default') }}</div>
<div class="flex flex-wrap gap-2">
@if($record->status === 'published')<flux:button size="sm" :href="route('pages.public.show', $record->slug)" target="_blank" variant="ghost">View</flux:button>@endif
<flux:button size="sm" :href="route('content.pages.page.edit', $record)">Content</flux:button>
<flux:button size="sm" :href="route('administration.pages.edit', $record)" variant="primary">Edit page</flux:button>
</div>
</div>
</article>
@empty
<div class="col-span-full rounded-2xl border-2 border-dashed border-zinc-300 px-6 py-16 text-center dark:border-zinc-700">
<h2 class="text-lg font-semibold">No pages found</h2>
<p class="mt-2 text-sm text-zinc-500">Create your first page or clear the current filters.</p>
<flux:button class="mt-5" :href="route('administration.pages.create')" variant="primary">Create page</flux:button>
</div>
@endforelse
</div>
{{ $records->links() }}
</div>
</x-layouts.app>