v3 : socle Rust (moteur pur, accès trustinWallet, serveur) ; maquette Nuxt rangée dans maquette/
- crates/ld-engine : formule inertielle (parité avec la maquette, garde C), coquille / changement de fond, assistant d'édition à règles, composition d'un document sous vote permanent (adoptions, aperçus, rivales). - crates/trustin-access : qui voit, qui ouvre, qui gère les accès. - crates/ld-server : API axum + SQLite, connexion Ğ1 (client OAuth2 de connect.monnaie-libre.fr), invitations, entrée en vigueur automatique, signalements de contradiction, traductions éditoriales, publication git, taille de la toile lue sur la chaîne. - seeds/acte-engagement-certification.json : 33 articles, fr + en + es ; en vigueur : préambule, E1, E2 (seed a minima). - maquette/ : frontend, backend et docker v2 déplacés tels quels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
<script setup lang="ts">
|
||||
// « Je choisis autrement » — the permanent alternatives, each with its cost.
|
||||
// Weighting up is one tap; lightening REQUIRES a note and imposes a window.
|
||||
import type { ParamSpec, Verdict } from '~/types/domain'
|
||||
import { CHOOSE_OTHERWISE, PARAMETRIC_ALT, RECORD_HOW } from '~/lexicon'
|
||||
|
||||
type AltKey = 'parametric' | 'record' | 'binary'
|
||||
|
||||
const props = defineProps<{
|
||||
alternatives: Verdict['alternatives']
|
||||
parametricHint: boolean
|
||||
chosen: AltKey | null
|
||||
lightening: boolean
|
||||
decidedHow: string
|
||||
overrideNote: string
|
||||
spec?: ParamSpec
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:chosen', value: AltKey | null): void
|
||||
(e: 'update:decidedHow' | 'update:overrideNote', value: string): void
|
||||
(e: 'update:spec', value: ParamSpec): void
|
||||
}>()
|
||||
|
||||
const open = ref(false)
|
||||
|
||||
const keyed = computed(() =>
|
||||
props.alternatives.map((alt) => ({
|
||||
...alt,
|
||||
key: (alt.route === 'record'
|
||||
? 'record'
|
||||
: alt.label === PARAMETRIC_ALT ? 'parametric' : 'binary') as AltKey,
|
||||
})),
|
||||
)
|
||||
|
||||
function pick(key: AltKey) {
|
||||
emit('update:chosen', props.chosen === key ? null : key)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- ld-v2 -->
|
||||
<section class="alt">
|
||||
<button class="alt__toggle" type="button" @click="open = !open">
|
||||
<UIcon :name="open ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'" />
|
||||
<span>{{ CHOOSE_OTHERWISE }}</span>
|
||||
<span v-if="chosen && !open" class="alt__chosen-hint">1 choix actif</span>
|
||||
</button>
|
||||
|
||||
<div v-if="open" class="alt__body">
|
||||
<div class="alt__list">
|
||||
<button
|
||||
v-for="alt in keyed"
|
||||
:key="alt.key"
|
||||
class="alt__item"
|
||||
:class="{
|
||||
'alt__item--on': chosen === alt.key,
|
||||
'alt__item--hint': alt.key === 'parametric' && parametricHint,
|
||||
}"
|
||||
type="button"
|
||||
@click="pick(alt.key)"
|
||||
>
|
||||
<span class="alt__label">{{ alt.label }}</span>
|
||||
<span class="alt__cost">{{ alt.cost }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="chosen === 'parametric'" class="alt__detail">
|
||||
<CheminParamEditor
|
||||
:model-value="spec"
|
||||
@update:model-value="emit('update:spec', $event)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-else-if="chosen === 'record'" class="alt__detail">
|
||||
<label class="alt__field">
|
||||
<span>{{ RECORD_HOW }}</span>
|
||||
<input
|
||||
:value="decidedHow"
|
||||
type="text"
|
||||
placeholder="Décidé au café, comme d'habitude…"
|
||||
@input="emit('update:decidedHow', ($event.target as HTMLInputElement).value)"
|
||||
>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div v-if="lightening" class="alt__lighten">
|
||||
<p class="alt__lighten-mention">
|
||||
<UIcon name="i-lucide-scale" />
|
||||
Tu prends un chemin plus léger que celui du collectif : une fenêtre d'objection
|
||||
de 48 h est imposée, et ta raison s'affiche.
|
||||
</p>
|
||||
<textarea
|
||||
:value="overrideNote"
|
||||
class="alt__note"
|
||||
rows="2"
|
||||
placeholder="Pourquoi alléger ? (obligatoire)"
|
||||
@input="emit('update:overrideNote', ($event.target as HTMLTextAreaElement).value)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.alt { display: flex; flex-direction: column; gap: 0.625rem; }
|
||||
.alt__toggle {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
background: none;
|
||||
padding: 0.375rem 0.25rem;
|
||||
font-size: 0.9375rem;
|
||||
font-weight: 700;
|
||||
color: var(--mood-text-muted);
|
||||
cursor: pointer;
|
||||
align-self: flex-start;
|
||||
border-radius: var(--r-input);
|
||||
}
|
||||
.alt__toggle:hover { color: var(--mood-text); }
|
||||
.alt__chosen-hint { font-size: 0.75rem; font-weight: 600; color: var(--mood-accent); }
|
||||
.alt__body { display: flex; flex-direction: column; gap: 0.75rem; }
|
||||
.alt__list { display: flex; flex-direction: column; gap: 0.5rem; }
|
||||
.alt__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 0.2rem;
|
||||
padding: 0.625rem 0.875rem;
|
||||
border-radius: var(--r-icon);
|
||||
background: var(--mood-input-bg);
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
transition: transform 0.1s ease;
|
||||
}
|
||||
.alt__item:hover { transform: translateY(-1px); }
|
||||
.alt__item--hint { box-shadow: 0 0 0 1.5px color-mix(in srgb, var(--mood-accent) 55%, transparent); }
|
||||
.alt__item--on {
|
||||
background: var(--mood-accent-soft);
|
||||
box-shadow: 0 0 0 2px var(--mood-accent);
|
||||
}
|
||||
.alt__label { font-size: 0.9063rem; font-weight: 700; color: var(--mood-text); }
|
||||
.alt__cost { font-size: 0.8125rem; font-style: italic; color: var(--mood-text-muted); }
|
||||
.alt__detail { padding-left: 0.25rem; }
|
||||
.alt__field { display: flex; flex-direction: column; gap: 0.35rem; font-size: 0.875rem; font-weight: 700; }
|
||||
.alt__field input { padding: 0.625rem 0.75rem; font-size: 0.9375rem; width: 100%; }
|
||||
.alt__lighten {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem;
|
||||
border-radius: var(--r-input);
|
||||
background: var(--mood-status-fenetre-bg);
|
||||
}
|
||||
.alt__lighten-mention {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.4rem;
|
||||
margin: 0;
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
color: var(--mood-status-fenetre);
|
||||
}
|
||||
.alt__note { width: 100%; padding: 0.625rem 0.75rem; font-size: 0.9375rem; resize: vertical; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user