initiation librodrome
This commit is contained in:
81
app/pages/lire/[slug].vue
Normal file
81
app/pages/lire/[slug].vue
Normal file
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<div v-if="chapter">
|
||||
<BookChapterHeader
|
||||
:title="chapter.title"
|
||||
:description="chapter.description"
|
||||
:order="chapter.order"
|
||||
:reading-time="chapter.readingTime"
|
||||
:chapter-slug="slug"
|
||||
/>
|
||||
|
||||
<BookChapterContent :content="chapter" />
|
||||
|
||||
<!-- Prev / Next navigation -->
|
||||
<nav class="mt-16 flex items-center justify-between border-t border-white/8 pt-8">
|
||||
<NuxtLink
|
||||
v-if="prevChapter"
|
||||
:to="`/lire/${prevChapter.stem}`"
|
||||
class="btn-ghost gap-2"
|
||||
>
|
||||
<div class="i-lucide-arrow-left h-4 w-4" />
|
||||
<span class="text-sm">{{ prevChapter.title }}</span>
|
||||
</NuxtLink>
|
||||
<div v-else />
|
||||
|
||||
<NuxtLink
|
||||
v-if="nextChapter"
|
||||
:to="`/lire/${nextChapter.stem}`"
|
||||
class="btn-ghost gap-2"
|
||||
>
|
||||
<span class="text-sm">{{ nextChapter.title }}</span>
|
||||
<div class="i-lucide-arrow-right h-4 w-4" />
|
||||
</NuxtLink>
|
||||
<div v-else />
|
||||
</nav>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: 'reading',
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
const slug = route.params.slug as string
|
||||
|
||||
// Initialize guided mode
|
||||
useGuidedMode()
|
||||
|
||||
const { data: chapter } = await useAsyncData(`chapter-${slug}`, () =>
|
||||
queryCollection('book').path(`/book/${slug}`).first(),
|
||||
)
|
||||
|
||||
if (!chapter.value) {
|
||||
throw createError({ statusCode: 404, statusMessage: 'Chapitre non trouvé' })
|
||||
}
|
||||
|
||||
useHead({
|
||||
title: chapter.value?.title,
|
||||
})
|
||||
|
||||
// Get adjacent chapters for navigation
|
||||
const { data: allChapters } = await useAsyncData('book-nav', () =>
|
||||
queryCollection('book').order('order', 'ASC').all(),
|
||||
)
|
||||
|
||||
const currentIndex = computed(() =>
|
||||
allChapters.value?.findIndex(c => c.stem === slug) ?? -1,
|
||||
)
|
||||
|
||||
const prevChapter = computed(() => {
|
||||
const idx = currentIndex.value
|
||||
if (idx <= 0 || !allChapters.value) return null
|
||||
return allChapters.value[idx - 1]
|
||||
})
|
||||
|
||||
const nextChapter = computed(() => {
|
||||
const idx = currentIndex.value
|
||||
if (!allChapters.value || idx >= allChapters.value.length - 1) return null
|
||||
return allChapters.value[idx + 1]
|
||||
})
|
||||
</script>
|
||||
71
app/pages/lire/index.vue
Normal file
71
app/pages/lire/index.vue
Normal file
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<div class="section-padding">
|
||||
<div class="container-content">
|
||||
<header class="mb-12 text-center">
|
||||
<p class="mb-2 font-mono text-sm tracking-widest text-primary uppercase">{{ content?.kicker }}</p>
|
||||
<h1 class="page-title font-display font-bold tracking-tight text-white">
|
||||
{{ content?.title }}
|
||||
</h1>
|
||||
<p class="mt-4 mx-auto max-w-2xl text-white/60">
|
||||
{{ content?.description }}
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div class="mx-auto max-w-3xl">
|
||||
<ul class="flex flex-col gap-3">
|
||||
<li
|
||||
v-for="chapter in chapters"
|
||||
:key="chapter.path"
|
||||
>
|
||||
<NuxtLink
|
||||
:to="`/lire/${chapter.stem}`"
|
||||
class="card-surface flex items-start gap-4 group"
|
||||
>
|
||||
<span class="font-mono text-2xl font-bold text-primary/30 leading-none mt-1 w-10 text-right flex-shrink-0">
|
||||
{{ String(chapter.order).padStart(2, '0') }}
|
||||
</span>
|
||||
<div class="min-w-0 flex-1">
|
||||
<h2 class="font-display text-lg font-semibold text-white group-hover:text-primary transition-colors">
|
||||
{{ chapter.title }}
|
||||
</h2>
|
||||
<p v-if="chapter.description" class="mt-1 text-sm text-white/50">
|
||||
{{ chapter.description }}
|
||||
</p>
|
||||
<div class="mt-2 flex items-center gap-3">
|
||||
<span v-if="chapter.readingTime" class="text-xs text-white/30">
|
||||
<span class="i-lucide-clock inline-block h-3 w-3 mr-1 align-middle" />
|
||||
{{ chapter.readingTime }}
|
||||
</span>
|
||||
<SongBadges :chapter-slug="chapter.stem!" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="i-lucide-chevron-right h-5 w-5 text-white/20 group-hover:text-primary/60 transition-colors flex-shrink-0 mt-2" />
|
||||
</NuxtLink>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: 'default',
|
||||
})
|
||||
|
||||
const { data: content } = await usePageContent('lire')
|
||||
|
||||
useHead({
|
||||
title: content.value?.meta?.title ?? 'Table des matières',
|
||||
})
|
||||
|
||||
const { data: chapters } = await useAsyncData('book-toc', () =>
|
||||
queryCollection('book').order('order', 'ASC').all(),
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-title {
|
||||
font-size: clamp(2rem, 5vw, 2.75rem);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user