Intégrer widget feedback (Feedback Hub) #67

Closed
opened 2026-04-10 19:36:24 +00:00 by maximus · 0 comments
Owner

Contexte

Intégrer le widget Feedback Hub dans Simpl'Résultat (desktop Tauri v2). Le service feedback-api est déployé à https://feedback.lacompagniemaximus.com.

Documents de référence

Issues liées

  • Sibling : maximus/simpl-liste #68 — version mobile, non bloquant mais à garder en cohérence
  • Follow-up docs : créé ci-dessous dans la-compagnie-maximus pour MAJ du registre post-intégration

Tension privacy-first

L'app est explicitement "privacy-first, zéro donnée envoyée vers un serveur tiers". CSP actuelle (src-tauri/tauri.conf.json) n'autorise que api. et auth.lacompagniemaximus.com. L'intégration :

  • Ajoute un canal de collecte externe — exige divulgation dans le guide utilisateur et les traductions docs.*
  • Exige un consentement explicite avant le premier envoi (Loi 25 — opt-in, pas opt-out)
  • Toute donnée attachée (route, logs, identité Maximus) doit être opt-in par checkbox décochée par défaut

Décision de design

Emplacement (déviation assumée des conventions cross-app) :

  • Ops guide prévoit FAB bas-droit (site vitrine) ou icône topbar (booking)
  • Simpl'Résultat est une app desktop privacy-first — on choisit une card dans Settings, fusionnée avec LogViewerCard. Pas de FAB, pas d'icône persistante dans le chrome. Justifié par l'esthétique minimaliste + les logs déjà disponibles au même endroit pour le diagnostic.
  • À documenter explicitement dans le registre feedback-hub-ops.md post-intégration.

