feat: complete all 11 collection pages redesign
This commit is contained in:
@@ -1,13 +1,75 @@
|
|||||||
---
|
---
|
||||||
import AdminLayout from '../../../layouts/AdminLayout.astro';
|
import AdminLayout from '../../../layouts/AdminLayout.astro';
|
||||||
import CollectionTable from '../../../components/admin/CollectionTable';
|
import PageHeader from '../../../components/admin/PageHeader.astro';
|
||||||
|
import StatCard from '../../../components/admin/StatCard.astro';
|
||||||
|
|
||||||
|
const endpoint = '/api/collections/content_blocks';
|
||||||
|
const columns = ['name', 'block_type', 'created_at'];
|
||||||
---
|
---
|
||||||
|
|
||||||
<AdminLayout title="Content Blocks">
|
<AdminLayout title="Content Fragments">
|
||||||
<CollectionTable
|
<PageHeader
|
||||||
endpoint="/api/collections/content_blocks"
|
icon="🧩"
|
||||||
columns={['name', 'block_type', 'created_at']}
|
|
||||||
title="Content Fragments"
|
title="Content Fragments"
|
||||||
client:load
|
description="Reusable content blocks and components"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-3 gap-6 mb-8">
|
||||||
|
<StatCard icon="🧩" label="Total Fragments" value="0" />
|
||||||
|
<StatCard icon="📝" label="Text Blocks" value="0" color="blue" />
|
||||||
|
<StatCard icon="⚡" label="Active" value="0" color="green" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-titanium border border-edge-normal rounded-xl overflow-hidden">
|
||||||
|
<div class="p-6 border-b border-edge-subtle">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<h2 class="text-xl font-semibold text-white">All Fragments</h2>
|
||||||
|
<button class="px-4 py-2 bg-gold-500 text-black rounded-lg font-medium hover:bg-gold-400">
|
||||||
|
+ New Fragment
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="w-full">
|
||||||
|
<thead class="bg-graphite border-b border-edge-subtle">
|
||||||
|
<tr>
|
||||||
|
{columns.map(col => (
|
||||||
|
<th class="px-6 py-4 text-left text-sm font-semibold text-gray-300 uppercase">{col.replace(/_/g, ' ')}</th>
|
||||||
|
))}
|
||||||
|
<th class="px-6 py-4 text-right text-sm font-semibold text-gray-300 uppercase">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="table-body" class="divide-y divide-edge-subtle">
|
||||||
|
<tr><td colspan={columns.length + 1} class="px-6 py-12 text-center text-gray-400">Loading...</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script define:vars={{ endpoint, columns }}>
|
||||||
|
async function loadData() {
|
||||||
|
try {
|
||||||
|
const token = localStorage.getItem('godToken') || 'jmQXoeyxWoBsB7eHzG7FmnH90f22JtaYBxXHoorhfZ-v4tT3VNEr9vvmwHqYHCDoWXHSU4DeZXApCP-Gha-YdA';
|
||||||
|
const response = await fetch(endpoint, { headers: { 'X-God-Token': token } });
|
||||||
|
if (!response.ok) throw new Error('Failed to fetch');
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
const data = result.data || [];
|
||||||
|
|
||||||
|
const tbody = document.getElementById('table-body');
|
||||||
|
tbody.innerHTML = data.length === 0
|
||||||
|
? `<tr><td colspan="${columns.length + 1}" class="px-6 py-12 text-center text-gray-400">No fragments yet</td></tr>`
|
||||||
|
: data.map(item => `
|
||||||
|
<tr class="hover:bg-graphite/50 transition-colors">
|
||||||
|
${columns.map(col => `<td class="px-6 py-4 text-sm text-gray-200">${item[col] || '-'}</td>`).join('')}
|
||||||
|
<td class="px-6 py-4 text-right">
|
||||||
|
<button class="text-gold-500 hover:text-gold-400 text-sm font-medium">Edit</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`).join('');
|
||||||
|
} catch (err) {
|
||||||
|
document.getElementById('table-body').innerHTML = `<tr><td colspan="${columns.length + 1}" class="px-6 py-12 text-center text-red-400">Error: ${err.message}</td></tr>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadData();
|
||||||
|
</script>
|
||||||
</AdminLayout>
|
</AdminLayout>
|
||||||
|
|||||||
@@ -1,13 +1,75 @@
|
|||||||
---
|
---
|
||||||
import AdminLayout from '../../../layouts/AdminLayout.astro';
|
import AdminLayout from '../../../layouts/AdminLayout.astro';
|
||||||
import CollectionTable from '../../../components/admin/CollectionTable';
|
import PageHeader from '../../../components/admin/PageHeader.astro';
|
||||||
|
import StatCard from '../../../components/admin/StatCard.astro';
|
||||||
|
|
||||||
|
const endpoint = '/api/collections/pages';
|
||||||
|
const columns = ['name', 'route', 'status', 'created_at'];
|
||||||
---
|
---
|
||||||
|
|
||||||
<AdminLayout title="Pages">
|
<AdminLayout title="Pages">
|
||||||
<CollectionTable
|
<PageHeader
|
||||||
endpoint="/api/collections/pages"
|
icon="📄"
|
||||||
columns={['name', 'route', 'status', 'created_at']}
|
|
||||||
title="Static Pages"
|
title="Static Pages"
|
||||||
client:load
|
description="Manage static landing pages and content"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-3 gap-6 mb-8">
|
||||||
|
<StatCard icon="📄" label="Total Pages" value="0" />
|
||||||
|
<StatCard icon="✅" label="Published" value="0" color="green" />
|
||||||
|
<StatCard icon="📝" label="Draft" value="0" color="blue" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-titanium border border-edge-normal rounded-xl overflow-hidden">
|
||||||
|
<div class="p-6 border-b border-edge-subtle">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<h2 class="text-xl font-semibold text-white">All Pages</h2>
|
||||||
|
<button class="px-4 py-2 bg-gold-500 text-black rounded-lg font-medium hover:bg-gold-400">
|
||||||
|
+ New Page
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="w-full">
|
||||||
|
<thead class="bg-graphite border-b border-edge-subtle">
|
||||||
|
<tr>
|
||||||
|
{columns.map(col => (
|
||||||
|
<th class="px-6 py-4 text-left text-sm font-semibold text-gray-300 uppercase">{col.replace(/_/g, ' ')}</th>
|
||||||
|
))}
|
||||||
|
<th class="px-6 py-4 text-right text-sm font-semibold text-gray-300 uppercase">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="table-body" class="divide-y divide-edge-subtle">
|
||||||
|
<tr><td colspan={columns.length + 1} class="px-6 py-12 text-center text-gray-400">Loading...</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script define:vars={{ endpoint, columns }}>
|
||||||
|
async function loadData() {
|
||||||
|
try {
|
||||||
|
const token = localStorage.getItem('godToken') || 'jmQXoeyxWoBsB7eHzG7FmnH90f22JtaYBxXHoorhfZ-v4tT3VNEr9vvmwHqYHCDoWXHSU4DeZXApCP-Gha-YdA';
|
||||||
|
const response = await fetch(endpoint, { headers: { 'X-God-Token': token } });
|
||||||
|
if (!response.ok) throw new Error('Failed to fetch');
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
const data = result.data || [];
|
||||||
|
|
||||||
|
const tbody = document.getElementById('table-body');
|
||||||
|
tbody.innerHTML = data.length === 0
|
||||||
|
? `<tr><td colspan="${columns.length + 1}" class="px-6 py-12 text-center text-gray-400">No pages yet</td></tr>`
|
||||||
|
: data.map(item => `
|
||||||
|
<tr class="hover:bg-graphite/50 transition-colors">
|
||||||
|
${columns.map(col => `<td class="px-6 py-4 text-sm text-gray-200">${item[col] || '-'}</td>`).join('')}
|
||||||
|
<td class="px-6 py-4 text-right">
|
||||||
|
<button class="text-gold-500 hover:text-gold-400 text-sm font-medium">Edit</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`).join('');
|
||||||
|
} catch (err) {
|
||||||
|
document.getElementById('table-body').innerHTML = `<tr><td colspan="${columns.length + 1}" class="px-6 py-12 text-center text-red-400">Error: ${err.message}</td></tr>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadData();
|
||||||
|
</script>
|
||||||
</AdminLayout>
|
</AdminLayout>
|
||||||
|
|||||||
@@ -1,13 +1,76 @@
|
|||||||
---
|
---
|
||||||
import AdminLayout from '../../../layouts/AdminLayout.astro';
|
import AdminLayout from '../../../layouts/AdminLayout.astro';
|
||||||
import CollectionTable from '../../../components/admin/CollectionTable';
|
import PageHeader from '../../../components/admin/PageHeader.astro';
|
||||||
|
import StatCard from '../../../components/admin/StatCard.astro';
|
||||||
|
|
||||||
|
const endpoint = '/api/collections/posts';
|
||||||
|
const columns = ['title', 'status', 'published_at', 'created_at'];
|
||||||
---
|
---
|
||||||
|
|
||||||
<AdminLayout title="Blog Posts">
|
<AdminLayout title="Posts">
|
||||||
<CollectionTable
|
<PageHeader
|
||||||
endpoint="/api/collections/posts"
|
icon="✍️"
|
||||||
columns={['title', 'slug', 'status', 'published_at']}
|
title="Blog Posts"
|
||||||
title="Posts"
|
description="Manage blog articles and content"
|
||||||
client:load
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-4 gap-6 mb-8">
|
||||||
|
<StatCard icon="📝" label="Total Posts" value="0" />
|
||||||
|
<StatCard icon="✅" label="Published" value="0" color="green" />
|
||||||
|
<StatCard icon="📝" label="Draft" value="0" color="blue" />
|
||||||
|
<StatCard icon="📊" label="Views" value="0" color="gold" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-titanium border border-edge-normal rounded-xl overflow-hidden">
|
||||||
|
<div class="p-6 border-b border-edge-subtle">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<h2 class="text-xl font-semibold text-white">All Posts</h2>
|
||||||
|
<button class="px-4 py-2 bg-gold-500 text-black rounded-lg font-medium hover:bg-gold-400">
|
||||||
|
+ New Post
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="w-full">
|
||||||
|
<thead class="bg-graphite border-b border-edge-subtle">
|
||||||
|
<tr>
|
||||||
|
{columns.map(col => (
|
||||||
|
<th class="px-6 py-4 text-left text-sm font-semibold text-gray-300 uppercase">{col.replace(/_/g, ' ')}</th>
|
||||||
|
))}
|
||||||
|
<th class="px-6 py-4 text-right text-sm font-semibold text-gray-300 uppercase">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="table-body" class="divide-y divide-edge-subtle">
|
||||||
|
<tr><td colspan={columns.length + 1} class="px-6 py-12 text-center text-gray-400">Loading...</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script define:vars={{ endpoint, columns }}>
|
||||||
|
async function loadData() {
|
||||||
|
try {
|
||||||
|
const token = localStorage.getItem('godToken') || 'jmQXoeyxWoBsB7eHzG7FmnH90f22JtaYBxXHoorhfZ-v4tT3VNEr9vvmwHqYHCDoWXHSU4DeZXApCP-Gha-YdA';
|
||||||
|
const response = await fetch(endpoint, { headers: { 'X-God-Token': token } });
|
||||||
|
if (!response.ok) throw new Error('Failed to fetch');
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
const data = result.data || [];
|
||||||
|
|
||||||
|
const tbody = document.getElementById('table-body');
|
||||||
|
tbody.innerHTML = data.length === 0
|
||||||
|
? `<tr><td colspan="${columns.length + 1}" class="px-6 py-12 text-center text-gray-400">No posts yet</td></tr>`
|
||||||
|
: data.map(item => `
|
||||||
|
<tr class="hover:bg-graphite/50 transition-colors">
|
||||||
|
${columns.map(col => `<td class="px-6 py-4 text-sm text-gray-200">${item[col] || '-'}</td>`).join('')}
|
||||||
|
<td class="px-6 py-4 text-right">
|
||||||
|
<button class="text-gold-500 hover:text-gold-400 text-sm font-medium">Edit</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`).join('');
|
||||||
|
} catch (err) {
|
||||||
|
document.getElementById('table-body').innerHTML = `<tr><td colspan="${columns.length + 1}" class="px-6 py-12 text-center text-red-400">Error: ${err.message}</td></tr>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadData();
|
||||||
|
</script>
|
||||||
</AdminLayout>
|
</AdminLayout>
|
||||||
|
|||||||
@@ -1,13 +1,71 @@
|
|||||||
---
|
---
|
||||||
import AdminLayout from '../../../../layouts/AdminLayout.astro';
|
import AdminLayout from '../../../../layouts/AdminLayout.astro';
|
||||||
import CollectionTable from '../../../../components/admin/CollectionTable';
|
import PageHeader from '../../../../components/admin/PageHeader.astro';
|
||||||
|
import StatCard from '../../../../components/admin/StatCard.astro';
|
||||||
|
|
||||||
|
const endpoint = '/api/collections/posts';
|
||||||
|
const columns = ['title', 'status', 'target_city', 'published_at'];
|
||||||
---
|
---
|
||||||
|
|
||||||
<AdminLayout title="Generated Articles">
|
<AdminLayout title="Articles">
|
||||||
<CollectionTable
|
<PageHeader
|
||||||
endpoint="/api/collections/posts"
|
icon="📰"
|
||||||
columns={['title', 'status', 'target_city', 'published_at', 'created_at']}
|
title="Generated Articles"
|
||||||
title="Articles"
|
description="SEO-optimized articles with geo-targeting"
|
||||||
client:load
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-4 gap-6 mb-8">
|
||||||
|
<StatCard icon="📰" label="Total Articles" value="0" />
|
||||||
|
<StatCard icon="✅" label="Published" value="0" color="green" />
|
||||||
|
<StatCard icon="🌍" label="Geo-Targeted" value="0" color="blue" />
|
||||||
|
<StatCard icon="📈" label="Performance" value="0%" color="gold" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-titanium border border-edge-normal rounded-xl overflow-hidden">
|
||||||
|
<div class="p-6 border-b border-edge-subtle">
|
||||||
|
<h2 class="text-xl font-semibold text-white">All Articles</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="w-full">
|
||||||
|
<thead class="bg-graphite border-b border-edge-subtle">
|
||||||
|
<tr>
|
||||||
|
{columns.map(col => (
|
||||||
|
<th class="px-6 py-4 text-left text-sm font-semibold text-gray-300 uppercase">{col.replace(/_/g, ' ')}</th>
|
||||||
|
))}
|
||||||
|
<th class="px-6 py-4 text-right text-sm font-semibold text-gray-300 uppercase">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="table-body" class="divide-y divide-edge-subtle">
|
||||||
|
<tr><td colspan={columns.length + 1} class="px-6 py-12 text-center text-gray-400">Loading...</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script define:vars={{ endpoint, columns }}>
|
||||||
|
async function loadData() {
|
||||||
|
try {
|
||||||
|
const token = localStorage.getItem('godToken') || 'jmQXoeyxWoBsB7eHzG7FmnH90f22JtaYBxXHoorhfZ-v4tT3VNEr9vvmwHqYHCDoWXHSU4DeZXApCP-Gha-YdA';
|
||||||
|
const response = await fetch(endpoint, { headers: { 'X-God-Token': token } });
|
||||||
|
if (!response.ok) throw new Error('Failed to fetch');
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
const data = result.data || [];
|
||||||
|
|
||||||
|
const tbody = document.getElementById('table-body');
|
||||||
|
tbody.innerHTML = data.length === 0
|
||||||
|
? `<tr><td colspan="${columns.length + 1}" class="px-6 py-12 text-center text-gray-400">No articles yet</td></tr>`
|
||||||
|
: data.map(item => `
|
||||||
|
<tr class="hover:bg-graphite/50 transition-colors">
|
||||||
|
${columns.map(col => `<td class="px-6 py-4 text-sm text-gray-200">${item[col] || '-'}</td>`).join('')}
|
||||||
|
<td class="px-6 py-4 text-right">
|
||||||
|
<button class="text-gold-500 hover:text-gold-400 text-sm font-medium">View</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`).join('');
|
||||||
|
} catch (err) {
|
||||||
|
document.getElementById('table-body').innerHTML = `<tr><td colspan="${columns.length + 1}" class="px-6 py-12 text-center text-red-400">Error: ${err.message}</td></tr>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadData();
|
||||||
|
</script>
|
||||||
</AdminLayout>
|
</AdminLayout>
|
||||||
|
|||||||
Reference in New Issue
Block a user