feat(plantare): surface product link + dates; spec the bilateral record

The Plantaré screen stored madeOn/dueBy/varietyId but showed none of them,
so it felt half-built. Surface what's already there (no schema change):

- watchPlantareViews() joins each commitment with its variety label
- tile shows the made-on date, a "Return by {date}" line (amber + "overdue"
  when an open promise is past due, via new seedWarning colour), the variety
  name, and taps through to /variety/:id
- add sheet gains an optional return-by date picker (createPlantare already
  took dueBy)
- variety detail shows a read-only commitments section (hidden when empty,
  no add button — honours the single hand-over door)
- i18n en/es/pt/ast; tests for the join, dueBy round-trip, tile, and section

Also specs the deferred two-sided record in docs/design/plantare-bilateral.md
(Nostr pubkey counterparty, PlantareTransport propose->accept->counter-sign,
deferred key/signature/movement columns, provenance DAG, reputation anchor),
with a faithful transcription of the paper Plantaré v0.4 (BAH-Semillero 2009,
CC-BY-SA). Cross-linked from data-model §2.7 and sharing-model §6.
This commit is contained in:
vjrj 2026-07-13 18:06:33 +02:00
parent e57763d0a2
commit adf396d49d
21 changed files with 687 additions and 27 deletions

View file

@ -937,6 +937,13 @@ class _Translations$plantare$pt extends Translations$plantare$en {
@override String get openSection => 'Pendentes';
@override String get settledSection => 'Feitos';
@override String get removeConfirm => 'Remover este compromisso?';
@override String returnBy({required Object date}) => 'Devolver até ${date}';
@override String get overdue => 'vencido';
@override String get dueByLabel => 'Devolver até (opcional)';
@override String get dueByHint => 'Um lembrete gentil, nunca imposto';
@override String get pickDate => 'Escolher data';
@override String get clearDate => 'Limpar data';
@override String get sectionTitle => 'Compromissos';
}
// Path: handover
@ -1884,6 +1891,13 @@ extension on TranslationsPt {
'plantare.openSection' => 'Pendentes',
'plantare.settledSection' => 'Feitos',
'plantare.removeConfirm' => 'Remover este compromisso?',
'plantare.returnBy' => ({required Object date}) => 'Devolver até ${date}',
'plantare.overdue' => 'vencido',
'plantare.dueByLabel' => 'Devolver até (opcional)',
'plantare.dueByHint' => 'Um lembrete gentil, nunca imposto',
'plantare.pickDate' => 'Escolher data',
'plantare.clearDate' => 'Limpar data',
'plantare.sectionTitle' => 'Compromissos',
'handover.title' => 'Dei ou recebi sementes',
'handover.help' => 'Uma oferta, uma troca ou uma venda: anota-o, com promessa de devolver semente ou sem ela.',
'handover.iGave' => 'Dei sementes',
@ -1906,6 +1920,8 @@ extension on TranslationsPt {
'sale.counterpartyHint' => 'Uma pessoa ou um coletivo (opcional)',
'sale.amount' => 'Montante',
'sale.currency' => 'Moeda',
_ => null,
} ?? switch (path) {
'sale.currencyHint' => '€, Ğ1, horas… (opcional)',
'sale.hours' => 'horas',
'sale.note' => 'Nota (opcional)',
@ -1913,8 +1929,6 @@ extension on TranslationsPt {
'sale.delete' => 'Remover',
'sale.removeConfirm' => 'Remover esta venda?',
'legal.title' => 'Privacidade e regras',
_ => null,
} ?? switch (path) {
'legal.subtitle' => 'A tua privacidade, as regras do mercado e a legalidade das sementes',
'legal.privacyTitle' => 'A tua privacidade',
'legal.privacyBody' => 'O Tane funciona sem conta, e tudo o que registas fica no teu dispositivo, cifrado. Não há publicidade, nem rastreadores, nem servidor do Tane.\n\nNada é partilhado a não ser que tu queiras: quando publicas uma oferta ou o teu perfil, ou envias uma mensagem, viaja por servidores comunitários. As ofertas levam apenas uma zona aproximada — nunca a tua morada.\n\nO que publicas é público, e podem ficar cópias mesmo depois de o retirares — por isso partilha com cuidado.',