wiki_ghostguild/app/pages/articles/[slug].vue

152 lines
4.5 KiB
Vue

<template>
<div>
<!-- Loading State -->
<div v-if="pending" class="text-center py-8">
<div class="text-gray-600 dark:text-gray-400">Loading article...</div>
</div>
<!-- Error State -->
<div v-else-if="!article" class="text-center py-8">
<div class="text-red-600 dark:text-red-400">Article not found</div>
<NuxtLink
to="/articles"
class="text-blue-600 hover:text-blue-800 mt-4 inline-block"
>
Back to Articles
</NuxtLink>
</div>
<!-- Article Content -->
<article v-else class="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-8">
<!-- Header -->
<div class="border-b border-gray-200 dark:border-gray-700 pb-6 mb-6">
<h1 class="text-3xl font-bold text-gray-900 dark:text-white mb-4">
{{ article.title }}
</h1>
<div
class="flex flex-wrap gap-4 text-sm text-gray-600 dark:text-gray-400"
>
<span>By {{ article.author || "Unknown" }}</span>
<span v-if="article.publishedAt">
{{ new Date(article.publishedAt).toLocaleDateString() }}
</span>
<span
v-if="article.category"
class="px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded"
>
{{ article.category }}
</span>
<span
v-if="article.accessLevel && article.accessLevel !== 'public'"
class="text-orange-600 dark:text-orange-400"
>
🔒 {{ article.accessLevel }}
</span>
</div>
<!-- Edit Button (for future use) -->
<!-- <div v-if="canEdit" class="mt-4">
<NuxtLink
:to="`${article._path}/edit`"
class="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700"
>
Edit Article
</NuxtLink>
</div> -->
</div>
<!-- Access Control Warning -->
<div
v-if="article.accessLevel === 'member'"
class="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-4 mb-6"
>
<p class="text-blue-800 dark:text-blue-200">
👥 This content is for Baby Ghosts members only
</p>
</div>
<div
v-else-if="article.accessLevel === 'cohort'"
class="bg-purple-50 dark:bg-purple-900/20 border border-purple-200 dark:border-purple-800 rounded-lg p-4 mb-6"
>
<p class="text-purple-800 dark:text-purple-200">
🔒 This content is for a specific cohort
</p>
</div>
<div
v-else-if="article.accessLevel === 'admin'"
class="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4 mb-6"
>
<p class="text-red-800 dark:text-red-200">
🔐 This is internal/admin content
</p>
</div>
<!-- Article Body -->
<ContentRenderer
:value="article"
class="prose prose-lg prose-gray dark:prose-invert max-w-none"
/>
<!-- Tags -->
<div
v-if="article.tags?.length"
class="mt-8 pt-6 border-t border-gray-200 dark:border-gray-700"
>
<div class="flex flex-wrap gap-2">
<span
v-for="tag in article.tags"
:key="tag"
class="px-3 py-1 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-full text-sm"
>
#{{ tag }}
</span>
</div>
</div>
<!-- Navigation -->
<div
class="mt-8 pt-6 border-t border-gray-200 dark:border-gray-700 flex gap-4"
>
<NuxtLink
to="/articles"
class="text-blue-600 hover:text-blue-800 dark:text-blue-400"
>
Back to Articles
</NuxtLink>
</div>
</article>
</div>
</template>
<script setup>
const route = useRoute();
const slug = route.params.slug;
// Fetch article from Nuxt Content
const { data: article, pending } = await useAsyncData(
`article-${slug}`,
async () => {
// Query for the specific article by stem (filename without extension)
const articles = await queryCollection("articles").all();
return articles.find((a) => a.stem === slug);
},
);
// SEO
useHead({
title: () => article.value?.title || "Article",
meta: [
{ name: "description", content: () => article.value?.description || "" },
],
});
// Watch for route changes
watch(
() => route.params.slug,
() => {
// Route change will trigger new useAsyncData
},
{ immediate: true },
);
</script>