v3 : le vote déplié sous l'article, le panier, la signature par le portefeuille Ğ1

- Le vote se déplie sous l'article, façon accordéon, l'URL pour état (?vote=) :
  niveau d'eau, totaux, seuil et voix manquantes, ton vote et où il en est
  (enregistré, inscrit sur la chaîne, en attente, à reconfirmer, non compté),
  inertie et formule. Le reste d'une version (coquilles, traductions,
  signalements, historique) passe dans l'onglet « Détails » du panneau.
- Le panier : article par article, « tout pour / tout contre » par section ou
  pour tout le document, jamais pour un article à plusieurs versions ; un seul
  geste (une écriture, ou une transaction).
- La signature : le portefeuille de la page (injectedWeb3), polkadot-api chargé
  au moment de signer ; sans portefeuille, Ğecko par lien et QR ; la page
  attend la confirmation. Un bulletin que le portefeuille n'a pas signé est
  lâché aussitôt (DELETE /api/ballots/{id}).
- Pastilles d'ambiance : flèches dans le groupe, anneau de focus pointillé à
  la couleur d'accent de la page (3:1 au moins), distinct du choix.
- e2e : chaîne simulée (indexeur et nœud), faux portefeuille ; C21–C24, et
  C4, C5, C6, C11, C17, C19, C20, M1 mis à jour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-10-30 22:47:26 +01:00
