108 lines
3.4 KiB
Vue
108 lines
3.4 KiB
Vue
<template>
|
||
<div>
|
||
<h1 class="text-4xl font-bold text-gray-900 dark:text-white mb-4">
|
||
Ghost Guild Knowledge Commons
|
||
</h1>
|
||
<p class="text-xl text-gray-600 dark:text-gray-400 mb-8">
|
||
Collaborative knowledge base for the Baby Ghosts community
|
||
</p>
|
||
|
||
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||
<div class="bg-white dark:bg-gray-800 p-6 rounded-lg shadow">
|
||
<h2 class="text-xl font-semibold mb-2">📚 Browse Articles</h2>
|
||
<p class="text-gray-600 dark:text-gray-400 mb-4">
|
||
Explore our growing collection of guides and resources
|
||
</p>
|
||
<NuxtLink to="/articles" class="text-blue-600 hover:text-blue-800">
|
||
View All Articles →
|
||
</NuxtLink>
|
||
</div>
|
||
|
||
<div
|
||
v-if="isAuthenticated"
|
||
class="bg-white dark:bg-gray-800 p-6 rounded-lg shadow"
|
||
>
|
||
<h2 class="text-xl font-semibold mb-2">✍️ Contribute</h2>
|
||
<p class="text-gray-600 dark:text-gray-400 mb-4">
|
||
Share your knowledge with the community
|
||
</p>
|
||
<NuxtLink to="/articles/new" class="text-blue-600 hover:text-blue-800">
|
||
Create Article →
|
||
</NuxtLink>
|
||
</div>
|
||
|
||
<div class="bg-white dark:bg-gray-800 p-6 rounded-lg shadow">
|
||
<h2 class="text-xl font-semibold mb-2">👥 Community</h2>
|
||
<p class="text-gray-600 dark:text-gray-400 mb-4">
|
||
Connect with other Baby Ghosts members
|
||
</p>
|
||
<a
|
||
href="https://ghostguild.org"
|
||
class="text-blue-600 hover:text-blue-800"
|
||
>
|
||
Visit Ghost Guild →
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Recent Articles -->
|
||
<div class="mt-12">
|
||
<h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-6">
|
||
Recent Articles
|
||
</h2>
|
||
<div v-if="pending" class="text-gray-600 dark:text-gray-400">
|
||
Loading...
|
||
</div>
|
||
<div v-else-if="articles?.length" class="space-y-4">
|
||
<article
|
||
v-for="article in articles"
|
||
:key="article.slug"
|
||
class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow"
|
||
>
|
||
<NuxtLink
|
||
:to="`/articles/${article.slug}`"
|
||
class="text-xl font-semibold text-blue-600 hover:text-blue-800"
|
||
>
|
||
{{ article.title }}
|
||
</NuxtLink>
|
||
<p class="text-gray-600 dark:text-gray-400 mt-2">
|
||
{{ article.description }}
|
||
</p>
|
||
<div class="mt-2 text-sm text-gray-500">
|
||
<span>{{ article.category }}</span> •
|
||
<span>{{
|
||
new Date(article.publishedAt).toLocaleDateString()
|
||
}}</span>
|
||
</div>
|
||
</article>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
const { isAuthenticated } = useAuth();
|
||
|
||
// Fetch recent articles from Nuxt Content to keep generate static-friendly
|
||
const { data: recentArticles, pending } = await useAsyncData(
|
||
"recent-articles",
|
||
async () => {
|
||
const entries = await queryCollection("articles").all();
|
||
|
||
const getTimestamp = (value) => {
|
||
if (!value) {
|
||
return 0;
|
||
}
|
||
const date = new Date(value);
|
||
return Number.isNaN(date.getTime()) ? 0 : date.getTime();
|
||
};
|
||
|
||
return entries
|
||
.filter((article) => article.published !== false)
|
||
.sort((a, b) => getTimestamp(b.publishedAt) - getTimestamp(a.publishedAt))
|
||
.slice(0, 5);
|
||
},
|
||
);
|
||
|
||
const articles = computed(() => recentArticles.value || []);
|
||
</script>
|