Integrate article and page management into the Laravel admin dashboard
to serve as a headless CMS for the Next.js frontend (usher-site).
Backend:
- 7 migrations: article_categories, article_tags, articles, pivots, attachments, pages
- 5 models with relationships: Article, ArticleCategory, ArticleTag, ArticleAttachment, Page
- 4 admin controllers: articles (with publish/archive/pin), categories, tags, pages
- Admin views with EasyMDE markdown editor, multi-select categories/tags
- Navigation section "官網管理" in admin sidebar
API (v1):
- GET /api/v1/articles (filtered by type, category, tag, search; paginated)
- GET /api/v1/articles/{slug} (with related articles)
- GET /api/v1/categories
- GET /api/v1/pages/{slug} (with children)
- GET /api/v1/homepage (aggregated homepage data)
- Attachment download endpoint
- CORS configured for usher.org.tw, vercel.app, localhost:3000
Content migration:
- ImportHugoContent command: imports Hugo markdown files as articles/pages
- Successfully imported 27 articles, 17 categories, 11 tags, 9 pages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
121 lines
8.6 KiB
PHP
121 lines
8.6 KiB
PHP
<x-app-layout>
|
|
<x-slot name="header">
|
|
<div class="flex items-center justify-between">
|
|
<h2 class="text-xl font-semibold leading-tight text-gray-800 dark:text-gray-200">
|
|
官網頁面管理
|
|
</h2>
|
|
<a href="{{ route('admin.pages.create') }}" class="inline-flex items-center rounded-md border border-transparent bg-indigo-600 dark:bg-indigo-500 px-3 py-2 text-sm font-medium text-white hover:bg-indigo-700 dark:hover:bg-indigo-600">
|
|
+ 建立頁面
|
|
</a>
|
|
</div>
|
|
</x-slot>
|
|
|
|
<div class="py-12">
|
|
<div class="mx-auto max-w-7xl sm:px-6 lg:px-8 space-y-6">
|
|
@if (session('status'))
|
|
<div class="rounded-md bg-green-50 dark:bg-green-900/50 p-4">
|
|
<p class="text-sm font-medium text-green-800 dark:text-green-200">{{ session('status') }}</p>
|
|
</div>
|
|
@endif
|
|
|
|
@if (session('error'))
|
|
<div class="rounded-md bg-red-50 dark:bg-red-900/50 p-4">
|
|
<p class="text-sm font-medium text-red-800 dark:text-red-200">{{ session('error') }}</p>
|
|
</div>
|
|
@endif
|
|
|
|
<div class="bg-white dark:bg-gray-800 shadow overflow-hidden sm:rounded-lg">
|
|
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
<thead class="bg-gray-50 dark:bg-gray-700">
|
|
<tr>
|
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500 dark:text-gray-300">頁面</th>
|
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500 dark:text-gray-300">網址代碼</th>
|
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500 dark:text-gray-300">狀態</th>
|
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500 dark:text-gray-300">排序</th>
|
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500 dark:text-gray-300">建立者</th>
|
|
<th scope="col" class="px-6 py-3 text-right text-xs font-medium uppercase tracking-wider text-gray-500 dark:text-gray-300">操作</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
|
@forelse($pages as $page)
|
|
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
<a href="{{ route('admin.pages.show', $page) }}" class="hover:text-indigo-600 dark:hover:text-indigo-400">
|
|
{{ $page->title }}
|
|
</a>
|
|
</div>
|
|
@if($page->template)
|
|
<div class="text-xs text-gray-500 dark:text-gray-400">模板:{{ $page->template }}</div>
|
|
@endif
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400 font-mono">
|
|
{{ $page->slug }}
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
<span class="inline-flex rounded-full px-2 text-xs font-semibold leading-5
|
|
@if($page->status === 'draft') bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-300
|
|
@else bg-green-100 dark:bg-green-900/50 text-green-800 dark:text-green-300
|
|
@endif">
|
|
{{ $page->getStatusLabel() }}
|
|
</span>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
|
{{ $page->sort_order }}
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
|
{{ $page->creator->name ?? 'N/A' }}
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium space-x-2">
|
|
<a href="{{ route('admin.pages.show', $page) }}" class="text-indigo-600 dark:text-indigo-400 hover:text-indigo-900 dark:hover:text-indigo-300">查看</a>
|
|
<a href="{{ route('admin.pages.edit', $page) }}" class="text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-300">編輯</a>
|
|
</td>
|
|
</tr>
|
|
{{-- Child pages --}}
|
|
@foreach($page->children as $child)
|
|
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700 bg-gray-50/50 dark:bg-gray-800/50">
|
|
<td class="px-6 py-4 whitespace-nowrap pl-12">
|
|
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
|
<span class="text-gray-400 dark:text-gray-500 mr-1">└</span>
|
|
<a href="{{ route('admin.pages.show', $child) }}" class="hover:text-indigo-600 dark:hover:text-indigo-400">
|
|
{{ $child->title }}
|
|
</a>
|
|
</div>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400 font-mono">
|
|
{{ $child->slug }}
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
<span class="inline-flex rounded-full px-2 text-xs font-semibold leading-5
|
|
@if($child->status === 'draft') bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-300
|
|
@else bg-green-100 dark:bg-green-900/50 text-green-800 dark:text-green-300
|
|
@endif">
|
|
{{ $child->getStatusLabel() }}
|
|
</span>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
|
{{ $child->sort_order }}
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
|
{{ $child->creator->name ?? 'N/A' }}
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium space-x-2">
|
|
<a href="{{ route('admin.pages.show', $child) }}" class="text-indigo-600 dark:text-indigo-400 hover:text-indigo-900 dark:hover:text-indigo-300">查看</a>
|
|
<a href="{{ route('admin.pages.edit', $child) }}" class="text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-300">編輯</a>
|
|
</td>
|
|
</tr>
|
|
@endforeach
|
|
@empty
|
|
<tr>
|
|
<td colspan="6" class="px-6 py-12 text-center text-sm text-gray-500 dark:text-gray-400">
|
|
沒有找到頁面。<a href="{{ route('admin.pages.create') }}" class="text-indigo-600 dark:text-indigo-400 hover:underline">建立第一個頁面</a>
|
|
</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</x-app-layout>
|