Transport : proxy via Rust (commande Tauri send_feedback) :

  • Bypasse CORS (l'origine Tauri n'est pas whitelistée côté FEEDBACK_CORS_ORIGINS et on évite de l'ajouter)
  • Aligné avec startOAuth, validateLicenseKey, updater (pattern existant)
  • Permet un audit clair de ce qui quitte la machine (argument privacy)
  • Zéro changement serveur requis

Flow utilisateur :

  1. Bouton "Envoyer un feedback" dans la card Logs de Settings (fusionné dans LogViewerCard.tsx)
  2. Dialog avec textarea (2000 char, compteur) + 3 checkboxes décochées par défaut :
    • "Inclure le contexte de navigation" → envoie page, locale, theme, viewport, timestamp + userAgent composé ("Simpl'Résultat/<version> (<os>)")
    • "Inclure les derniers logs d'erreur" → appendre les N dernières entrées warn/error au content (pas au context — volontaire, limite 2000 char)
    • "M'identifier avec mon compte Maximus" (visible seulement si useAuth().state.status === "authenticated") → envoie l'identifiant Maximus dans user_id
  3. Premier envoi : modal de consentement one-time expliquant que la donnée quitte la machine. Flag feedbackConsentAccepted persisté (localStorage ou config profile).
  4. Handling codes serveur : 201 → toast succès + auto-close 2s ; 400/429/500/network → toasts i18n clairs (wording du guide ops).

Payload conforme au contrat API

Le serveur a une whitelist stricte ALLOWED_CONTEXT_KEYS = {page, locale, theme, viewport, userAgent, referrer, timestamp} (clés non-whitelistées silencieusement droppées, 500 chars/champ). Aucune extension de la whitelist n'est souhaitée (convention cross-app).

Exemple envoi (toutes checkboxes cochées)

{
  "app_id": "simpl-resultat",
  "content": "Le bouton export ne fonctionne pas.\n\n---\nLogs récents :\n[2026-04-17T15:23Z] error: ExportError at exportService.ts:47\n[2026-04-17T15:22Z] warn: retrying fs_write",
  "user_id": "<maximus-account-id>",
  "context": {
    "page": "/reports/cartes",
    "locale": "fr",
    "theme": "dark",
    "viewport": "1920x1080",
    "userAgent": "Simpl'Résultat/0.8.1 (Linux)",
    "timestamp": "2026-04-17T15:23:00.000Z"
  }
}
  • app_version + ospackés dans userAgent, pas de clés custom
  • profile_namepas envoyé (local-only, pas de sens cross-app)
  • logssuffixe du content avec séparateur \n\n---\nLogs récents :\n, troncature pour rester sous 2000 char total
  • user_id : identifiant Maximus depuis useAuth().state.account (à confirmer côté impl : id ou email selon ce que retourne getAccountInfo())

Wording (conforme au guide ops pour cohérence cross-app)

Clé i18n FR EN
feedback.button Envoyer un feedback Send feedback
feedback.dialog.title Votre avis Your feedback
feedback.dialog.placeholder Décrivez votre suggestion, commentaire ou problème... Describe your suggestion, comment, or issue...
feedback.dialog.submit Envoyer Send
feedback.dialog.cancel Annuler Cancel
feedback.checkbox.context Inclure le contexte de navigation (page, thème, écran, version) Include navigation context (page, theme, viewport, version)
feedback.checkbox.logs Inclure les derniers logs d'erreur Include recent error logs
feedback.checkbox.identify M'identifier avec mon compte Maximus Identify me with my Maximus account
feedback.toast.success Merci pour votre feedback Thank you for your feedback
feedback.toast.error.429 Trop de feedbacks envoyés récemment. Réessayez plus tard. Too many feedbacks sent recently. Try again later.
feedback.toast.error.400 Feedback invalide. Vérifiez le contenu. Invalid feedback. Check the content.
feedback.toast.error.generic Erreur lors de l'envoi. Réessayez plus tard. Error while sending. Try again later.
feedback.consent.title Envoi de feedback Feedback submission
feedback.consent.body Votre feedback sera envoyé à feedback.lacompagniemaximus.com pour que nous puissions améliorer l'application. Cette opération nécessite une connexion Internet et constitue une exception au fonctionnement 100 % local de l'application. Your feedback will be sent to feedback.lacompagniemaximus.com so we can improve the app. This requires an internet connection and is an exception to the app's 100 % local operation.
feedback.consent.accept J'accepte I agree

Travail à faire

Client (ce ticket — simpl-resultat)

  • Ajouter https://feedback.lacompagniemaximus.com au CSP connect-src de src-tauri/tauri.conf.json (pour futur fetch direct si jamais on abandonne le proxy Rust)
  • Ajouter dépendance Rust reqwest si pas déjà présente dans src-tauri/Cargo.toml
  • Créer commande Tauri send_feedback(app_id, content, user_id?, context?) dans src-tauri/src/commands/feedback_commands.rs — POST vers feedback-api, retourne Result<FeedbackResult, String>
  • Enregistrer la commande dans src-tauri/src/lib.rs
  • Créer src/services/feedbackService.ts — wrapper invoke("send_feedback", ...) + typage TS
  • Créer src/hooks/useFeedback.ts — state machine (idle → sending → success → error) via useReducer
  • Créer src/components/settings/FeedbackDialog.tsx — Dialog avec textarea, compteur, 3 checkboxes, handling des codes
  • Créer src/components/settings/FeedbackConsentDialog.tsx — modal one-time consentement privacy
  • Modifier src/components/settings/LogViewerCard.tsx — ajouter bouton "Envoyer un feedback" qui ouvre le dialog
  • Ajouter helper dans src/services/logService.tsgetRecentErrorLogs(n) pour extraire les N derniers warn/error formatés en string
  • Implémenter composition userAgent : "Simpl'Résultat/<version> (<os>)" via tauri::app_handle().package_info() + tauri_plugin_os (si pas déjà en deps, ajouter)
  • Persister flag feedbackConsentAccepted (localStorage ou config profile)
  • Ajouter namespace feedback.* dans src/i18n/locales/fr.json + en.json (table wording ci-dessus)
  • MAJ docs.* dans les deux locales — divulguer le canal de feedback externe
  • MAJ docs/guide-utilisateur.md — section feedback
  • Entrée CHANGELOG (EN + FR) sous ## [Unreleased]Added

Cross-project

  • maximus/la-compagnie-maximus : MAJ docs/feedback-hub-ops.md registre apps — ajouter ligne simpl-resultat (app_id, placement "Card Settings", CORS = — proxy Rust, date) — issue de tracking créée séparément
  • maximus/simpl-feedback-api : aucun changement (proxy Rust, whitelist figée par convention)
  • maximus/simpl-liste #68 : sibling non bloquant, pas de dépendance

Fichiers concernés

Fichier Action Raison
src-tauri/tauri.conf.json Modifier CSP connect-src feedback-api
src-tauri/src/commands/feedback_commands.rs Créer Commande send_feedback (reqwest POST)
src-tauri/src/lib.rs Modifier Enregistrer commande
src-tauri/Cargo.toml Modifier si besoin reqwest + tauri-plugin-os éventuel
src/services/feedbackService.ts Créer Wrapper TS sur commande Tauri
src/hooks/useFeedback.ts Créer State machine
src/components/settings/FeedbackDialog.tsx Créer Dialog textarea + checkboxes
src/components/settings/FeedbackConsentDialog.tsx Créer Modal consentement one-time
src/components/settings/LogViewerCard.tsx Modifier Ajouter bouton feedback
src/services/logService.ts Modifier Helper getRecentErrorLogs(n)
src/i18n/locales/fr.json + en.json Modifier Namespace feedback.* + MAJ docs.*
docs/guide-utilisateur.md Modifier Section feedback
CHANGELOG.md + CHANGELOG.fr.md Modifier Entrée [Unreleased] → Added

Surface de test

  • Aucun test existant sur SettingsPage, LogViewerCard, authService, logService
  • Tests à ajouter :
    • feedbackService.test.ts — invoke wrapper, propagation d'erreurs
    • useFeedback.test.ts — state machine (idle → sending → success/error)
    • logService.test.tsgetRecentErrorLogs(n) filtre correctement info/warn/error
  • Test manuel bout-en-bout :
    1. Envoyer un feedback depuis l'app en dev
    2. Vérifier côté PostgreSQL : SELECT * FROM feedback_hub.feedbacks WHERE app_id='simpl-resultat' ORDER BY created_at DESC LIMIT 1
    3. Vérifier que seules les clés whitelistées sont présentes dans context

Critères d'acceptation

  • Un utilisateur peut soumettre un feedback texte depuis la card Logs de Settings
  • Le premier envoi déclenche un modal de consentement privacy (une seule fois)
  • Les 3 checkboxes (contexte, logs, identité Maximus) sont décochées par défaut
  • Le checkbox "M'identifier" n'apparaît que si connecté à un compte Maximus
  • user_id est envoyé uniquement si checkbox cochée ; sinon null
  • Logs encodés comme suffixe du content, pas comme clé context custom
  • userAgent contient "Simpl'Résultat/<version> (<os>)"
  • Rate limit 429 affiche un message i18n clair ; 400 idem ; 500/erreur réseau idem
  • Textarea désactivée si vide ou whitespace-only ; compteur 0/2000
  • Dark mode et i18n FR/EN respectés
  • Le feedback apparaît dans feedback_hub.feedbacks avec app_id="simpl-resultat" et les champs de contexte corrects (test manuel)
  • Wording aligne avec le guide ops (voir tableau)
  • La documentation utilisateur mentionne le canal de feedback et sa nature externe
  • docs/feedback-hub-ops.md dans la-compagnie-maximus mis à jour (issue de suivi séparée)

Complexité estimée

Medium — code client structuré (1 commande Rust, 1 service, 1 hook, 2 composants), pas de changement serveur. Les vrais coûts sont : décisions privacy (consent, divulgation docs), wording i18n aligné multi-apps, tests.

Dépendances

  • Endpoint feedback-api live et conforme au contrat documenté
  • Référence d'implé disponible (la-suite-booking)
  • Maximus OAuth déjà intégré dans simpl-resultat (#51/#53)
  • Aucun bloqueur externe
## Contexte Intégrer le widget Feedback Hub dans Simpl'Résultat (desktop Tauri v2). Le service `feedback-api` est déployé à `https://feedback.lacompagniemaximus.com`. ### Documents de référence - [`la-compagnie-maximus/docs/feedback-hub-ops.md`](https://git.lacompagniemaximus.com/maximus/la-compagnie-maximus/src/branch/main/docs/feedback-hub-ops.md) — **source de vérité opérationnelle** (contrat API, conventions wording, placement, registre des apps) - `la-compagnie-maximus/spec-feedback-hub.md` — spec d'origine (design, figé) - [`la-suite-booking/components/feedback/FeedbackWidget.tsx`](https://git.lacompagniemaximus.com/maximus/la-suite-booking/src/branch/main/components/feedback/FeedbackWidget.tsx) — référence d'implémentation client ### Issues liées - **Sibling** : `maximus/simpl-liste` #68 — version mobile, non bloquant mais à garder en cohérence - **Follow-up docs** : créé ci-dessous dans `la-compagnie-maximus` pour MAJ du registre post-intégration ### Tension privacy-first L'app est explicitement *"privacy-first, zéro donnée envoyée vers un serveur tiers"*. CSP actuelle (`src-tauri/tauri.conf.json`) n'autorise que `api.` et `auth.lacompagniemaximus.com`. L'intégration : - Ajoute un canal de collecte externe — exige divulgation dans le guide utilisateur et les traductions `docs.*` - Exige un **consentement explicite** avant le premier envoi (Loi 25 — opt-in, pas opt-out) - Toute donnée attachée (route, logs, identité Maximus) doit être opt-in par checkbox décochée par défaut ## Décision de design **Emplacement (déviation assumée des conventions cross-app)** : - Ops guide prévoit `FAB bas-droit` (site vitrine) ou `icône topbar` (booking) - Simpl'Résultat est une app desktop privacy-first — on choisit **une card dans Settings, fusionnée avec `LogViewerCard`**. Pas de FAB, pas d'icône persistante dans le chrome. Justifié par l'esthétique minimaliste + les logs déjà disponibles au même endroit pour le diagnostic. - À documenter explicitement dans le registre `feedback-hub-ops.md` post-intégration. **Transport : proxy via Rust** (commande Tauri `send_feedback`) : - Bypasse CORS (l'origine Tauri n'est pas whitelistée côté `FEEDBACK_CORS_ORIGINS` et on évite de l'ajouter) - Aligné avec `startOAuth`, `validateLicenseKey`, updater (pattern existant) - Permet un audit clair de ce qui quitte la machine (argument privacy) - **Zéro changement serveur requis** **Flow utilisateur** : 1. Bouton "Envoyer un feedback" dans la card Logs de Settings (fusionné dans `LogViewerCard.tsx`) 2. Dialog avec textarea (2000 char, compteur) + 3 checkboxes décochées par défaut : - "Inclure le contexte de navigation" → envoie `page`, `locale`, `theme`, `viewport`, `timestamp` + `userAgent` composé (`"Simpl'Résultat/<version> (<os>)"`) - "Inclure les derniers logs d'erreur" → appendre les N dernières entrées warn/error au `content` (pas au `context` — volontaire, limite 2000 char) - "M'identifier avec mon compte Maximus" (visible seulement si `useAuth().state.status === "authenticated"`) → envoie l'identifiant Maximus dans `user_id` 3. **Premier envoi** : modal de consentement one-time expliquant que la donnée quitte la machine. Flag `feedbackConsentAccepted` persisté (localStorage ou config profile). 4. Handling codes serveur : 201 → toast succès + auto-close 2s ; 400/429/500/network → toasts i18n clairs (wording du guide ops). ## Payload conforme au contrat API Le serveur a une whitelist stricte `ALLOWED_CONTEXT_KEYS = {page, locale, theme, viewport, userAgent, referrer, timestamp}` (clés non-whitelistées silencieusement droppées, 500 chars/champ). **Aucune extension de la whitelist n'est souhaitée** (convention cross-app). ### Exemple envoi (toutes checkboxes cochées) ```json { "app_id": "simpl-resultat", "content": "Le bouton export ne fonctionne pas.\n\n---\nLogs récents :\n[2026-04-17T15:23Z] error: ExportError at exportService.ts:47\n[2026-04-17T15:22Z] warn: retrying fs_write", "user_id": "<maximus-account-id>", "context": { "page": "/reports/cartes", "locale": "fr", "theme": "dark", "viewport": "1920x1080", "userAgent": "Simpl'Résultat/0.8.1 (Linux)", "timestamp": "2026-04-17T15:23:00.000Z" } } ``` - `app_version` + `os` → **packés dans `userAgent`**, pas de clés custom - `profile_name` → **pas envoyé** (local-only, pas de sens cross-app) - `logs` → **suffixe du `content`** avec séparateur `\n\n---\nLogs récents :\n`, troncature pour rester sous 2000 char total - `user_id` : identifiant Maximus depuis `useAuth().state.account` (à confirmer côté impl : `id` ou `email` selon ce que retourne `getAccountInfo()`) ## Wording (conforme au guide ops pour cohérence cross-app) | Clé i18n | FR | EN | |---|---|---| | `feedback.button` | Envoyer un feedback | Send feedback | | `feedback.dialog.title` | Votre avis | Your feedback | | `feedback.dialog.placeholder` | Décrivez votre suggestion, commentaire ou problème... | Describe your suggestion, comment, or issue... | | `feedback.dialog.submit` | Envoyer | Send | | `feedback.dialog.cancel` | Annuler | Cancel | | `feedback.checkbox.context` | Inclure le contexte de navigation (page, thème, écran, version) | Include navigation context (page, theme, viewport, version) | | `feedback.checkbox.logs` | Inclure les derniers logs d'erreur | Include recent error logs | | `feedback.checkbox.identify` | M'identifier avec mon compte Maximus | Identify me with my Maximus account | | `feedback.toast.success` | Merci pour votre feedback | Thank you for your feedback | | `feedback.toast.error.429` | Trop de feedbacks envoyés récemment. Réessayez plus tard. | Too many feedbacks sent recently. Try again later. | | `feedback.toast.error.400` | Feedback invalide. Vérifiez le contenu. | Invalid feedback. Check the content. | | `feedback.toast.error.generic` | Erreur lors de l'envoi. Réessayez plus tard. | Error while sending. Try again later. | | `feedback.consent.title` | Envoi de feedback | Feedback submission | | `feedback.consent.body` | Votre feedback sera envoyé à feedback.lacompagniemaximus.com pour que nous puissions améliorer l'application. Cette opération nécessite une connexion Internet et constitue une exception au fonctionnement 100 % local de l'application. | Your feedback will be sent to feedback.lacompagniemaximus.com so we can improve the app. This requires an internet connection and is an exception to the app's 100 % local operation. | | `feedback.consent.accept` | J'accepte | I agree | ## Travail à faire ### Client (ce ticket — `simpl-resultat`) - [ ] Ajouter `https://feedback.lacompagniemaximus.com` au CSP `connect-src` de `src-tauri/tauri.conf.json` (pour futur fetch direct si jamais on abandonne le proxy Rust) - [ ] Ajouter dépendance Rust `reqwest` si pas déjà présente dans `src-tauri/Cargo.toml` - [ ] Créer commande Tauri `send_feedback(app_id, content, user_id?, context?)` dans `src-tauri/src/commands/feedback_commands.rs` — POST vers `feedback-api`, retourne `Result<FeedbackResult, String>` - [ ] Enregistrer la commande dans `src-tauri/src/lib.rs` - [ ] Créer `src/services/feedbackService.ts` — wrapper `invoke("send_feedback", ...)` + typage TS - [ ] Créer `src/hooks/useFeedback.ts` — state machine (idle → sending → success → error) via `useReducer` - [ ] Créer `src/components/settings/FeedbackDialog.tsx` — Dialog avec textarea, compteur, 3 checkboxes, handling des codes - [ ] Créer `src/components/settings/FeedbackConsentDialog.tsx` — modal one-time consentement privacy - [ ] Modifier `src/components/settings/LogViewerCard.tsx` — ajouter bouton "Envoyer un feedback" qui ouvre le dialog - [ ] Ajouter helper dans `src/services/logService.ts` — `getRecentErrorLogs(n)` pour extraire les N derniers warn/error formatés en string - [ ] Implémenter composition `userAgent` : `"Simpl'Résultat/<version> (<os>)"` via `tauri::app_handle().package_info()` + `tauri_plugin_os` (si pas déjà en deps, ajouter) - [ ] Persister flag `feedbackConsentAccepted` (localStorage ou config profile) - [ ] Ajouter namespace `feedback.*` dans `src/i18n/locales/fr.json` + `en.json` (table wording ci-dessus) - [ ] MAJ `docs.*` dans les deux locales — divulguer le canal de feedback externe - [ ] MAJ `docs/guide-utilisateur.md` — section feedback - [ ] Entrée CHANGELOG (EN + FR) sous `## [Unreleased]` → `Added` ### Cross-project - [ ] **`maximus/la-compagnie-maximus`** : MAJ `docs/feedback-hub-ops.md` registre apps — ajouter ligne `simpl-resultat` (app_id, placement "Card Settings", CORS = — proxy Rust, date) — issue de tracking créée séparément - [x] **`maximus/simpl-feedback-api`** : aucun changement (proxy Rust, whitelist figée par convention) - [x] **`maximus/simpl-liste` #68** : sibling non bloquant, pas de dépendance ## Fichiers concernés | Fichier | Action | Raison | |---|---|---| | `src-tauri/tauri.conf.json` | Modifier | CSP `connect-src` feedback-api | | `src-tauri/src/commands/feedback_commands.rs` | Créer | Commande `send_feedback` (reqwest POST) | | `src-tauri/src/lib.rs` | Modifier | Enregistrer commande | | `src-tauri/Cargo.toml` | Modifier si besoin | `reqwest` + `tauri-plugin-os` éventuel | | `src/services/feedbackService.ts` | Créer | Wrapper TS sur commande Tauri | | `src/hooks/useFeedback.ts` | Créer | State machine | | `src/components/settings/FeedbackDialog.tsx` | Créer | Dialog textarea + checkboxes | | `src/components/settings/FeedbackConsentDialog.tsx` | Créer | Modal consentement one-time | | `src/components/settings/LogViewerCard.tsx` | Modifier | Ajouter bouton feedback | | `src/services/logService.ts` | Modifier | Helper `getRecentErrorLogs(n)` | | `src/i18n/locales/fr.json` + `en.json` | Modifier | Namespace `feedback.*` + MAJ `docs.*` | | `docs/guide-utilisateur.md` | Modifier | Section feedback | | `CHANGELOG.md` + `CHANGELOG.fr.md` | Modifier | Entrée `[Unreleased] → Added` | ## Surface de test - Aucun test existant sur `SettingsPage`, `LogViewerCard`, `authService`, `logService` - Tests à ajouter : - `feedbackService.test.ts` — invoke wrapper, propagation d'erreurs - `useFeedback.test.ts` — state machine (idle → sending → success/error) - `logService.test.ts` — `getRecentErrorLogs(n)` filtre correctement info/warn/error - Test manuel bout-en-bout : 1. Envoyer un feedback depuis l'app en dev 2. Vérifier côté PostgreSQL : `SELECT * FROM feedback_hub.feedbacks WHERE app_id='simpl-resultat' ORDER BY created_at DESC LIMIT 1` 3. Vérifier que seules les clés whitelistées sont présentes dans `context` ## Critères d'acceptation - [ ] Un utilisateur peut soumettre un feedback texte depuis la card Logs de Settings - [ ] Le premier envoi déclenche un modal de consentement privacy (une seule fois) - [ ] Les 3 checkboxes (contexte, logs, identité Maximus) sont décochées par défaut - [ ] Le checkbox "M'identifier" n'apparaît que si connecté à un compte Maximus - [ ] `user_id` est envoyé uniquement si checkbox cochée ; sinon `null` - [ ] Logs encodés comme suffixe du `content`, pas comme clé `context` custom - [ ] `userAgent` contient `"Simpl'Résultat/<version> (<os>)"` - [ ] Rate limit 429 affiche un message i18n clair ; 400 idem ; 500/erreur réseau idem - [ ] Textarea désactivée si vide ou whitespace-only ; compteur 0/2000 - [ ] Dark mode et i18n FR/EN respectés - [ ] Le feedback apparaît dans `feedback_hub.feedbacks` avec `app_id="simpl-resultat"` et les champs de contexte corrects (test manuel) - [ ] Wording aligne avec le guide ops (voir tableau) - [ ] La documentation utilisateur mentionne le canal de feedback et sa nature externe - [ ] `docs/feedback-hub-ops.md` dans `la-compagnie-maximus` mis à jour (issue de suivi séparée) ## Complexité estimée **Medium** — code client structuré (1 commande Rust, 1 service, 1 hook, 2 composants), pas de changement serveur. Les vrais coûts sont : décisions privacy (consent, divulgation docs), wording i18n aligné multi-apps, tests. ## Dépendances - ✅ Endpoint feedback-api live et conforme au contrat documenté - ✅ Référence d'implé disponible (`la-suite-booking`) - ✅ Maximus OAuth déjà intégré dans simpl-resultat (#51/#53) - Aucun bloqueur externe
maximus added the
status:ready
type:feature
source:human
labels 2026-04-10 19:36:24 +00:00
maximus added
status:review
and removed
status:ready
labels 2026-04-17 14:19:09 +00:00
maximus added
status:approved
and removed
status:review
labels 2026-04-17 14:25:39 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: maximus/Simpl-Resultat#67
No description provided.