From ab7e0a33628ce7d093526f2310e13fae43e5c7a9 Mon Sep 17 00:00:00 2001 From: le king fu Date: Mon, 27 Apr 2026 08:06:54 -0400 Subject: [PATCH] feat(prices): i18n FR/EN keys + CHANGELOG entries Closes #160 --- CHANGELOG.fr.md | 2 ++ CHANGELOG.md | 2 ++ src/i18n/locales/en.json | 34 ++++++++++++++++++++++++++++++++++ src/i18n/locales/fr.json | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+) diff --git a/CHANGELOG.fr.md b/CHANGELOG.fr.md index 9fb2d9a..cf34ed6 100644 --- a/CHANGELOG.fr.md +++ b/CHANGELOG.fr.md @@ -11,6 +11,8 @@ - **Bilan — éditeur de snapshot (type simple)** (route `/balance/snapshot`) : deuxième tranche de la feature *Bilan*. La nouvelle page permet de créer ou modifier un snapshot daté de votre patrimoine : choisissez une date (par défaut aujourd'hui), saisissez la valeur de chaque compte actif groupé par catégorie, puis enregistrez. Le mode est piloté par le paramètre `?date=` de l'URL — si un snapshot existe déjà à cette date, la page bascule automatiquement en mode édition (la contrainte UNIQUE sur `balance_snapshots.snapshot_date` garantit un snapshot par jour). La date d'un snapshot existant est immuable : pour la changer, supprimez puis recréez. Un bouton *Pré-remplir depuis le précédent* copie les valeurs du snapshot antérieur le plus récent (comptes simples uniquement — les comptes cotés seront pris en charge quand l'éditeur coté arrivera). Un bouton *Supprimer* affiche une modal de double confirmation qui exige de retaper la date du snapshot avant d'activer l'action destructive. Seules les valeurs de type simple sont acceptées à ce stade (`quantity` et `unit_price` sont laissés `NULL`) ; l'éditeur coté (quantité × prix unitaire + récupération de prix) arrivera dans une prochaine version. Nouveau hook `useSnapshotEditor` (`useReducer` couvrant tout le cycle de vie) et deux nouveaux composants `SnapshotEditor` + `SnapshotLineRow`. i18n FR/EN sous `balance.snapshot.*` (#146) - **Bilan — fondations du schéma et page Comptes** (route `/balance/accounts`) : première tranche de la nouvelle feature *Bilan*. La migration SQL v9 introduit 5 tables (`balance_categories`, `balance_accounts`, `balance_snapshots`, `balance_snapshot_lines`, `balance_account_transfers`) avec 7 index et seede 7 catégories standard — Encaisse, CELI, REER, Fonds commun, Autre (type simple) + Action et Cryptomonnaie (type coté). La colonne `currency` est verrouillée à `CAD` via une contrainte CHECK au MVP — le support multi-devises arrivera plus tard. La nouvelle page expose deux onglets : *Comptes* (CRUD complet sur les comptes de l'utilisateur, archivage soft plutôt que suppression dure pour préserver les snapshots historiques) et *Catégories* (renommer une catégorie, créer des catégories de type simple, supprimer celles créées par l'utilisateur — les catégories standard sont protégées). Couverture i18n FR/EN complète sous `balance.*`. Snapshots, transferts, rendements et price-fetching premium arriveront dans les prochaines issues ; pour l'instant la route est accessible directement par URL (pas encore d'entrée sidebar) (#138) +- **Récupération de prix premium pour actions (best-effort) et crypto (exchanges directs)** — vie privée préservée via proxy maximus-api. Toggle dans les Paramètres pour révoquer le consentement. (#160) + ### Modifié - **Clé publique Ed25519 de licence** : la clé embarquée a été rotée pour correspondre au serveur de licences `maximus-api` qui vient d'être déployé en production (live à `https://api.lacompagniemaximus.com`). Aucune licence n'avait été émise en production avec l'ancienne clé, donc ce changement est invisible pour les utilisateurs existants — mais `/licenses/activate` répond désormais, donc l'activation par machine (issue #53) sera débloquée dès la sortie de cette version. La clé privée correspondante vit uniquement sur le serveur (#49) diff --git a/CHANGELOG.md b/CHANGELOG.md index db755c1..4ca552d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ - **Balance sheet — snapshot editor (simple kind)** (route `/balance/snapshot`): second slice of the *Bilan* feature. The new page lets you create or edit a dated snapshot of your balance: pick a date (defaulting to today), enter the value of each active account grouped by category, and save. The mode is driven by the `?date=` query parameter — when a snapshot already exists at that date the page automatically flips into edit mode (the underlying `balance_snapshots.snapshot_date` UNIQUE constraint guarantees one snapshot per day). The date of an existing snapshot is immutable: to change it, delete the snapshot and create a new one. A *Prefill from previous snapshot* button copies values from the most recent earlier snapshot (simple-kind accounts only — priced accounts will be handled when the priced editor lands in a later release). A *Delete* button surfaces a double-confirmation modal that requires retyping the snapshot date before the destructive action is enabled. Only simple-kind values are accepted at this stage (`quantity` and `unit_price` are kept `NULL`); the priced editor (quantity × unit price + price fetch) ships in a later release. New `useSnapshotEditor` hook (scoped `useReducer` covering the full lifecycle) and two new components `SnapshotEditor` + `SnapshotLineRow`. FR/EN i18n under `balance.snapshot.*` (#146) - **Balance sheet — schema foundation and accounts page** (route `/balance/accounts`): first slice of the upcoming *Bilan* feature. New SQL migration v9 introduces 5 tables (`balance_categories`, `balance_accounts`, `balance_snapshots`, `balance_snapshot_lines`, `balance_account_transfers`) with 7 indexes and seeds 7 standard categories — Cash, TFSA, RRSP, Mutual Fund, Other (simple kind) plus Stock and Crypto (priced kind). The `currency` column is hardcoded to `CAD` via a CHECK constraint at the MVP — multi-currency support will come in a later release. The new accounts page exposes two tabs: *Accounts* (full CRUD over the user's holdings, soft-archive instead of hard delete to preserve historic snapshots) and *Categories* (rename any category, create simple-kind ones, delete user-created ones — seeded categories are protected). The full FR/EN i18n coverage uses keys under `balance.*`. Snapshots, transfers, returns and the price-fetching premium remain to ship in upcoming issues; for now the route is reachable directly via URL (no sidebar entry yet) (#138) +- **Price-fetching premium for stocks (best-effort) and crypto (direct exchanges)** — privacy preserved via maximus-api proxy. Privacy toggle in Settings to revoke consent. (#160) + ### Changed - **License Ed25519 public key** rotated to match the freshly deployed `maximus-api` license server (now live at `https://api.lacompagniemaximus.com`). No production licenses had been issued against the previous key, so this change is invisible to existing users — but `/licenses/activate` now answers, so machine activation (Issue #53) is unblocked once this release ships. The matching private key lives only on the server (#49) diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 6678ee7..e4851ea 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -635,6 +635,15 @@ "Your data is stored locally and is never affected by updates", "Change the app language using the language selector in the sidebar" ] + }, + "privacy": { + "priceFetchConsent": { + "label": "Price fetching via Maximus", + "description": "Allow Simpl'Résultat to use the Maximus proxy to fetch asset prices. Privacy: your IP is hidden.", + "confirmRevoke": "The fetch button will ask for consent again next time. Continue?", + "revokeButton": "Revoke consent", + "notPremium": "Premium licenses only" + } } }, "charts": { @@ -1730,6 +1739,31 @@ "evolution": { "transferIn": "In", "transferOut": "Out" + }, + "priceFetching": { + "button": "Fetch price", + "tooltipNotPremium": "Available with premium subscription", + "bestEffortNotice": "Source not guaranteed, may be unavailable. Manual input remains primary.", + "attribution": "via Maximus on {{date}}", + "consent": { + "title": "Price fetching via Maximus", + "body": "By clicking Accept, you authorize Simpl'Résultat to query the Maximus proxy to fetch this price. The proxy hides your IP from data providers. No browsing history is stored.", + "accept": "Accept", + "decline": "Cancel" + }, + "errors": { + "invalidSymbol": "Invalid symbol", + "invalidDate": "Invalid date", + "missingParam": "Missing parameter", + "authFailed": "Authentication failed — check your license", + "premiumRequired": "This feature requires a premium subscription", + "licenseRevoked": "License revoked", + "symbolNotFound": "Symbol not found", + "rateLimit": "Too many requests — retry in {{seconds}}s", + "serverUnavailable": "Server unavailable — try again later", + "bestEffortDegraded": "Best-effort price source temporarily unavailable — retry in {{minutes}}min or enter manually", + "sessionCapReached": "Fetch limit reached for this session. Enter remaining prices manually." + } } } } diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json index eef8cc8..78937c2 100644 --- a/src/i18n/locales/fr.json +++ b/src/i18n/locales/fr.json @@ -635,6 +635,15 @@ "Vos données sont stockées localement et ne sont jamais affectées par les mises à jour", "Changez la langue de l'application via le sélecteur de langue dans la barre latérale" ] + }, + "privacy": { + "priceFetchConsent": { + "label": "Récupération de prix via Maximus", + "description": "Permet à Simpl'Résultat d'utiliser le proxy Maximus pour récupérer les prix d'actifs. Privacy : ton IP est masquée.", + "confirmRevoke": "Le bouton de récupération demandera à nouveau ton consentement la prochaine fois. Continuer ?", + "revokeButton": "Révoquer le consentement", + "notPremium": "Réservé aux licences premium" + } } }, "charts": { @@ -1730,6 +1739,31 @@ "evolution": { "transferIn": "Entrée", "transferOut": "Sortie" + }, + "priceFetching": { + "button": "Récupérer le prix", + "tooltipNotPremium": "Disponible avec abonnement premium", + "bestEffortNotice": "Source non garantie, peut être indisponible. La saisie manuelle reste prioritaire.", + "attribution": "via Maximus le {{date}}", + "consent": { + "title": "Récupération de prix via Maximus", + "body": "En cliquant sur \"Accepter\", tu autorises Simpl'Résultat à interroger le proxy Maximus pour récupérer ce prix. Le proxy masque ton IP aux fournisseurs de données. Aucun historique de consultation n'est stocké.", + "accept": "Accepter", + "decline": "Annuler" + }, + "errors": { + "invalidSymbol": "Symbole invalide", + "invalidDate": "Date invalide", + "missingParam": "Paramètre manquant", + "authFailed": "Échec d'authentification — vérifie ta licence", + "premiumRequired": "Cette fonction nécessite un abonnement premium", + "licenseRevoked": "Licence révoquée", + "symbolNotFound": "Symbole introuvable", + "rateLimit": "Trop de requêtes — réessaie dans {{seconds}} s", + "serverUnavailable": "Serveur indisponible — réessaie plus tard", + "bestEffortDegraded": "Source de prix temporairement indisponible — réessayez dans {{minutes}} min ou saisissez manuellement", + "sessionCapReached": "Limite de récupération atteinte pour cette session. Saisissez les prix restants manuellement." + } } } } -- 2.45.2