Restructure Engagement Forgeron + fix GenesisBlock + InertiaSlider
- Seed: restructure Engagement Forgeron (51→59 items) avec 3 nouvelles sections: Engagements fondamentaux (EF1-EF3), Engagements techniques (ET1-ET3), Qualification (Q0-Q1) liée au protocole Embarquement - Seed: ajout protocole Embarquement Forgeron (5 jalons: candidature, miroir, évaluation, certification Smith, mise en ligne) - GenesisBlock: fix lisibilité — fond mood-surface teinté accent au lieu de mood-text inversé, texte mood-aware au lieu de rgba blanc hardcodé - InertiaSlider: mini affiche "Inertie" sous le curseur, compact en width:fit-content pour s'adapter au label - Frontend: ajout section qualification dans SECTION_META/SECTION_ORDER - Pages, composants et tests des sprints précédents Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -45,6 +45,9 @@ const SECTION_META: Record<string, { label: string; icon: string }> = {
|
||||
introduction: { label: 'Introduction', icon: 'i-lucide-scroll-text' },
|
||||
fondamental: { label: 'Engagements fondamentaux', icon: 'i-lucide-shield-check' },
|
||||
technique: { label: 'Engagements techniques', icon: 'i-lucide-wrench' },
|
||||
qualification: { label: 'Qualification', icon: 'i-lucide-graduation-cap' },
|
||||
aspirant: { label: 'Aspirant forgeron', icon: 'i-lucide-user-plus' },
|
||||
certificateur: { label: 'Certificateur forgeron', icon: 'i-lucide-stamp' },
|
||||
conclusion: { label: 'Conclusion', icon: 'i-lucide-bookmark' },
|
||||
annexe: { label: 'Annexes', icon: 'i-lucide-paperclip' },
|
||||
formule: { label: 'Formule de vote', icon: 'i-lucide-calculator' },
|
||||
@@ -52,7 +55,7 @@ const SECTION_META: Record<string, { label: string; icon: string }> = {
|
||||
ordonnancement: { label: 'Ordonnancement', icon: 'i-lucide-list-ordered' },
|
||||
}
|
||||
|
||||
const SECTION_ORDER = ['introduction', 'fondamental', 'technique', 'conclusion', 'annexe', 'formule', 'inertie', 'ordonnancement']
|
||||
const SECTION_ORDER = ['introduction', 'fondamental', 'technique', 'qualification', 'aspirant', 'certificateur', 'conclusion', 'annexe', 'formule', 'inertie', 'ordonnancement']
|
||||
|
||||
const sections = computed((): Section[] => {
|
||||
const grouped: Record<string, DocumentItem[]> = {}
|
||||
@@ -140,11 +143,36 @@ async function archiveToSanctuary() {
|
||||
const activeSection = ref<string | null>(null)
|
||||
|
||||
function scrollToSection(tag: string) {
|
||||
const el = document.getElementById(`section-${tag}`)
|
||||
if (el) {
|
||||
el.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||
activeSection.value = tag
|
||||
// Expand the section if collapsed
|
||||
if (collapsedSections.value[tag]) {
|
||||
collapsedSections.value[tag] = false
|
||||
}
|
||||
nextTick(() => {
|
||||
const el = document.getElementById(`section-${tag}`)
|
||||
if (el) {
|
||||
el.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||
activeSection.value = tag
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// ─── Collapsible sections ────────────────────────────────────
|
||||
// First 2 sections open by default, rest collapsed
|
||||
|
||||
const collapsedSections = ref<Record<string, boolean>>({})
|
||||
|
||||
watch(sections, (newSections) => {
|
||||
if (newSections.length > 0 && Object.keys(collapsedSections.value).length === 0) {
|
||||
const map: Record<string, boolean> = {}
|
||||
newSections.forEach((s, i) => {
|
||||
map[s.tag] = i >= 2 // collapsed if index >= 2
|
||||
})
|
||||
collapsedSections.value = map
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
function toggleSection(tag: string) {
|
||||
collapsedSections.value[tag] = !collapsedSections.value[tag]
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -280,8 +308,11 @@ function scrollToSection(tag: string) {
|
||||
:id="`section-${section.tag}`"
|
||||
class="doc-page__section"
|
||||
>
|
||||
<!-- Section header -->
|
||||
<div class="doc-page__section-header">
|
||||
<!-- Section header (clickable toggle) -->
|
||||
<button
|
||||
class="doc-page__section-header"
|
||||
@click="toggleSection(section.tag)"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<UIcon :name="section.icon" style="color: var(--mood-accent)" />
|
||||
<h2 class="doc-page__section-title">
|
||||
@@ -291,20 +322,29 @@ function scrollToSection(tag: string) {
|
||||
{{ section.items.length }}
|
||||
</UBadge>
|
||||
</div>
|
||||
<InertiaSlider :preset="section.inertiaPreset" compact class="max-w-48" />
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<InertiaSlider :preset="section.inertiaPreset" compact mini />
|
||||
<UIcon
|
||||
name="i-lucide-chevron-down"
|
||||
class="doc-page__section-chevron"
|
||||
:class="{ 'doc-page__section-chevron--open': !collapsedSections[section.tag] }"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<!-- Items -->
|
||||
<div class="doc-page__section-items">
|
||||
<EngagementCard
|
||||
v-for="item in section.items"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
:document-slug="slug"
|
||||
:show-actions="auth.isAuthenticated"
|
||||
@propose="handlePropose"
|
||||
/>
|
||||
</div>
|
||||
<!-- Items (collapsible) -->
|
||||
<Transition name="section-collapse">
|
||||
<div v-show="!collapsedSections[section.tag]" class="doc-page__section-items">
|
||||
<EngagementCard
|
||||
v-for="item in section.items"
|
||||
:key="item.id"
|
||||
:item="item"
|
||||
:document-slug="slug"
|
||||
:show-actions="auth.isAuthenticated"
|
||||
@propose="handlePropose"
|
||||
/>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -449,6 +489,27 @@ function scrollToSection(tag: string) {
|
||||
gap: 1rem;
|
||||
padding: 0.75rem 0;
|
||||
border-bottom: 2px solid color-mix(in srgb, var(--mood-accent) 15%, transparent);
|
||||
width: 100%;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
|
||||
.doc-page__section-header:hover {
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.doc-page__section-chevron {
|
||||
font-size: 1rem;
|
||||
color: var(--mood-text-muted);
|
||||
transform: rotate(-90deg);
|
||||
transition: transform 0.25s ease;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.doc-page__section-chevron--open {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
.doc-page__section-title {
|
||||
@@ -469,4 +530,22 @@ function scrollToSection(tag: string) {
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
/* Section collapse transition */
|
||||
.section-collapse-enter-active,
|
||||
.section-collapse-leave-active {
|
||||
transition: all 0.3s ease;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.section-collapse-enter-from,
|
||||
.section-collapse-leave-to {
|
||||
opacity: 0;
|
||||
max-height: 0;
|
||||
}
|
||||
|
||||
.section-collapse-enter-to,
|
||||
.section-collapse-leave-from {
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -87,7 +87,6 @@ const filteredDocuments = computed(() => {
|
||||
})
|
||||
|
||||
/** Toolbox vignettes from protocols. */
|
||||
const toolboxTitle = 'Modalites de vote'
|
||||
|
||||
const typeLabel = (docType: string): string => {
|
||||
switch (docType) {
|
||||
@@ -252,23 +251,27 @@ async function createDocument() {
|
||||
|
||||
<!-- Toolbox sidebar -->
|
||||
<template #toolbox>
|
||||
<div class="toolbox-section-title">
|
||||
{{ toolboxTitle }}
|
||||
</div>
|
||||
<template v-if="protocols.protocols.length > 0">
|
||||
<ToolboxVignette
|
||||
v-for="protocol in protocols.protocols"
|
||||
:key="protocol.id"
|
||||
:title="protocol.name"
|
||||
:bullets="['Applicable aux documents', protocol.mode_params || 'Configuration standard']"
|
||||
:actions="[
|
||||
{ label: 'Voir', icon: 'i-lucide-eye', to: `/protocols/${protocol.id}` },
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<p v-else class="toolbox-empty-text">
|
||||
Aucun protocole configure
|
||||
</p>
|
||||
<ToolboxVignette
|
||||
title="Modules"
|
||||
:bullets="['Structurer en sections et clauses', 'Vote independant par clause']"
|
||||
:actions="[
|
||||
{ label: 'Voir', icon: 'i-lucide-puzzle', emit: 'modules' },
|
||||
]"
|
||||
/>
|
||||
<ToolboxVignette
|
||||
title="Votes permanents"
|
||||
:bullets="['Chaque clause est modifiable', 'Seuil adaptatif WoT']"
|
||||
:actions="[
|
||||
{ label: 'Formules', icon: 'i-lucide-calculator', to: '/protocols/formulas', primary: true },
|
||||
]"
|
||||
/>
|
||||
<ToolboxVignette
|
||||
title="Inertie de remplacement"
|
||||
:bullets="['4 niveaux de difficulte', 'Protege les textes fondamentaux']"
|
||||
:actions="[
|
||||
{ label: 'Simuler', icon: 'i-lucide-sliders-horizontal', to: '/protocols/formulas', primary: true },
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</SectionLayout>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user