- Aujourd'hui (Fil 13 sections + capture sticky) + Le chemin (tunnel 2 gestes, Q0 inline, 3 chips, dérogation asymétrique, alternatives réglage/consignation) - Registre + fiche décision (timeline, périmètre premier/second lieu auditable, affluence non-ignorable, S'instruire condensé, éléments + cartographie de clôture, épreuve du réel, Remettre en question, PV A4, gravure) - Salle de vote 5 modalités (consentement, nuancé+histogramme, binaire hérité avec jauge inertielle, Réglage collectif complet — faisceau, médiane basse, Pour moi, Explorer, cristallisation-geste —, élection à départage humain) - Textes (Pacte en clair, document vivant, diff, vue projetée, Atelier des formules porté du v1 sur le moteur unique) + Mandats (faits comptés, feux de la rampe, wizard 3 étapes) + Observatoire (consigner→observer→protocoliser) - Onboarding 7 gabarits + Données locales (export/import, attributs, atelier) - voting→framing gardé (Reformuler d'un réglage figé non cristallisé) - Pages v1 supprimées (login, documents, mandates, protocols, sanctuary, tools, decisions/new) — 342 tests verts, build zéro erreur Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
175 lines
5.0 KiB
Vue
175 lines
5.0 KiB
Vue
<script setup lang="ts">
|
|
// <!-- ld-v2 --> Carte d'un document de la bibliothèque : titre, rôle,
|
|
// nombre de clauses, mini-jauges des sessions en cours sur ses clauses,
|
|
// provenance résumée (version, vote d'origine), badge 井 si gravures.
|
|
import type { TextDoc } from '~/types/domain'
|
|
import { PACT_BADGE } from '~/lexicon'
|
|
import type { ClauseSessionGauge } from './textsModel'
|
|
import { formatInt } from './textsModel'
|
|
|
|
const props = withDefaults(defineProps<{
|
|
doc: TextDoc
|
|
clauseCount: number
|
|
gauges?: ClauseSessionGauge[]
|
|
engraved?: number
|
|
/** Carte épinglée du Pacte — mise en avant. */
|
|
pinned?: boolean
|
|
}>(), { gauges: () => [], engraved: 0, pinned: false })
|
|
|
|
const roleLabel = computed(() => (props.doc.role === 'pact' ? PACT_BADGE : 'document de référence'))
|
|
|
|
const version = computed(() =>
|
|
props.doc.provenance?.sources.find(s => s.version)?.version)
|
|
|
|
const voteSummary = computed(() => {
|
|
const r = props.doc.provenance?.voteRecord?.result
|
|
if (!r) return null
|
|
return `${formatInt(r.for)} pour · ${formatInt(r.against)} contre — toile de ${formatInt(r.wotSize)}`
|
|
})
|
|
|
|
const visibleGauges = computed(() => props.gauges.slice(0, 3))
|
|
</script>
|
|
|
|
<template>
|
|
<!-- ld-v2 -->
|
|
<NuxtLink
|
|
:to="`/textes/${doc.slug}`"
|
|
class="doc-card ld-card ld-card--hover"
|
|
:class="{ 'doc-card--pinned': pinned }"
|
|
>
|
|
<header class="doc-card__head">
|
|
<span class="doc-card__role" :class="{ 'doc-card__role--pact': doc.role === 'pact' }">
|
|
<UIcon :name="doc.role === 'pact' ? 'i-lucide-scroll' : 'i-lucide-book-open'" />
|
|
{{ roleLabel }}
|
|
</span>
|
|
<span v-if="engraved > 0" class="doc-card__engraved" title="Décisions gravées sur ce texte">
|
|
井 {{ engraved }}
|
|
</span>
|
|
</header>
|
|
|
|
<h3 class="doc-card__title">{{ doc.title }}</h3>
|
|
<p class="doc-card__desc">{{ doc.description }}</p>
|
|
|
|
<div class="doc-card__meta">
|
|
<span class="doc-card__chip">
|
|
<UIcon name="i-lucide-list" />
|
|
{{ clauseCount }} clause{{ clauseCount > 1 ? 's' : '' }}
|
|
</span>
|
|
<span v-if="version" class="doc-card__chip doc-card__chip--mono">v{{ version }}</span>
|
|
<span v-if="voteSummary" class="doc-card__chip doc-card__chip--vote">
|
|
<UIcon name="i-lucide-vote" />
|
|
{{ voteSummary }}
|
|
</span>
|
|
</div>
|
|
|
|
<div v-if="visibleGauges.length" class="doc-card__sessions">
|
|
<p class="doc-card__sessions-heading">
|
|
{{ gauges.length > 1 ? `${gauges.length} sessions en cours` : 'Une session en cours' }}
|
|
</p>
|
|
<div class="doc-card__gauges">
|
|
<SessionMiniGauge
|
|
v-for="gauge in visibleGauges"
|
|
:key="gauge.decisionId"
|
|
:gauge="gauge"
|
|
@click.stop
|
|
/>
|
|
</div>
|
|
</div>
|
|
</NuxtLink>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.doc-card {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.6rem;
|
|
padding: clamp(1rem, 3vw, 1.35rem);
|
|
text-decoration: none;
|
|
color: inherit;
|
|
}
|
|
.doc-card--pinned {
|
|
background:
|
|
linear-gradient(140deg, var(--mood-accent-soft), transparent 55%),
|
|
var(--mood-surface);
|
|
}
|
|
|
|
.doc-card__head {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 0.5rem;
|
|
}
|
|
.doc-card__role {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.35rem;
|
|
font-size: 0.6875rem;
|
|
font-weight: 800;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.06em;
|
|
padding: 3px 10px;
|
|
border-radius: var(--r-pill);
|
|
background: color-mix(in srgb, var(--mood-text) 7%, transparent);
|
|
color: var(--mood-text-muted);
|
|
}
|
|
.doc-card__role--pact { background: var(--mood-accent-soft); color: var(--mood-accent); }
|
|
.doc-card__engraved {
|
|
font-size: 0.8125rem;
|
|
font-weight: 800;
|
|
color: var(--mood-status-vigueur);
|
|
}
|
|
|
|
.doc-card__title {
|
|
font-size: clamp(1rem, 2.5vw, 1.15rem);
|
|
font-weight: 800;
|
|
color: var(--mood-text);
|
|
letter-spacing: -0.01em;
|
|
line-height: 1.25;
|
|
}
|
|
.doc-card__desc {
|
|
font-size: 0.8125rem;
|
|
color: var(--mood-text-muted);
|
|
line-height: 1.55;
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 2;
|
|
line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.doc-card__meta { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.15rem; }
|
|
.doc-card__chip {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.3rem;
|
|
font-size: 0.71875rem;
|
|
font-weight: 700;
|
|
padding: 3px 9px;
|
|
border-radius: var(--r-pill);
|
|
background: color-mix(in srgb, var(--mood-text) 6%, transparent);
|
|
color: var(--mood-text-muted);
|
|
}
|
|
.doc-card__chip--mono { font-family: monospace; }
|
|
.doc-card__chip--vote {
|
|
background: var(--mood-status-vigueur-bg);
|
|
color: var(--mood-status-vigueur);
|
|
}
|
|
|
|
.doc-card__sessions {
|
|
margin-top: 0.35rem;
|
|
padding-top: 0.6rem;
|
|
box-shadow: 0 -1px 0 color-mix(in srgb, var(--mood-text) 7%, transparent);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.35rem;
|
|
}
|
|
.doc-card__sessions-heading {
|
|
font-size: 0.6875rem;
|
|
font-weight: 800;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.06em;
|
|
color: var(--mood-status-vote);
|
|
}
|
|
.doc-card__gauges { display: flex; flex-direction: column; gap: 0.3rem; }
|
|
</style>
|