Backend: genesis_json sur Document, section_tag/inertia_preset/is_permanent_vote sur DocumentItem Frontend: 5 nouveaux composants pour vue detail document enrichie - GenesisBlock: sources, outils, synthese forum, contributeurs (depliable) - InertiaSlider: visualisation inertie 4 niveaux avec params formule G/M - MiniVoteBoard: tableau vote compact (barre seuil, pour/contre, participation) - EngagementCard: carte item enrichie integrant vote + inertie + actions - DocumentTuto: modal pedagogique vote permanent/inertie/seuils Seed et page [slug] enrichis pour exploiter les nouveaux champs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
193 lines
3.8 KiB
Vue
193 lines
3.8 KiB
Vue
<script setup lang="ts">
|
|
/**
|
|
* Inertia slider — displays the inertia preset level for a section.
|
|
* Read-only indicator (voting on the preset uses the standard vote flow).
|
|
* Shows the formula parameters underneath.
|
|
*/
|
|
const props = withDefaults(defineProps<{
|
|
preset: string
|
|
compact?: boolean
|
|
}>(), {
|
|
compact: false,
|
|
})
|
|
|
|
interface InertiaLevel {
|
|
label: string
|
|
gradient: number
|
|
majority: number
|
|
color: string
|
|
position: number // 0-100 for slider position
|
|
description: string
|
|
}
|
|
|
|
const LEVELS: Record<string, InertiaLevel> = {
|
|
low: {
|
|
label: 'Basse',
|
|
gradient: 0.1,
|
|
majority: 50,
|
|
color: '#22c55e',
|
|
position: 10,
|
|
description: 'Facile a remplacer',
|
|
},
|
|
standard: {
|
|
label: 'Standard',
|
|
gradient: 0.2,
|
|
majority: 50,
|
|
color: '#3b82f6',
|
|
position: 37,
|
|
description: 'Equilibre participation/consensus',
|
|
},
|
|
high: {
|
|
label: 'Haute',
|
|
gradient: 0.4,
|
|
majority: 60,
|
|
color: '#f59e0b',
|
|
position: 63,
|
|
description: 'Forte mobilisation requise',
|
|
},
|
|
very_high: {
|
|
label: 'Tres haute',
|
|
gradient: 0.6,
|
|
majority: 66,
|
|
color: '#ef4444',
|
|
position: 90,
|
|
description: 'Quasi-unanimite requise',
|
|
},
|
|
}
|
|
|
|
const level = computed((): InertiaLevel => LEVELS[props.preset] ?? LEVELS.standard!)
|
|
</script>
|
|
|
|
<template>
|
|
<div class="inertia" :class="{ 'inertia--compact': compact }">
|
|
<!-- Slider track -->
|
|
<div class="inertia__track">
|
|
<div class="inertia__fill" :style="{ width: `${level.position}%`, background: level.color }" />
|
|
<div
|
|
class="inertia__thumb"
|
|
:style="{ left: `${level.position}%`, borderColor: level.color }"
|
|
/>
|
|
<!-- Level marks -->
|
|
<div
|
|
v-for="(lvl, key) in LEVELS"
|
|
:key="key"
|
|
class="inertia__mark"
|
|
:class="{ 'inertia__mark--active': key === preset }"
|
|
:style="{ left: `${lvl.position}%` }"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Label row -->
|
|
<div class="inertia__info">
|
|
<span class="inertia__label" :style="{ color: level.color }">
|
|
{{ level.label }}
|
|
</span>
|
|
<span v-if="!compact" class="inertia__params">
|
|
G={{ level.gradient }} M={{ level.majority }}%
|
|
</span>
|
|
</div>
|
|
|
|
<!-- Description (not in compact mode) -->
|
|
<p v-if="!compact" class="inertia__desc">
|
|
{{ level.description }}
|
|
</p>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.inertia {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.375rem;
|
|
}
|
|
|
|
.inertia--compact {
|
|
gap: 0.25rem;
|
|
}
|
|
|
|
.inertia__track {
|
|
position: relative;
|
|
height: 6px;
|
|
background: color-mix(in srgb, var(--mood-text) 10%, transparent);
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.inertia--compact .inertia__track {
|
|
height: 4px;
|
|
}
|
|
|
|
.inertia__fill {
|
|
position: absolute;
|
|
inset: 0;
|
|
border-radius: 3px;
|
|
transition: width 0.3s ease;
|
|
}
|
|
|
|
.inertia__fill {
|
|
right: auto;
|
|
}
|
|
|
|
.inertia__thumb {
|
|
position: absolute;
|
|
top: 50%;
|
|
transform: translate(-50%, -50%);
|
|
width: 14px;
|
|
height: 14px;
|
|
border-radius: 50%;
|
|
background: var(--mood-bg);
|
|
border: 3px solid;
|
|
transition: left 0.3s ease;
|
|
z-index: 2;
|
|
}
|
|
|
|
.inertia--compact .inertia__thumb {
|
|
width: 10px;
|
|
height: 10px;
|
|
border-width: 2px;
|
|
}
|
|
|
|
.inertia__mark {
|
|
position: absolute;
|
|
top: 50%;
|
|
transform: translate(-50%, -50%);
|
|
width: 4px;
|
|
height: 4px;
|
|
border-radius: 50%;
|
|
background: color-mix(in srgb, var(--mood-text) 20%, transparent);
|
|
z-index: 1;
|
|
}
|
|
|
|
.inertia__mark--active {
|
|
background: transparent;
|
|
}
|
|
|
|
.inertia__info {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.inertia__label {
|
|
font-size: 0.6875rem;
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.04em;
|
|
}
|
|
|
|
.inertia--compact .inertia__label {
|
|
font-size: 0.625rem;
|
|
}
|
|
|
|
.inertia__params {
|
|
font-size: 0.625rem;
|
|
font-family: monospace;
|
|
color: var(--mood-text-muted);
|
|
}
|
|
|
|
.inertia__desc {
|
|
font-size: 0.6875rem;
|
|
color: var(--mood-text-muted);
|
|
line-height: 1.3;
|
|
}
|
|
</style>
|