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.
This commit is contained in:
@@ -1,5 +1,75 @@
|
||||
<x-layouts.app :title="__('Pages')">
|
||||
<div class="space-y-6"><div class="flex items-center justify-between"><div><h1 class="text-2xl font-semibold">Pages</h1><p class="text-sm text-zinc-500">Manage pages content.</p></div><a href="{{ route('administration.pages.create') }}" class="rounded-lg bg-zinc-900 px-4 py-2 text-sm text-white dark:bg-white dark:text-zinc-900">Add new</a></div>
|
||||
@if(session('status'))<div class="rounded-lg bg-emerald-50 p-3 text-emerald-800">{{ session('status') }}</div>@endif
|
||||
<div class="overflow-hidden rounded-xl border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900"><table class="w-full text-sm"><thead class="bg-zinc-50 dark:bg-zinc-800"><tr><th class="px-4 py-3 text-left">Title</th><th class="px-4 py-3 text-left">Slug</th><th class="px-4 py-3 text-left">Summary</th><th class="px-4 py-3 text-right">Actions</th></tr></thead><tbody class="divide-y divide-zinc-200 dark:divide-zinc-800">@forelse($records as $record)<tr><td class="px-4 py-3">{{ is_array($record->title) ? json_encode($record->title) : Str::limit((string) $record->title, 70) }}</td><td class="px-4 py-3">{{ is_array($record->slug) ? json_encode($record->slug) : Str::limit((string) $record->slug, 70) }}</td><td class="px-4 py-3">{{ is_array($record->summary) ? json_encode($record->summary) : Str::limit((string) $record->summary, 70) }}</td><td class="px-4 py-3 text-right"><a class="mr-3 underline" href="{{ route('administration.pages.edit', $record) }}">Edit</a><form class="inline" method="POST" action="{{ route('administration.pages.destroy', $record) }}">@csrf @method('DELETE')<button class="text-red-600" onclick="return confirm('Delete this record?')">Delete</button></form></td></tr>@empty<tr><td colspan="4" class="px-4 py-10 text-center text-zinc-500">No records yet.</td></tr>@endforelse</tbody></table></div>{{ $records->links() }}</div>
|
||||
<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>
|
||||
|
||||
Reference in New Issue
Block a user