co-authored by Claude Opus 5.5
parent 3541e1b1a6
commit b260596cfd
36 changed files with 4353 additions and 234 deletions
+83 -2
View File
@@ -122,6 +122,8 @@ export const es: Messages = {
'doc.action.vote': 'Votación',
'doc.action.proposals': 'Propuestas',
'doc.action.propose': 'Proponer una modificación',
'doc.basket': 'Votar en bloque',
'doc.basket.count': 'Votar en bloque · {n}',
'doc.empty': 'Ningún artículo está en vigor. Las propuestas de abajo suben en cuanto alcanzan su umbral.',
'doc.published': 'Publicado en el repositorio de textos',
'doc.publish.pending': 'Publicación pendiente',
@@ -176,6 +178,8 @@ export const es: Messages = {
'panel.proposals': 'Propuestas',
'panel.propose': 'Proponer',
'panel.settings': 'Ajustes',
'panel.basket': 'Cesta',
'panel.details': 'Detalles',
'panel.close': 'Cerrar el panel',
'panel.inforce': 'En vigor',
'panel.proposal': 'Propuesta',
@@ -199,7 +203,76 @@ export const es: Messages = {
'vote.entered.remove': '«{title}» se ha retirado del texto en vigor.',
'vote.entered.inertia': 'La inercia de «{title}» ha cambiado.',
'vote.closed': 'Esta versión ya no está en votación.',
'vote.wotOnly': 'Aquí solo votan los miembros de la red de confianza Ğ1: conéctate con tu clave Ğ1 para votar. Puedes leer, proponer y señalar una contradicción.',
'vote.fold': 'La votación de «{title}»',
'vote.mine.none': 'No has votado esta versión.',
'vote.mine.for': 'Tu voto: a favor',
'vote.mine.against': 'Tu voto: en contra',
'vote.mine.chain': 'inscrito en la cadena Ğ1',
'vote.mine.saved': 'registrado',
'vote.mine.pending': 'Esperando a la cadena Ğ1: {choice}.',
'vote.mine.refused': 'Tu último voto no se contó: {reason}',
'vote.mine.expired': 'Tu último voto no se vio a tiempo en la cadena Ğ1: no cuenta.',
'vote.choice.for': 'a favor',
'vote.choice.against': 'en contra',
'vote.choice.none': 'retirar tu voto',
'vote.chain.note': 'Tu voto sale de tu monedero Ğ1: {amount} Ğ1, en un solo gesto. La cadena muestra que has votado, nunca lo que has votado.',
'vote.chain.counts': 'Tu voz cuenta para la identidad Ğ1 del monedero que firma, si es miembro de la red de confianza.',
'vote.chain.off': 'La votación por la cadena Ğ1 aún no está abierta: falta crear la cuenta que recibe los votos. Puedes leer, proponer y señalar una contradicción.',
'vote.members': 'Aquí solo votan los miembros de este grupo.',
'vote.details': 'Erratas, traducciones, señalamientos, historial',
'ballot.reason.not_member': 'el monedero que firmó no es el de un miembro de la red de confianza Ğ1.',
'ballot.reason.no_identity': 'el monedero que firmó no está vinculado a ninguna identidad Ğ1.',
'ballot.reason.transfer': 'falta la transferencia a la cuenta de los votos, o su importe es demasiado bajo.',
'basket.title': 'Cesta de votación',
'basket.intro': 'Elige artículo por artículo, una sección entera o todo el documento: todo sale en un solo gesto.',
'basket.doc': 'Todo el documento',
'basket.for': 'Todo a favor',
'basket.against': 'Todo en contra',
'basket.clear': 'Vaciar',
'basket.pick': 'Tu elección para «{title}»',
'basket.pick.retract': 'Retirar',
'basket.versions': 'Varias versiones: elige la tuya.',
'basket.waiting.one': '{n} artículo tiene varias versiones: un gesto agrupado no lo rellena, elige tu versión.',
'basket.waiting.other': '{n} artículos tienen varias versiones: un gesto agrupado no los rellena, elige tu versión.',
'basket.inforce': 'en vigor',
'basket.count.one': '{n} elección',
'basket.count.other': '{n} elecciones',
'basket.send.server': 'Votar ({n})',
'basket.send.chain': 'Firmar y enviar ({n})',
'basket.note.server': 'Tus votos cuentan en cuanto se envían, de una sola vez.',
'basket.note.chain': 'Una sola transacción de {amount} Ğ1, firmada por tu monedero, para todas tus elecciones.',
'basket.empty': 'Nada en la cesta por ahora.',
'basket.sent.one': '{n} voto registrado.',
'basket.sent.other': '{n} votos registrados.',
'basket.section': 'Votar esta sección',
'basket.mine': 'tu voto: {choice}',
'sign.title': 'Votar con tu monedero Ğ1',
'sign.summary.one': '{n} voto, en una transacción: {amount} Ğ1 a la cuenta de los votos, con tu papeleta sellada.',
'sign.summary.other': '{n} votos, en una transacción: {amount} Ğ1 a la cuenta de los votos, con tu papeleta sellada.',
'sign.wallet': '¿Con qué monedero?',
'sign.account': '¿Con qué cuenta?',
'sign.opening': 'Tu monedero se abre…',
'sign.signing': 'Confirma en tu monedero.',
'sign.waiting': 'La cadena Ğ1 confirma tu voto, en general en menos de un minuto.',
'sign.confirmed': 'Tu voto cuenta: está inscrito en la cadena Ğ1.',
'sign.confirmed.as': 'Contado para la identidad Ğ1 «{name}».',
'sign.refused': 'Tu voto no se contó: {reason}',
'sign.expired': 'Tu voto no se vio a tiempo en la cadena Ğ1: no cuenta. Vuelve a empezar cuando quieras.',
'sign.error.cancelled': 'Rechazaste la firma: no se envió nada.',
'sign.error.funds': 'Tu monedero no tiene suficientes Ğ1: debe quedarle al menos 1 Ğ1 después del voto.',
'sign.error.node': 'La cadena Ğ1 no responde. Vuelve a intentarlo en un momento.',
'sign.error.account': 'Esta cuenta ya no está en tu monedero.',
'sign.error.wallet': 'Tu monedero no pudo firmar. Vuelve a intentarlo, o usa Ğecko.',
'sign.none': 'Ningún monedero está conectado a este navegador (duniter-connect, por ejemplo). En tu teléfono, Ğecko sirve.',
'sign.gecko': 'Usar Ğecko',
'sign.gecko.scan': 'Escanea este código con Ğecko, o toca el enlace en tu teléfono. Comprueba el importe y el comentario, y luego valida.',
'sign.gecko.open': 'Abrir en Ğecko',
'sign.gecko.amount': 'Importe',
'sign.gecko.comment': 'Comentario',
'sign.gecko.qr': 'Código para escanear con Ğecko',
'sign.close': 'Cerrar',
'sign.later': 'Puedes cerrar: la confirmación llegará sola, y tu voto aparecerá bajo el artículo.',
'sign.retry': 'Reintentar',
'inertia.title': 'Inercia',
'inertia.explain': 'Cuanto más alta es la inercia, más votos hacen falta para cambiar este texto cuando vota poca gente.',
@@ -394,13 +467,21 @@ export const es: Messages = {
'error.identity_kind': 'Un nuevo enlace solo puede ser para una persona invitada, nunca para una identidad Ğ1.',
'error.hidden_characters': 'El texto contiene caracteres invisibles (inversión del sentido de escritura, espacio de ancho cero, guion condicional, selector de color de un emoji…): quítalos.',
'error.changed': 'El texto ha cambiado mientras tanto: vuelve a cargarlo y empieza de nuevo.',
'error.wot_members_only': 'Aquí solo votan los miembros de la red de confianza Ğ1.',
'error.label': 'El nombre está vacío o es demasiado largo (60 caracteres como máximo).',
'error.name': 'Tu nombre tiene de 2 a 60 caracteres, en una línea: letras, cifras, espacios, guion, apóstrofo o punto.',
'error.name_taken': 'Este nombre ya está ocupado. Elige otro, añadiendo una inicial por ejemplo.',
'error.team_limits': 'Un enlace de equipo vale de 1 a 90 días, para 1 a 500 personas.',
'error.groups': 'Elige al menos un grupo.',
'error.publication_off': 'La publicación no está configurada en este servidor.',
'error.chain_vote_off': 'La votación por la cadena Ğ1 aún no está abierta.',
'error.chain_only': 'Aquí, el voto pasa por la cadena Ğ1, con tu monedero.',
'error.server_vote': 'Aquí se vota directamente, sin monedero.',
'error.too_many_ballots': 'Ya tienes muchos votos esperando a la cadena Ğ1: espera a que se confirmen.',
'error.lines': 'Este voto ya no vale: recarga la página y vuelve a empezar.',
'error.choice': 'Esta elección no existe.',
'error.author': 'Esta cuenta no es una dirección Ğ1.',
'error.not_votable': 'Esta versión ya no está en votación.',
'error.members_only': 'Aquí solo votan los miembros de este grupo.',
'error.retry': 'Reintentar',
'common.cancel': 'Cancelar',