feat(prices): PriceFetchControl + consent modal + best-effort UX (#158) #167
4 changed files with 72 additions and 0 deletions
|
|
@ -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 — é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)
|
- **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é
|
### 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)
|
- **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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 — 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)
|
- **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
|
### 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)
|
- **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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -635,6 +635,15 @@
|
||||||
"Your data is stored locally and is never affected by updates",
|
"Your data is stored locally and is never affected by updates",
|
||||||
"Change the app language using the language selector in the sidebar"
|
"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": {
|
"charts": {
|
||||||
|
|
@ -1730,6 +1739,31 @@
|
||||||
"evolution": {
|
"evolution": {
|
||||||
"transferIn": "In",
|
"transferIn": "In",
|
||||||
"transferOut": "Out"
|
"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."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -635,6 +635,15 @@
|
||||||
"Vos données sont stockées localement et ne sont jamais affectées par les mises à jour",
|
"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"
|
"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": {
|
"charts": {
|
||||||
|
|
@ -1730,6 +1739,31 @@
|
||||||
"evolution": {
|
"evolution": {
|
||||||
"transferIn": "Entrée",
|
"transferIn": "Entrée",
|
||||||
"transferOut": "Sortie"
|
"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."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue