From d775e484b68651dacdeadcff2e9b497162d2e702 Mon Sep 17 00:00:00 2001 From: "Fabien F." Date: Tue, 4 Aug 2026 10:54:48 +0200 Subject: [PATCH 01/27] =?UTF-8?q?docs(spec):=20le=20seuil=20de=20puce=20de?= =?UTF-8?q?vient=20un=20r=C3=A9glage,=20et=20le=205=20cesse=20d'=C3=AAtre?= =?UTF-8?q?=20une=20constante?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MIN_PRODUCTS_FOR_CHIP est une constante du front dont le commentaire dit « pas un réglage (ADR-025) ». ADR-024 justifie qu'un seuil EXISTE — en 2022 « Autres » menait à un seul produit, un quart de barre pour une tuile — mais aucune mesure ne dit pourquoi cinq. C'est ce qui le rend discutable, et ADR-057 a déjà tranché le même genre de question en accordant un réglage à `ui.grid_columns`. La conception tient en un champ : `ui.min_products_for_chip`, défaut 5, plancher 1, pas de plafond, appliqué à chaque catégorie sur son propre effectif. Le comportement livré ne bouge pas — un fichier sans la clé retombe sur 5 par le profil neutre. Écarté et dit : le tableau par rayon et son module de phrases (la demande était UN paramètre), le plafond (réversible en revenant sur le champ), et un `0` valant « aucune puce » (autre décision, autre réglage le jour venu). Numéros vérifiés dans le dépôt plutôt que supposés : contrôle 50 (49 est le plus haut), ADR-059 (058 est le plus haut). --- ...026-08-04-seuil-de-puce-reglable-design.md | 209 ++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-04-seuil-de-puce-reglable-design.md diff --git a/docs/superpowers/specs/2026-08-04-seuil-de-puce-reglable-design.md b/docs/superpowers/specs/2026-08-04-seuil-de-puce-reglable-design.md new file mode 100644 index 0000000..d0490f4 --- /dev/null +++ b/docs/superpowers/specs/2026-08-04-seuil-de-puce-reglable-design.md @@ -0,0 +1,209 @@ +# Le seuil de puce devient un réglage — conception + +**Date** : 04/08/2026 · **Portée** : `internal/domain`, `internal/web`, `web/src`, §11.2, +§11.3 (contrôle 50), §14.3-2, §14.4 · **Amende, sans le renverser** : ADR-024 · +**Nouvel ADR** : ADR-059 + +--- + +## 1. Le problème + +Une catégorie n'obtient sa puce de filtre qu'au-delà de **5 produits pesables sur ce +poste**. Le nombre est une constante du front, `MIN_PRODUCTS_FOR_CHIP` +(`web/src/lib/catalog.ts:104`), et son commentaire le dit « CONSTANTE DU CODE, pas un +réglage (ADR-025) ». + +Deux faits, mesurés, encadrent la demande. + +**Le seuil ne masque aucun produit.** `web/test/chips.test.ts:79-92` l'épingle sur +`flv_1.csv`, où « Autres » ne compte qu'un produit : ce produit reste dans « Tout », et +il sort à la recherche. Ce qui disparaît est la **puce**, jamais la tuile. Le masquage +réel est un autre mécanisme, `categories[].visible`, et le test `le seuil et le masquage +sont deux mécanismes distincts` (ligne 104) existe pour qu'on ne les confonde pas. + +**Le 5 est posé, pas mesuré.** ADR-024 justifie l'*existence* d'un seuil — en 2022, +« Autres » menait à un seul produit, soit un quart de barre de navigation pour une +tuile — mais aucune mesure ne dit pourquoi cinq plutôt que trois ou huit. C'est ce qui +rend le nombre légitimement discutable, et c'est le motif de cette conception. + +## 2. Ce qui est décidé + +**Un réglage, un seul** : `ui.min_products_for_chip`, entier, **défaut 5**, **plancher +1**, **pas de plafond**. + +Le précédent est `ui.grid_columns` (ADR-057), qui a amendé ADR-025 au motif que +« combien de produits voir d'un coup » est une décision de magasin qu'aucune mesure +d'écran ne tranche. « À partir de combien de produits une catégorie mérite-t-elle sa +puce » est la même espèce de question : elle dépend de la forme du catalogue d'une +coopérative, et cette forme s'inverse d'un export à l'autre — `flv.csv` donne +`A = 140, V = 118, L = 68, F = 29`, `flv_1.csv` donnait `L = 84, V = 58, F = 10, A = 1`. + +## 3. Ce que le seuil commande, et ce qu'il ne commande pas + +Le seuil s'applique **à chaque catégorie séparément, sur son propre effectif**. Il n'y a +nulle part de réglage sur le nombre de catégories. + +| Seuil | Fruits 28 | Légumes 67 | Vrac 110 | Autres 126 | Barre servie | +| --- | --- | --- | --- | --- | --- | +| 1 | puce | puce | puce | puce | Tout · Fruits · Légumes · Vrac · Autres | +| 5 (défaut) | puce | puce | puce | puce | Tout · Fruits · Légumes · Vrac · Autres | +| 70 | — | — | puce | puce | Tout · Vrac · Autres | +| 999 | — | — | — | — | Tout | + +La dernière ligne n'est pas un second mécanisme : c'est la même règle par catégorie, qui +se trouve échouer sur les quatre. + +**Ce que le seuil ne fait pas** : il ne retire aucun produit de « Tout », ni de la +recherche. Une catégorie sous le seuil garde toutes ses tuiles dans la vue au repos. + +## 4. Le schéma de configuration + +`UIConfig` (`internal/domain/config.go`) reçoit un champ : + +```go +// MinProductsForChip is how many weighable tiles a category needs before the grid +// gives it a filter chip. Default 5, floor 1. +// +// It is a SETTING because what it settles -- « à partir de quand un rayon mérite son +// filtre » -- depends on the SHAPE of a cooperative's catalogue, which no measurement +// answers and which inverts from one export to the next: flv.csv gives A = 140, +// V = 118, L = 68, F = 29 where flv_1.csv gave L = 84, V = 58, F = 10, A = 1 +// (ADR-059, which amends ADR-024 without reversing it). +// +// Under the threshold a category loses its CHIP and never its tiles: its products stay +// in « Tout » and stay searchable. What really takes products off a screen is +// categories[].visible, and the two are not the same decision. +MinProductsForChip int `json:"min_products_for_chip"` +``` + +`NeutralProfile()` (`internal/domain/profiles.go:57-73`) écrit `MinProductsForChip: 5` +en toutes lettres, à côté de `GridColumns`, et pour la même raison qui y est déjà écrite : +ce profil se lit comme la documentation de ce que fait un poste d'usine, et le zéro d'un +champ non renseigné n'y dit rien. + +Un fichier écrit avant ce réglage n'a pas la clé. Le décodage part de `NeutralProfile()` +et `Config.UnmarshalJSON` garde ce que le document ne nomme pas (§11.5) : la valeur +retombe sur 5, donc **le comportement livré ne bouge pas**. Aucune migration n'est +nécessaire, aucune clé n'est retirée, `scanRetired` n'a rien à dire. + +## 5. Le contrôle 50 + +`validateGrid` est le contrôle 49 et reste le plus haut numéro publié. Le nouveau est le +**50**, ajouté après lui, dans une fonction à part : + +```go +// validateChipThreshold is control 50: ui.min_products_for_chip is at least 1. +// +// A floor and no ceiling, on purpose. No pair of bounds is true of every catalogue -- +// the same number is generous on a 355-product export and severe on a 107-product one, +// and the two are the SAME cooperative four years apart. What a ceiling would protect +// against, a value above the biggest shelf, leaves the bar with « Tout » alone and is +// undone by coming back to the field. +// +// The floor is what has no legitimate reading: at 0 a category with no tile at all would +// get a chip, and touching it would open an empty grid. +``` + +La faute nomme le plancher et ce que 1 signifie, comme le contrôle 49 nomme à la fois +l'intervalle et le sens du zéro. + +`TestValidateReportsItsFaultsInTheOrderTheControlsAreNumbered` +(`internal/domain/validate_order_test.go:40`) épingle l'ordre des fautes. Son premier cas +s'appelle « trente champs cassés, du contrôle 1 au contrôle 49 » : il est étendu au +contrôle 50, nom du cas compris. + +## 6. Le chemin jusqu'à l'écran client + +Le réglage voyage dans le bloc `presentation` de `GET /api/v1/catalog`, avec les cinq +autres réglages d'écran. + +1. `catalogPresentationDTO` (`internal/web/catalog.go:110-127`) reçoit + `MinProductsForChip int` ↦ `min_products_for_chip`. +2. `presentationOf` (`catalog.go:134`) le porte. Sa documentation dit qu'elle est + « l'UNIQUE endroit où ce payload se construit, et `presentationDigest` hache ce + qu'elle retourne » : **le champ entre dans l'empreinte tout seul**, donc un écran + client voisin applique le nouveau seuil sans redémarrage, par le flux d'état. +3. Le cache de `catalogBytes` est indexé sur `cfg.Fingerprint()`, qui suit le bloc `ui` : + un rechargement à chaud invalide le payload. +4. `Presentation` (`web/src/lib/catalog.ts:57-78`) reçoit le champ, documenté en TSDoc. +5. `chips()` (`catalog.ts:162`) lit `catalog.presentation.min_products_for_chip` au lieu + de la constante. + +`MIN_PRODUCTS_FOR_CHIP = 5` **reste exportée**, comme défaut de secours d'un payload qui +ne porterait pas la clé, et son commentaire cesse de dire « pas un réglage » pour dire +d'où vient désormais le nombre. + +## 7. L'écran d'administration + +Le réglage s'édite sur **la page Catalogue**, dans le panneau qui porte déjà le nombre de +colonnes. C'est là que `ui.grid_columns` s'édite (`Catalog.svelte:103-112, 181`), par la +même mécanique de brouillon (`draft.number(path)`), et les deux réglages répondent à la +même question — ce que la grille montre. + +- un chemin nommé, `const CHIP_THRESHOLD_PATH = 'ui.min_products_for_chip'` ; +- un champ nombre, plancher 1, sans plafond ; +- `fields.ts` reçoit `'ui.min_products_for_chip': 'Produits minimum pour afficher une catégorie'`, + qui est le libellé lu à la fois par le formulaire et par le tableau de différences. + +Le libellé **est** ce qui répond à « on ne comprendrait pas » : il dit ce que le nombre +commande, à l'endroit où on le tape. + +## 8. Ce qui est écarté + +**Pas de tableau par rayon, pas de module de phrases.** Une annonce « Fruits n'aura pas +de puce : 28 tuiles, sous le seuil de 70 » a été conçue puis retirée : elle demandait un +module de rédaction dans `admin/lib/`, ses tests, et une deuxième formulation à tenir +d'accord avec la première. Le réglage se lit, se change et se relit ; l'écran client d'à +côté montre le résultat en quelques secondes par le flux d'état. La demande était **un +seul paramètre**. + +**Pas de plafond.** Voir §5 : ce qu'un plafond protégerait est réversible en revenant sur +le champ. + +**Pas de valeur « aucune puce ».** Un `0` qui voudrait dire « pas de barre de catégories +sur ce poste » a été écarté : c'est une autre décision, elle mérite son propre réglage le +jour où quelqu'un la demande, et lui donner le zéro d'un compteur de produits obligerait +l'écran à expliquer deux natures pour un champ. + +## 9. Documentation + +| Endroit | Ce qui change | +| --- | --- | +| §11.2 | la clé `ui.min_products_for_chip` entre dans le tableau du bloc `ui` | +| §14.3-2 (ligne 4122) | « seuil : au moins 5 produits pesables sur ce poste » devient le seuil **configuré**, 5 par défaut | +| §14.3 (ligne 4128) | « aucun rayon ne passe sous le seuil de la puce » reste vrai et nomme le défaut | +| ADR-024 (ligne 5145) | reçoit le renvoi « le seuil devient réglable, ADR-059 » sans être réécrit | +| ADR-059 (nouveau) | la décision, sa raison, ses conséquences, sur le modèle d'ADR-057 | +| `testdata/config-lacagette.json` | la clé, à 5, dans le bloc `ui` | +| `handbook/` | **rien**. Vérifié : aucun de ses fichiers ne nomme un réglage du bloc `ui` — ni `grid_columns`, ni `show_by_unit_products`, ni « colonnes de la grille ». Le `handbook/` ne reprend que ce qui met en route (ODR-0002) | + +## 10. Tests + +**Domaine.** Le contrôle 50 refuse 0 et accepte 1 ; l'ordre des fautes va jusqu'à 50 ; le +profil neutre porte 5 ; un document sans la clé décode à 5. + +**Serveur.** `TestEveryFieldOfThePresentationEntersItsDigest` +(`internal/web/catalog_test.go:293`) parcourt le DTO par réflexion : **il couvre le +nouveau champ sans qu'on y touche**, et c'est précisément le mécanisme qu'il existe pour +garantir. Une entrée est ajoutée à la table de +`TestThePresentationDigestFollowsThePresentationAndNothingElse` (ligne 241), et un test +vérifie que le seuil servi est celui de la configuration. + +**Front.** `chips()` suit le seuil servi. Trois tests épinglent aujourd'hui le 5 en dur et +passent au seuil du payload : + +- `web/test/chips.test.ts:71` — « mesure bien une catégorie sous le seuil de 5 » ; +- `web/test/chips.test.ts:107-113` — « à 4 produits pas de puce, à 5 une puce » ; +- `web/test/unit-products.test.ts:95` — le commentaire qui nomme la constante. + +Un test neuf montre le même catalogue rendant quatre puces à seuil 5 et deux à seuil 70, +sur `flv.csv`, avec les effectifs réels. + +**Vérification** : `.\make.ps1 test` (passe `-race` puis `CGO_ENABLED=0`), `.\make.ps1 vet`, +`.\make.ps1 front-check`. + +## 11. Conséquence assumée + +Un seuil supérieur au plus gros rayon laisse la barre avec « Tout » seul, et rien ne le +signale avant l'enregistrement. C'est le prix de « un seul paramètre », il est réversible +en revenant sur le nombre, et il a été énoncé avant d'être retenu. From e105774ad4dba8f6391c4ecff26d87d80f8ac49b Mon Sep 17 00:00:00 2001 From: "Fabien F." Date: Tue, 4 Aug 2026 11:08:39 +0200 Subject: [PATCH 02/27] =?UTF-8?q?docs(plan):=20le=20plan=20du=20seuil=20r?= =?UTF-8?q?=C3=A9glable,=20et=20le=20pi=C3=A8ge=20que=20l'=C3=A9crire=20a?= =?UTF-8?q?=20trouv=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit La spec disait « la clé, à 5, dans testdata/config-lacagette.json ». Faux, et dangereux : les trois aides de test du dépôt décodent le fichier livré dans un Config ZÉRO, et le fichier livré ne porte pas non plus grid_columns — il se tait sur ce qu'il ne règle pas. Un contrôle à plancher 1 aurait donc refusé la configuration livrée, et les bancs auraient servi un seuil de 0, soit une puce à toute catégorie y compris vide. C'est le défaut du 28/07/2026 que config_test.go:281-284 nomme déjà. grid_columns y échappe parce que son défaut sûr EST le zéro, délibérément. Le seuil de puce n'a pas cette chance, alors Config.UnmarshalJSON corrige son zéro vers le défaut, comme il le fait déjà pour update.repository, à trois lignes de là. La spec gagne un §4 bis qui l'énonce, et le fichier livré reste muet. --- .../2026-08-04-seuil-de-puce-reglable.md | 805 ++++++++++++++++++ ...026-08-04-seuil-de-puce-reglable-design.md | 61 +- 2 files changed, 859 insertions(+), 7 deletions(-) create mode 100644 docs/superpowers/plans/2026-08-04-seuil-de-puce-reglable.md diff --git a/docs/superpowers/plans/2026-08-04-seuil-de-puce-reglable.md b/docs/superpowers/plans/2026-08-04-seuil-de-puce-reglable.md new file mode 100644 index 0000000..9a6685d --- /dev/null +++ b/docs/superpowers/plans/2026-08-04-seuil-de-puce-reglable.md @@ -0,0 +1,805 @@ +# Le seuil de puce devient un réglage — plan d'implémentation + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** `MIN_PRODUCTS_FOR_CHIP`, constante du front à 5, devient `ui.min_products_for_chip` — réglable par poste depuis l'écran d'administration, sans changer le comportement livré. + +**Architecture:** Un champ dans `UIConfig`, un contrôle de validation numéroté 50, un champ de plus dans le DTO `presentation` de `GET /api/v1/catalog` — qui entre dans `presentationDigest` par réflexion, donc l'écran client voisin l'applique sans redémarrage —, et une lecture dans `chips()` à la place de la constante. Le réglage s'édite sur la page Catalogue, à côté du nombre de colonnes, par la même mécanique de brouillon. + +**Tech Stack:** Go (zéro cgo), Svelte 5 + TypeScript, vitest, `make.ps1`. + +**Spec:** `docs/superpowers/specs/2026-08-04-seuil-de-puce-reglable-design.md` + +## Global Constraints + +- **Code en anglais** — identifiants, types, champs, clés de configuration **et commentaires**. **Documentation et messages utilisateurs en français.** (CLAUDE.md) +- **Commentaires : le POURQUOI, jamais le QUOI.** Le dépôt écrit des commentaires qui donnent la raison et la mesure. Un commentaire qui paraphrase la ligne suivante est un défaut. +- **Aucun nombre recopié dans un second endroit.** Le défaut vaut 5 et il est déclaré **une fois**, dans `DefaultMinProductsForChip`. +- **Go idiomatique gagne sur Clean Code** en cas de conflit. +- **`godoc`** en Go (commentaire commençant par le nom de l'élément, phrase complète), **TSDoc** en TypeScript et Svelte. +- **Vérification** : `.\make.ps1 test` (passe `-race` puis `CGO_ENABLED=0`), `.\make.ps1 vet`, `.\make.ps1 front-check`. Go n'est pas dans le PATH système de ce poste : préfixer par `$env:Path = "C:\Program Files\Go\bin;" + [Environment]::GetEnvironmentVariable('Path','User') + ";$env:Path"`. +- **Jamais de lien de session** dans un message de commit, une PR ou un fichier du dépôt. + +--- + +## File Structure + +| Fichier | Responsabilité, après ce plan | +| --- | --- | +| `internal/domain/config.go` | déclare `DefaultMinProductsForChip` et le champ `UIConfig.MinProductsForChip` | +| `internal/domain/config_json.go` | `Config.UnmarshalJSON` normalise le zéro vers le défaut, comme il le fait déjà pour `Update.Repository` | +| `internal/domain/profiles.go` | le profil neutre écrit le défaut en toutes lettres | +| `internal/domain/validate_settings.go` | le contrôle 50, dans sa propre fonction | +| `internal/domain/validate.go` | appelle le contrôle 50 après le 49 | +| `internal/web/catalog.go` | le champ dans `catalogPresentationDTO` et dans `presentationOf` | +| `web/src/lib/catalog.ts` | le champ dans `Presentation` ; `chips()` lit le seuil servi | +| `web/src/admin/pages/Catalog.svelte` | le champ nombre, à côté des colonnes | +| `web/src/admin/lib/fields.ts` | le libellé français de la clé | +| `docs/02-architecture.md` | §11.2, §14.3-2, §14.3, renvoi dans ADR-024, ADR-059 | + +--- + +### Task 1 : Le champ, son défaut, et le silence du fichier livré + +**Files:** +- Modify: `internal/domain/config.go` (constantes de grille ~143-165, `UIConfig` 196-228) +- Modify: `internal/domain/config_json.go:145-154` (`Config.UnmarshalJSON`) +- Modify: `internal/domain/profiles.go:57-73` (bloc `UI` du profil neutre) +- Test: `internal/domain/config_test.go` + +**Interfaces:** +- Produces: `domain.DefaultMinProductsForChip` (constante `int`, valeur 5) et `domain.UIConfig.MinProductsForChip` (`int`, clé JSON `min_products_for_chip`). Les tâches 2, 3 et 4 en dépendent. + +> **Pourquoi cette tâche est la première et pourquoi elle est seule.** `internal/domain/fixture_test.go:34`, `internal/web/harness_test.go:274` et `internal/station/doubles_test.go:31` décodent tous les trois le fichier livré dans un `Config` **zéro**, et `testdata/config-lacagette.json` ne porte pas la clé. Sans la normalisation ci-dessous, le champ vaudrait 0 partout, le contrôle 50 de la tâche 2 refuserait le fichier livré, et les bancs serviraient un seuil de 0 — une puce à toute catégorie, y compris vide. C'est le défaut du 28/07/2026 que `config_test.go:281-284` nomme déjà. + +- [ ] **Step 1: Write the failing test** + +Dans `internal/domain/config_test.go`, à la suite de `TestTheDeliveredFileNeedNotCarryTheGridColumns` (ligne 285-294) : + +```go +// TestTheDeliveredFileNeedNotCarryTheChipThreshold is the symmetric of the test above, +// for the one setting whose safe default is NOT its zero value. +// +// grid_columns escapes this because GridColumnsAutomatic is zero on purpose -- « a +// BEHAVIOUR and not a number ». The chip threshold has no such luck: its default is 5, +// its floor is 1, and its zero means nothing at all. The three test helpers of this +// repository decode the delivered file into a ZERO Config, so without the normalisation +// in Config.UnmarshalJSON a station would refuse its own delivered configuration -- +// which is the defect of 28/07/2026, word for word. +func TestTheDeliveredFileNeedNotCarryTheChipThreshold(t *testing.T) { + config := loadDelivered(t) + if config.UI.MinProductsForChip != DefaultMinProductsForChip { + t.Fatalf("seuil relu à %d sur un fichier qui ne dit rien, attendu le défaut %d", + config.UI.MinProductsForChip, DefaultMinProductsForChip) + } +} + +// TestAHandWrittenZeroReadsBackAsTheDefault: zero has no legitimate reading here -- it +// would give a chip to a category with no tile, whose press opens an empty grid. It is +// CORRECTED rather than refused, exactly as an empty update.repository is, because +// telling « absent » from « zero » would cost a *int or a codec of its own for UIConfig. +func TestAHandWrittenZeroReadsBackAsTheDefault(t *testing.T) { + var config Config + if err := json.Unmarshal([]byte(`{"version":1,"ui":{"min_products_for_chip":0}}`), &config); err != nil { + t.Fatalf("décodage : %v", err) + } + if config.UI.MinProductsForChip != DefaultMinProductsForChip { + t.Fatalf("seuil relu à %d, attendu le défaut %d", + config.UI.MinProductsForChip, DefaultMinProductsForChip) + } +} + +// TestAThresholdTheFileNamesIsKept, so the normalisation above corrects the zero and +// nothing else. +func TestAThresholdTheFileNamesIsKept(t *testing.T) { + var config Config + if err := json.Unmarshal([]byte(`{"version":1,"ui":{"min_products_for_chip":12}}`), &config); err != nil { + t.Fatalf("décodage : %v", err) + } + if config.UI.MinProductsForChip != 12 { + t.Fatalf("seuil relu à %d, attendu 12", config.UI.MinProductsForChip) + } +} +``` + +Vérifier d'abord que `encoding/json` est déjà importé dans ce fichier ; il l'est (`config_test.go:271` fait `json.Unmarshal`). + +- [ ] **Step 2: Run test to verify it fails** + +```powershell +$env:Path = "C:\Program Files\Go\bin;" + [Environment]::GetEnvironmentVariable('Path','User') + ";$env:Path" +go test ./internal/domain/ -run 'ChipThreshold|HandWrittenZero|ThresholdTheFileNames' -v +``` + +Expected: FAIL à la compilation — `undefined: DefaultMinProductsForChip`, `config.UI.MinProductsForChip undefined`. + +- [ ] **Step 3: Write minimal implementation** + +Dans `internal/domain/config.go`, ajouter la constante dans le bloc `const` qui porte déjà `GridColumnsAutomatic`, `MinGridColumns` et `MaxGridColumns` (lignes 143-165), après `MaxGridColumns = 12` : + +```go + // DefaultMinProductsForChip is how many weighable tiles a category needs, by + // default, before the grid gives it a filter chip. + // + // Five is what ADR-024 shipped, and it is kept as the default for that reason + // alone: what that ADR MEASURED is that a threshold has to exist -- in 2022 + // « Autres » led to a single product, a quarter of the navigation bar for one tile + // -- and no measurement says five rather than three or eight. That is what makes + // the number an operator's to set (ADR-059). + // + // It is NOT the zero value, and that is the whole difficulty this constant exists + // to answer: see Config.UnmarshalJSON. + DefaultMinProductsForChip = 5 +``` + +Dans le même fichier, ajouter le champ à `UIConfig`, après `GridColumns` (ligne 227) : + +```go + // MinProductsForChip is how many weighable tiles a category needs before the grid + // gives it a filter chip. Its default is DefaultMinProductsForChip, its floor is 1. + // + // It is a SETTING because what it settles -- « à partir de quand un rayon mérite son + // filtre » -- depends on the SHAPE of a cooperative's catalogue, which no measurement + // answers and which inverts from one export to the next: flv.csv gives A = 140, + // V = 118, L = 68, F = 29 where flv_1.csv gave L = 84, V = 58, F = 10, A = 1 + // (ADR-059, which amends ADR-024 without reversing it). + // + // Under the threshold a category loses its CHIP and never its tiles: its products + // stay in « Tout » and stay searchable. What really takes products off a screen is + // categories[].visible, and the two are not the same decision. + MinProductsForChip int `json:"min_products_for_chip"` +``` + +Dans `internal/domain/config_json.go`, dans `Config.UnmarshalJSON`, juste après le bloc qui rattrape `Update.Repository` (lignes 146-151) et avant `c.retired = nil` : + +```go + // Zero is not a threshold, it is a file that says nothing -- and the delivered file + // is one of them, on purpose (§11.2). Refusing it here would make a station refuse + // its own delivered configuration, which is the defect of 28/07/2026; correcting it + // is what the block above already does for a repository nobody named. + if c.UI.MinProductsForChip == 0 { + c.UI.MinProductsForChip = DefaultMinProductsForChip + } +``` + +Dans `internal/domain/profiles.go`, dans le bloc `UI` du profil neutre, après `GridColumns: GridColumnsAutomatic,` (ligne 72) : + +```go + // Written out although UnmarshalJSON would correct a zero anyway: this + // profile is read as the documentation of what a factory station does, and + // a field left to its zero value documents nothing. + MinProductsForChip: DefaultMinProductsForChip, +``` + +- [ ] **Step 4: Run test to verify it passes** + +```powershell +go test ./internal/domain/ -run 'ChipThreshold|HandWrittenZero|ThresholdTheFileNames' -v +``` + +Expected: PASS, trois tests. + +- [ ] **Step 5: Run the whole domain package** + +```powershell +go test ./internal/domain/ -count=1 +``` + +Expected: PASS. Si `TestNeutralProfile…` ou un test d'export échoue sur un champ de plus, c'est attendu : lire la faute, et **ne corriger que ce que le champ neuf a bougé**. + +- [ ] **Step 6: Commit** + +```bash +git add internal/domain/config.go internal/domain/config_json.go internal/domain/profiles.go internal/domain/config_test.go +git commit -m "feat(domain): le seuil de puce entre dans la configuration, et son zéro se corrige" +``` + +--- + +### Task 2 : Le contrôle 50 + +**Files:** +- Modify: `internal/domain/validate_settings.go` (après `validateGrid`, qui finit ligne 425) +- Modify: `internal/domain/validate.go:103` (la liste des appels de `Config.Validate`) +- Test: `internal/domain/validate_test.go`, `internal/domain/validate_order_test.go:47` + +**Interfaces:** +- Consumes: `domain.UIConfig.MinProductsForChip`, `domain.DefaultMinProductsForChip` (tâche 1). +- Produces: une faute de champ `ui.min_products_for_chip`. Rien d'autre n'en dépend. + +- [ ] **Step 1: Write the failing test** + +Dans `internal/domain/validate_test.go`, à la suite des tests du contrôle 49 : + +```go +// TestControl50RefusesAThresholdUnderOne guards the value that has no reading. +// +// A floor and NO ceiling, deliberately: no pair of bounds is true of every catalogue -- +// the same number is generous on a 355-product export and severe on a 107-product one, +// and those two are the SAME cooperative four years apart. A threshold above the biggest +// shelf leaves the bar with « Tout » alone and is undone by coming back to the field; +// a threshold under 1 would give a chip to a category with no tile, whose press opens an +// empty grid. +func TestControl50RefusesAThresholdUnderOne(t *testing.T) { + for _, refused := range []int{-1, -5} { + t.Run(strconv.Itoa(refused), func(t *testing.T) { + config := loadDelivered(t) + config.UI.MinProductsForChip = refused + faults := config.Validate(testRegistries()) + if findFault(faults, "ui.min_products_for_chip") == nil { + t.Fatalf("%d est accepté par le contrôle 50 ; obtenu :\n%s", + refused, strings.Join(fieldsOf(faults), "\n")) + } + }) + } +} + +// TestControl50AcceptsOneAndAnythingAbove: 1 is « toute catégorie non vide a sa puce », +// and there is no upper bound to refuse. +func TestControl50AcceptsOneAndAnythingAbove(t *testing.T) { + for _, accepted := range []int{1, DefaultMinProductsForChip, 70, 999} { + t.Run(strconv.Itoa(accepted), func(t *testing.T) { + config := loadDelivered(t) + config.UI.MinProductsForChip = accepted + if fault := findFault(config.Validate(testRegistries()), "ui.min_products_for_chip"); fault != nil { + t.Fatalf("%d est refusé par le contrôle 50 : %s", accepted, fault.Message) + } + }) + } +} + +// TestControl50HasNothingToSayAboutTheDeliveredFile, which does not carry the key. +func TestControl50HasNothingToSayAboutTheDeliveredFile(t *testing.T) { + config := loadDelivered(t) + if fault := findFault(config.Validate(testRegistries()), "ui.min_products_for_chip"); fault != nil { + t.Fatalf("le silence du fichier livré est traité comme une faute : %s", fault.Message) + } +} +``` + +- [ ] **Step 2: Run test to verify it fails** + +```powershell +go test ./internal/domain/ -run 'TestControl50' -v +``` + +Expected: FAIL — `TestControl50RefusesAThresholdUnderOne` échoue sur « -1 est accepté par le contrôle 50 » (aucun contrôle ne regarde encore ce champ). + +- [ ] **Step 3: Write minimal implementation** + +Dans `internal/domain/validate_settings.go`, après `validateGrid` (qui finit ligne 425) : + +```go +// validateChipThreshold is control 50: ui.min_products_for_chip is at least 1. +// +// A floor and no ceiling. What a ceiling would guard against -- a threshold above the +// biggest shelf, which leaves the category bar with « Tout » alone -- is undone by coming +// back to the field, and no pair of bounds is true of every catalogue: the same number is +// generous on the 355-product export of 2026 and severe on the 107-product one of 2022, +// and those are the same cooperative. +// +// The floor is the half that has no legitimate reading: under 1, a category with no tile +// at all would get a chip, and pressing it would open an empty grid. Zero never reaches +// here -- Config.UnmarshalJSON corrects it to DefaultMinProductsForChip, because a file +// that says nothing must not be refused (§11.2) -- so what this control catches is a +// negative somebody typed. +func (c *Config) validateChipThreshold() []Fault { + var faults faultList + if c.UI.MinProductsForChip < 1 { + faults.add("ui.min_products_for_chip", + "%d n'est pas un nombre de produits : à partir de 1, une catégorie obtient sa "+ + "puce dès qu'elle a ce nombre de tuiles sur ce poste", + c.UI.MinProductsForChip) + } + return faults +} +``` + +Dans `internal/domain/validate.go`, après la ligne 103 : + +```go + faults = append(faults, c.validateChipThreshold()...) // 50 +``` + +- [ ] **Step 4: Run test to verify it passes** + +```powershell +go test ./internal/domain/ -run 'TestControl50' -v +``` + +Expected: PASS, trois tests (dont six sous-cas). + +- [ ] **Step 5: Extend the fault-order test** + +Dans `internal/domain/validate_order_test.go`, le premier cas s'appelle `"trente champs cassés, du contrôle 1 au contrôle 49"` (ligne 47) et le commentaire de tête ligne 37 dit « from control 1 to control 49 ». Les deux deviennent 50, et le cas casse un champ de plus. Ajouter, à la fin de la liste des champs cassés du `break_` et **avant** sa fermeture, la ligne qui casse le contrôle 50 : + +```go + c.UI.MinProductsForChip = -1 // 50 +``` + +et ajouter `"ui.min_products_for_chip"` **en dernier** de la liste `want`. Lire la liste avant d'écrire : l'ordre attendu est celui des appels de `Config.Validate`, et le contrôle 50 est le dernier. + +- [ ] **Step 6: Run the whole domain package** + +```powershell +go test ./internal/domain/ -count=1 +``` + +Expected: PASS. + +- [ ] **Step 7: Commit** + +```bash +git add internal/domain/validate_settings.go internal/domain/validate.go internal/domain/validate_test.go internal/domain/validate_order_test.go +git commit -m "feat(domain): contrôle 50, un plancher sur le seuil de puce et pas de plafond" +``` + +--- + +### Task 3 : Le seuil voyage jusqu'à l'écran client + +**Files:** +- Modify: `internal/web/catalog.go` (`catalogPresentationDTO` 110-127, `presentationOf` 134-143) +- Test: `internal/web/catalog_test.go` + +**Interfaces:** +- Consumes: `domain.UIConfig.MinProductsForChip` (tâche 1). +- Produces: la clé JSON `min_products_for_chip` dans le bloc `presentation` de `GET /api/v1/catalog`. La tâche 4 en dépend. + +> `presentationOf` est documentée comme « l'UNIQUE endroit où ce payload se construit, et `presentationDigest` hache ce qu'elle retourne ». `TestEveryFieldOfThePresentationEntersItsDigest` (`catalog_test.go:293`) parcourt le DTO par réflexion : **il couvrira le nouveau champ sans qu'on y touche**, et il échouera si le champ est ajouté au DTO sans être rempli par `presentationOf`. + +- [ ] **Step 1: Write the failing test** + +Dans `internal/web/catalog_test.go`, à la suite de `TestTheCategoryLabelsFollowTheConfigurationAndNotTheLastImport` : + +```go +// TestTheChipThresholdTravelsWithTheCatalog: the grid decides which categories get a +// chip, and it decides it on a number this station sets (ADR-059). +// +// It rides in `presentation` with the other screen settings and for the same reason: the +// station states the setting, the grid applies it. Which categories end up with a chip is +// never computed here -- the payload stays the inventory of what this station shows. +func TestTheChipThresholdTravelsWithTheCatalog(t *testing.T) { + b := newBench(t, func(o *benchOptions) { + o.config = func(c *domain.Config) { c.UI.MinProductsForChip = 12 } + }) + + page := decodeStatus[catalogDTO](t, b.get("/api/v1/catalog"), http.StatusOK) + + if page.Options.MinProductsForChip != 12 { + t.Fatalf("presentation.min_products_for_chip = %d, attendu 12", + page.Options.MinProductsForChip) + } +} +``` + +Et ajouter une entrée à la table de `TestThePresentationDigestFollowsThePresentationAndNothingElse` (ligne 245), à côté de `"le nombre de colonnes"` : + +```go + "le seuil de puce": func(c *domain.Config) { c.UI.MinProductsForChip = 12 }, +``` + +- [ ] **Step 2: Run test to verify it fails** + +```powershell +go test ./internal/web/ -run 'TestTheChipThresholdTravelsWithTheCatalog' -v +``` + +Expected: FAIL à la compilation — `page.Options.MinProductsForChip undefined`. + +- [ ] **Step 3: Write minimal implementation** + +Dans `internal/web/catalog.go`, ajouter au `catalogPresentationDTO`, après `GridColumns` (ligne 126) : + +```go + // MinProductsForChip is how many tiles a category needs before the grid gives it a + // filter chip (ADR-059). Stated here and applied by the grid, like the settings + // above: which categories end up with a chip depends on what the grid actually shows, + // and the grid is the only side that knows it. + MinProductsForChip int `json:"min_products_for_chip"` +``` + +Et le remplir dans `presentationOf` (ligne 135-142) : + +```go + MinProductsForChip: ui.MinProductsForChip, +``` + +- [ ] **Step 4: Run test to verify it passes** + +```powershell +go test ./internal/web/ -run 'TestTheChipThreshold|TestThePresentationDigest|TestEveryFieldOfThePresentation' -v +``` + +Expected: PASS, tous. + +- [ ] **Step 5: Run the whole web package** + +```powershell +go test ./internal/web/ -count=1 +``` + +Expected: PASS. + +- [ ] **Step 6: Commit** + +```bash +git add internal/web/catalog.go internal/web/catalog_test.go +git commit -m "feat(web): le seuil de puce voyage avec le catalogue et entre dans l'empreinte" +``` + +--- + +### Task 4 : La grille lit le seuil servi + +**Files:** +- Modify: `web/src/lib/catalog.ts` (`Presentation` 56-78, `MIN_PRODUCTS_FOR_CHIP` 98-104, `chips` 147-182) +- Test: `web/test/chips.test.ts`, `web/test/unit-products.test.ts:89-101` + +**Interfaces:** +- Consumes: la clé `min_products_for_chip` du bloc `presentation` (tâche 3). +- Produces: `chips()` inchangée de signature — `chips(catalog: Catalog): Chip[]`. + +- [ ] **Step 1: Write the failing test** + +Dans `web/test/chips.test.ts`, à la fin du fichier, un `describe` neuf : + +```ts +describe('le seuil vient de la configuration du poste', () => { + const catalog = catalogFromExport('flv.csv') + + it('à 5 — le défaut livré — les quatre rayons ont leur puce', () => { + const served: Catalog = { + ...catalog, + presentation: { ...catalog.presentation, min_products_for_chip: 5 }, + } + expect(chips(served).map((c) => c.code)).toEqual([ + ALL_CATEGORIES, + 'fruits', + 'vegetables', + 'bulk', + 'other', + ]) + }) + + it('à 70, Fruits (28) et Légumes (67) perdent la leur, Vrac (110) et Autres (126) la gardent', () => { + const served: Catalog = { + ...catalog, + presentation: { ...catalog.presentation, min_products_for_chip: 70 }, + } + expect(chips(served).map((c) => c.code)).toEqual([ALL_CATEGORIES, 'bulk', 'other']) + }) + + it('ne retire aucun produit de « Tout » en retirant une puce', () => { + const served: Catalog = { + ...catalog, + presentation: { ...catalog.presentation, min_products_for_chip: 70 }, + } + // 331 pesables, et le poste de référence ne masque rien : le compte de « Tout » ne + // bouge pas d'un produit quand deux puces disparaissent. + expect(chips(served)[0]?.count).toBe(331) + expect(visibleProducts(served)).toHaveLength(331) + }) + + it('retombe sur le défaut quand le poste ne sert pas la clé', () => { + // Un binaire plus ancien que ce réglage : la barre reste celle d'aujourd'hui. + const { min_products_for_chip: _dropped, ...older } = catalog.presentation + const served = { ...catalog, presentation: older } as Catalog + expect(chips(served).map((c) => c.code)).toEqual([ + ALL_CATEGORIES, + 'fruits', + 'vegetables', + 'bulk', + 'other', + ]) + }) +}) +``` + +Vérifier que `catalogFromExport` (`web/test/fixtures/odoo.ts`) construit bien un `presentation`. S'il ne porte pas encore `min_products_for_chip`, l'y ajouter à `MIN_PRODUCTS_FOR_CHIP` — la fixture décrit ce qu'un poste sert. + +- [ ] **Step 2: Run test to verify it fails** + +```powershell +npm --prefix web test -- chips +``` + +Expected: FAIL — le cas à 70 rend encore les cinq puces, parce que `chips()` lit toujours la constante. + +- [ ] **Step 3: Write minimal implementation** + +Dans `web/src/lib/catalog.ts`, ajouter à l'interface `Presentation`, après `grid_columns` (ligne 77) : + +```ts + /** + * Le nombre de tuiles qu'une catégorie doit avoir sur ce poste pour obtenir sa puce + * de filtre. Défaut 5. + * + * En deçà, la catégorie perd sa PUCE et jamais ses tuiles : ses produits restent dans + * « Tout » et à la recherche. Ce qui retire vraiment des produits de l'écran est + * `categories[].visible`, et les deux ne sont pas la même décision (ADR-059). + */ + min_products_for_chip: number +``` + +Réécrire le commentaire de la constante (lignes 98-104) — elle cesse d'être la règle pour devenir le défaut : + +```ts +/** + * Le seuil qu'applique un poste dont la configuration ne dit rien. + * + * C'ÉTAIT une constante du code (ADR-024, ADR-025). Ce qu'ADR-024 a mesuré est qu'un + * seuil doit EXISTER — en 2022, « Autres » menait à UN produit, un quart de barre de + * navigation pour une tuile —, et aucune mesure ne dit cinq plutôt que trois ou huit. + * C'est ce qui en fait le réglage `ui.min_products_for_chip` (ADR-059), dont ce nombre + * n'est plus que le défaut : il ne sert qu'aux postes dont le binaire est plus ancien + * que la clé. + */ +export const MIN_PRODUCTS_FOR_CHIP = 5 +``` + +Dans `chips()` (ligne 162), remplacer la lecture de la constante : + +```ts +export function chips(catalog: Catalog): Chip[] { + const shown = visibleProducts(catalog) + const counts = new Map() + for (const p of shown) counts.set(p.category_code, (counts.get(p.category_code) ?? 0) + 1) + + // Le seuil vient du POSTE, et la constante n'est plus qu'un filet pour un service qui + // ne sert pas encore la clé (ADR-059). + const threshold = catalog.presentation.min_products_for_chip ?? MIN_PRODUCTS_FOR_CHIP + + const populated = catalog.categories + .filter((c) => c.visible && (counts.get(c.code) ?? 0) >= threshold) + .slice() + .sort((a, b) => a.rank - b.rank || a.code.localeCompare(b.code)) + .map((c) => ({ + code: c.code, + label: c.label, + color: c.color, + count: counts.get(c.code) ?? 0, + })) + + return [ + { code: ALL_CATEGORIES, label: 'Tout', color: 'var(--ink-muted)', count: shown.length }, + ...populated, + ] +} +``` + +Et mettre à jour le paragraphe TSDoc de `chips()` (lignes 147-161) qui dit aujourd'hui « une puce par catégorie PEUPLÉE » : il nomme désormais le réglage et non un nombre. + +- [ ] **Step 4: Run test to verify it passes** + +```powershell +npm --prefix web test -- chips +``` + +Expected: PASS. + +- [ ] **Step 5: Fix the two tests that pinned the constant as the rule** + +`web/test/chips.test.ts:68-72` et `:107-113` épinglent le 5 par `MIN_PRODUCTS_FOR_CHIP`. Ils restent **valides et utiles** — ils décrivent le comportement d'un poste au défaut — mais leurs libellés disent « le seuil de 5 » comme si c'était la règle. Les relire et corriger le libellé, pas l'assertion : le seuil servi par la fixture EST le défaut. + +`web/test/unit-products.test.ts:94-95` porte un commentaire qui nomme la constante ; le laisser exact. + +- [ ] **Step 6: Run the whole front suite** + +```powershell +npm --prefix web test +``` + +Expected: PASS. + +- [ ] **Step 7: Commit** + +```bash +git add web/src/lib/catalog.ts web/test/chips.test.ts web/test/unit-products.test.ts web/test/fixtures/odoo.ts +git commit -m "feat(front): la barre de catégories suit le seuil que le poste sert" +``` + +--- + +### Task 5 : Le réglage sur l'écran d'administration + +**Files:** +- Modify: `web/src/admin/pages/Catalog.svelte` (constantes ~102-112, gabarit ~825-873) +- Modify: `web/src/admin/lib/fields.ts:31` +- Test: `web/test/admin-catalog.test.ts` + +**Interfaces:** +- Consumes: la clé `ui.min_products_for_chip` (tâches 1 et 3), `draft.number(path)` et `draft.set(path, value)` déjà employés pour `ui.grid_columns`. + +> **Pourquoi un champ nombre et pas des boutons radio.** Les colonnes sont onze valeurs nommées et bornées ; le seuil n'a pas de plafond, donc rien à énumérer. + +- [ ] **Step 1: Write the failing test** + +Dans `web/test/admin-catalog.test.ts`, sur le modèle des tests voisins qui pilotent le brouillon (lire d'abord comment ils montent la page et comment ils lisent `draft`) : + +```ts +it('écrit le seuil de puce dans le brouillon', async () => { + const field = screen.getByLabelText(/produits minimum pour afficher une catégorie/i) + await fireEvent.input(field, { target: { value: '3' } }) + expect(draft.number('ui.min_products_for_chip')).toBe(3) +}) +``` + +- [ ] **Step 2: Run test to verify it fails** + +```powershell +npm --prefix web test -- admin-catalog +``` + +Expected: FAIL — aucun champ ne porte ce libellé. + +- [ ] **Step 3: Write minimal implementation** + +Dans `web/src/admin/pages/Catalog.svelte`, après `GRID_COLUMNS_CHOICES` (ligne 112) : + +```ts + /** The key that decides from how many tiles a category gets its filter chip. */ + const CHIP_THRESHOLD_PATH = 'ui.min_products_for_chip' +``` + +Et à côté de `gridColumns` (ligne 181) : + +```ts + /** The chip threshold AS DRAFTED. */ + const chipThreshold = $derived(draft.number(CHIP_THRESHOLD_PATH)) +``` + +Dans le gabarit, après le bloc `data-grid-count` et sa phrase « Se tromper ne coûte rien… » (lignes 862-873) : + +```svelte +

+ + {#if preferences.showTechnicalNames}{CHIP_THRESHOLD_PATH}{/if} +

+ draft.set(CHIP_THRESHOLD_PATH, Number(event.currentTarget.value))} + /> +

+ En dessous de ce nombre, la catégorie n’a pas de puce dans la barre du bas. Ses + produits restent dans « Tout » et à la recherche : ce réglage ne retire aucune + tuile. Pour ne plus montrer une catégorie du tout, c’est sa case « visible » qu’il + faut décocher. +

+``` + +Dans `web/src/admin/lib/fields.ts`, après la ligne 31 : + +```ts + 'ui.min_products_for_chip': 'Produits minimum pour afficher une catégorie', +``` + +- [ ] **Step 4: Run test to verify it passes** + +```powershell +npm --prefix web test -- admin-catalog +``` + +Expected: PASS. + +- [ ] **Step 5: Run the whole front suite and the budget check** + +```powershell +npm --prefix web test +$env:Path = "C:\Program Files\Go\bin;" + [Environment]::GetEnvironmentVariable('Path','User') + ";$env:Path" +.\make.ps1 front-check +``` + +Expected: PASS, et le budget de l'écran client tenu — ce champ est hors écran client. + +- [ ] **Step 6: Commit** + +```bash +git add web/src/admin/pages/Catalog.svelte web/src/admin/lib/fields.ts web/test/admin-catalog.test.ts +git commit -m "feat(admin): le seuil de puce se règle à côté des colonnes de la grille" +``` + +--- + +### Task 6 : La documentation et l'ADR-059 + +**Files:** +- Modify: `docs/02-architecture.md` — §11.2 (tableau du bloc `ui`), §14.3-2 (ligne 4122), §14.3 (ligne 4128), ADR-024 (ligne 5145), et l'ADR-059 après l'ADR-058 (ligne 5856) + +> **Aucun compteur à recopier.** `SUIVI.md` porte les chiffres du projet ; ce plan n'y touche pas et n'écrit nulle part un nombre d'ADR ou de tests. + +- [ ] **Step 1: Locate the §11.2 `ui` table** + +```powershell +Select-String -Path docs/02-architecture.md -Pattern 'show_by_unit_products|grid_columns' | Select-Object -First 10 +``` + +Ajouter la ligne de `min_products_for_chip` dans le tableau du bloc `ui`, au format des lignes voisines, en disant : le défaut 5, le plancher 1, pas de plafond, et que le seuil retire une puce et jamais une tuile. + +- [ ] **Step 2: §14.3-2, ligne 4122** + +La phrase dit aujourd'hui : + +> une puce par catégorie **peuplée** (seuil : au moins 5 produits pesables sur ce poste ; en deçà, la catégorie reste dans « Tout » et ses produits restent atteignables par la recherche) + +Elle devient le seuil **configuré**, `ui.min_products_for_chip`, 5 par défaut, en gardant mot pour mot la seconde moitié — « en deçà, la catégorie reste dans "Tout" » — qui est ce que le lecteur doit retenir. + +- [ ] **Step 3: §14.3, ligne 4128** + +« aucun rayon ne passe sous le seuil de la puce » reste vrai ; nommer le défaut plutôt que laisser croire à un nombre figé. + +- [ ] **Step 4: ADR-024, ligne 5145** + +N'y toucher qu'en ajoutant un renvoi, sans réécrire la décision : le seuil devient réglable par ADR-059, ce qui **amende ADR-024 sans le renverser** — l'existence du seuil, qu'ADR-024 a mesurée, ne bouge pas. + +- [ ] **Step 5: Write ADR-059, after ADR-058 (line 5856)** + +Sur le modèle d'ADR-057 (ligne 5772), avec sa ligne de statut : + +``` +**Statut** : accepté · **Date** : 04/08/2026 · **Portée** : `internal/domain`, `internal/web`, `web/src`, §11.2, §11.3 (contrôle 50), §14.3-2, §14.4 · **Amende, sans le renverser** : ADR-024 · **Renvoie à** : ADR-025 +``` + +**Contexte** : le seuil est une constante du front dont le commentaire dit « pas un réglage (ADR-025) ». ADR-024 a mesuré qu'un seuil doit exister — `A = 1` en 2022, un quart de barre pour une tuile — mais aucune mesure ne dit cinq. + +**Décision** : `ui.min_products_for_chip`, défaut 5, plancher 1, pas de plafond, appliqué à chaque catégorie sur son propre effectif. + +**Conséquences** : (a) le comportement livré ne bouge pas, le fichier livré se tait et §4 bis de la spec rend ce silence sûr ; (b) un seuil supérieur au plus gros rayon laisse « Tout » seul, et c'est réversible en revenant sur le champ ; (c) le seuil retire une puce et jamais une tuile — `categories[].visible` reste le seul mécanisme qui retire des produits ; (d) le champ entre dans `presentationDigest` par réflexion, donc l'écran client voisin l'applique sans redémarrage. + +- [ ] **Step 6: Commit** + +```bash +git add docs/02-architecture.md +git commit -m "docs(architecture): ADR-059, le seuil de puce devient un réglage" +``` + +--- + +### Task 7 : Vérification complète + +- [ ] **Step 1: The whole Go suite, both passes** + +```powershell +$env:Path = "C:\Program Files\Go\bin;" + [Environment]::GetEnvironmentVariable('Path','User') + ";$env:Path" +.\make.ps1 test +``` + +Expected: les deux passes vertes — `-race` puis `CGO_ENABLED=0`. + +- [ ] **Step 2: vet, boundary, deps** + +```powershell +.\make.ps1 vet +``` + +Expected: exit 0. + +- [ ] **Step 3: The front** + +```powershell +.\make.ps1 front-check +``` + +Expected: vert, budget de l'écran client tenu. + +- [ ] **Step 4: Break the guarantee to check it holds** + +Remettre temporairement `chips()` sur la constante au lieu du seuil servi, relancer `npm --prefix web test -- chips`, **vérifier que le nouveau cas à 70 devient rouge**, puis annuler la modification. Un test qui passe des deux côtés ne protège rien. + +- [ ] **Step 5: Show the output** + +Coller la sortie des trois cibles dans le rapport final. CLAUDE.md : « Avant de déclarer une tâche terminée, exécuter la vérification et en montrer la sortie. » + +--- + +## Self-Review + +**Couverture de la spec.** §2 le réglage → tâche 1 ; §3 la règle par catégorie → tâche 4 ; §4 le schéma → tâche 1 ; §4 bis la clé absente → tâche 1, steps 1 et 3 ; §5 le contrôle 50 → tâche 2 ; §6 le chemin jusqu'à l'écran → tâches 3 et 4 ; §7 l'écran d'administration → tâche 5 ; §8 ce qui est écarté → rien à faire, par construction ; §9 la documentation → tâche 6 ; §10 les tests → répartis ; §11 la conséquence assumée → ADR-059, tâche 6 step 5. + +**Cohérence des noms.** `DefaultMinProductsForChip` (Go, constante), `MinProductsForChip` (champ Go, DTO Go), `min_products_for_chip` (clé JSON, champ TS), `MIN_PRODUCTS_FOR_CHIP` (constante TS, défaut de secours), `CHIP_THRESHOLD_PATH` (chemin de brouillon), `validateChipThreshold` (contrôle 50). Employés à l'identique d'une tâche à l'autre. + +**Ordre.** La tâche 1 précède tout : sans elle, rien ne compile. Les tâches 2 à 6 sont indépendantes entre elles et peuvent être menées en parallèle. La tâche 7 les suit toutes. diff --git a/docs/superpowers/specs/2026-08-04-seuil-de-puce-reglable-design.md b/docs/superpowers/specs/2026-08-04-seuil-de-puce-reglable-design.md index d0490f4..c5058cf 100644 --- a/docs/superpowers/specs/2026-08-04-seuil-de-puce-reglable-design.md +++ b/docs/superpowers/specs/2026-08-04-seuil-de-puce-reglable-design.md @@ -81,10 +81,53 @@ en toutes lettres, à côté de `GridColumns`, et pour la même raison qui y est ce profil se lit comme la documentation de ce que fait un poste d'usine, et le zéro d'un champ non renseigné n'y dit rien. -Un fichier écrit avant ce réglage n'a pas la clé. Le décodage part de `NeutralProfile()` -et `Config.UnmarshalJSON` garde ce que le document ne nomme pas (§11.5) : la valeur -retombe sur 5, donc **le comportement livré ne bouge pas**. Aucune migration n'est -nécessaire, aucune clé n'est retirée, `scanRetired` n'a rien à dire. +### 4 bis. La clé absente, et le piège du 28/07/2026 + +Un fichier écrit avant ce réglage n'a pas la clé — **le fichier livré non plus**, et il +ne doit pas l'avoir : `TestTheDeliveredFileNeedNotCarryTheGridColumns` +(`internal/domain/config_test.go:285`) énonce la règle et nomme le défaut qu'elle +prévient, celui du 28/07/2026, « où une clé neuve a fait refuser à un poste sa propre +configuration livrée ». + +`ui.grid_columns` y échappe parce que **son défaut sûr EST le zéro**, délibérément : +`GridColumnsAutomatic = 0` est « un COMPORTEMENT et pas un nombre ». Le seuil de puce n'a +pas cette chance — son défaut est 5, son zéro n'a aucun sens, et son plancher est 1. Sans +précaution, un `Config` décodé depuis le fichier livré porterait `0`, le contrôle 50 +produirait une faute sur le fichier livré, et les bancs de `internal/web` et +`internal/station` — qui décodent tous les trois dans un `Config` **zéro** +(`fixture_test.go:34`, `internal/web/harness_test.go:274`, +`internal/station/doubles_test.go:31`) — serviraient un seuil de 0, donc une puce à toute +catégorie, y compris vide. + +Le remède existe déjà dans la méthode qui décode, et pour exactement ce problème : + +```go +// A file that names no repository -- one written before this block existed, +// or one that carries it empty -- runs on the default. Refusing here would put +// a station out of service over a field nobody meant to set. +if c.Update.Repository == "" { + c.Update.Repository = DefaultUpdateRepository +} +``` + +`Config.UnmarshalJSON` reçoit la même normalisation, juste en dessous : + +```go +if c.UI.MinProductsForChip == 0 { + c.UI.MinProductsForChip = DefaultMinProductsForChip +} +``` + +`DefaultMinProductsForChip = 5` est déclarée à côté de `MinGridColumns`, et +`NeutralProfile()` l'écrit en toutes lettres. Tous les chemins de décodage — +`DecodeConfigBlockByBlock`, les trois aides de test, un `json.Unmarshal` nu — atterrissent +donc sur 5 quand la clé manque. **Le comportement livré ne bouge pas.** Aucune migration, +aucune clé retirée, `scanRetired` n'a rien à dire. + +Conséquence assumée : un `0` écrit à la main se relit 5, comme un dépôt vide se relit le +dépôt par défaut. Le zéro n'a pas de lecture légitime ici — il donnerait une puce à une +catégorie sans tuile — et le refuser plutôt que le corriger obligerait à distinguer +« absent » de « zéro », donc un `*int` ou un décodeur maison pour `UIConfig`. ## 5. Le contrôle 50 @@ -174,13 +217,17 @@ l'écran à expliquer deux natures pour un champ. | §14.3 (ligne 4128) | « aucun rayon ne passe sous le seuil de la puce » reste vrai et nomme le défaut | | ADR-024 (ligne 5145) | reçoit le renvoi « le seuil devient réglable, ADR-059 » sans être réécrit | | ADR-059 (nouveau) | la décision, sa raison, ses conséquences, sur le modèle d'ADR-057 | -| `testdata/config-lacagette.json` | la clé, à 5, dans le bloc `ui` | +| `testdata/config-lacagette.json` | **rien.** Le fichier livré ne porte pas non plus `grid_columns` : il se tait sur ce qu'il ne règle pas, et §4 bis est ce qui rend ce silence sûr | | `handbook/` | **rien**. Vérifié : aucun de ses fichiers ne nomme un réglage du bloc `ui` — ni `grid_columns`, ni `show_by_unit_products`, ni « colonnes de la grille ». Le `handbook/` ne reprend que ce qui met en route (ODR-0002) | ## 10. Tests -**Domaine.** Le contrôle 50 refuse 0 et accepte 1 ; l'ordre des fautes va jusqu'à 50 ; le -profil neutre porte 5 ; un document sans la clé décode à 5. +**Domaine.** Le contrôle 50 refuse une valeur négative et accepte 1 ; l'ordre des fautes va +jusqu'à 50 ; le profil neutre porte 5 ; un document sans la clé décode à 5, et un `0` +écrit à la main aussi. Et surtout, le symétrique de +`TestTheDeliveredFileNeedNotCarryTheGridColumns` : **le fichier livré se tait sur ce +réglage et le contrôle 50 n'a rien à en dire** — c'est le test qui empêche le défaut du +28/07/2026 de revenir par cette clé-ci. **Serveur.** `TestEveryFieldOfThePresentationEntersItsDigest` (`internal/web/catalog_test.go:293`) parcourt le DTO par réflexion : **il couvre le From 134430a14f504e09525fa83fd4d6a59065beb67f Mon Sep 17 00:00:00 2001 From: "Fabien F." Date: Tue, 4 Aug 2026 11:09:25 +0200 Subject: [PATCH 03/27] =?UTF-8?q?fix(web):=20les=20libell=C3=A9s=20de=20la?= =?UTF-8?q?=20grille=20suivent=20la=20configuration,=20pas=20le=20dernier?= =?UTF-8?q?=20import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit catalogOf lisait les rayons dans le snapshot — donc dans la table SQLite `categories`, qui n'existe que comme parent de la clé étrangère products.category_code et que seul un IMPORT met à jour. Renommer un rayon dans config.json n'atteignait l'écran qu'au prochain catalogue publié par le producteur : un rayon renommé le lundi gardait son ancien nom jusqu'à l'export du vendredi, sans rien à l'écran pour le dire. §10.2 bis dit pourtant que la configuration porte le libellé, le rang, la couleur et « montrer cette catégorie sur ce poste », et §11.4 que ces quatre-là rechargent à chaud. Le commentaire du cache, dix lignes plus haut, l'écrivait déjà : « les catégories, les tarifs et les trois réglages d'écran viennent de la CONFIGURATION ». C'est la clé du cache qui avait raison, pas la boucle. L'empreinte de configuration n'était épinglée que sur `Visible` ; le cache du payload en dépend, donc le test couvre maintenant aussi le libellé. Une assertion épinglait l'ancien couplage (« une seule catégorie servie ») : elle vise en fait l'effectif du rayon de l'ail, et c'est ce qu'elle vérifie désormais. --- internal/domain/fingerprint_test.go | 5 ++ internal/web/catalog.go | 12 ++++- internal/web/catalog_test.go | 74 ++++++++++++++++++++++++++++- 3 files changed, 89 insertions(+), 2 deletions(-) diff --git a/internal/domain/fingerprint_test.go b/internal/domain/fingerprint_test.go index de0b504..d8d67c1 100644 --- a/internal/domain/fingerprint_test.go +++ b/internal/domain/fingerprint_test.go @@ -63,6 +63,11 @@ func TestFingerprintChangesWhenASharedValueChanges(t *testing.T) { "un seuil de panier": func(c *Config) { c.Limits.BasketMin = -300 }, "le gabarit": func(c *Config) { c.Printer.Template = "weighing_neutral_single" }, "une catégorie": func(c *Config) { c.Catalog.Categories[0].Visible = false }, + // The WORDING and not only the visibility: internal/web keys its serialized + // catalog on this digest, and the labels of the grid are read from the + // configuration (§10.2 bis). A renamed shelf that left the eight characters + // alone would be served from the cache with its old name until the next import. + "le libellé d'une catégorie": func(c *Config) { c.Catalog.Categories[0].Label = "Fruits d'ici" }, "la rétention du journal": func(c *Config) { c.Journal.MaxDays = 30 }, // Two stations that disagree here do not show the same grid: one offers fifteen // tiles the other does not have, and the eight characters have to say so. diff --git a/internal/web/catalog.go b/internal/web/catalog.go index cd30ee7..ac31026 100644 --- a/internal/web/catalog.go +++ b/internal/web/catalog.go @@ -306,7 +306,17 @@ func (s *Server) catalogOf(ctx context.Context, catalog *domain.Catalog, cfg dom } out.ProductCount = len(out.Products) - for _, c := range catalog.Categories() { + // From the CONFIGURATION, and not from the snapshot. The exchange file carries a + // letter; the label, the rank, the colour and « montrer cette catégorie sur ce + // poste » are shop decisions that live in config.json and reload hot (§10.2 bis, + // §11.4). The snapshot carries them too — the store hands back the `categories` + // table, which exists as the parent of the foreign key products.category_code and + // which only an IMPORT ever upserts. Reading the wording of the grid from there tied + // a configuration change to the next catalog a producer happens to publish: a shelf + // renamed on Monday kept its old name until the Friday export, with nothing on any + // screen to say why. The effectif below still comes from the catalog in service, + // because that is the one number a customer can check against the tiles. + for _, c := range cfg.Catalog.Categories { out.Categories = append(out.Categories, categoryDTO{ Code: c.Code, Label: c.Label, Rank: c.Rank, Color: c.Color, Visible: c.Visible, ProductCount: counts[c.Code], diff --git a/internal/web/catalog_test.go b/internal/web/catalog_test.go index 3f4223a..ba1e698 100644 --- a/internal/web/catalog_test.go +++ b/internal/web/catalog_test.go @@ -39,7 +39,17 @@ func TestTheCatalogIsServedWholeWithAValidator(t *testing.T) { if len(tile.Prices) != 2 || byCode["MEMBER"] != "4,79" || byCode["SOLIDARITY"] != "5,32" { t.Fatalf("tarifs de la tuile = %+v, attendu MEMBER=4,79 SOLIDARITY=5,32", tile.Prices) } - if len(page.Categories) != 1 || page.Categories[0].ProductCount != 1 { + // The FOUR shelves the configuration declares, and not the one the snapshot of this + // bench happens to carry: the shelves belong to config.json (§10.2 bis). A shelf with + // no tile costs the screen nothing — the grid gives a chip only from + // MIN_PRODUCTS_FOR_CHIP products up — and the payload stays the inventory of what + // this station is configured to show. What is asserted here is the effectif, which is + // counted on the catalog in service. + byCategory := map[string]int{} + for _, shelf := range page.Categories { + byCategory[shelf.Code] = shelf.ProductCount + } + if len(page.Categories) != 4 || byCategory["vegetables"] != 1 { t.Fatalf("catégories = %+v", page.Categories) } @@ -347,6 +357,68 @@ func TestTheStationServesEveryWeighableTileWhateverTheGridShows(t *testing.T) { } } +// TestTheCategoryLabelsFollowTheConfigurationAndNotTheLastImport pins §10.2 bis: the +// exchange file carries a LETTER, and the configuration carries the label, the rank, +// the colour and « montrer cette catégorie sur ce poste ». +// +// Those four reload hot (§11.4), so a volunteer who renames a shelf sees it on the grid +// without waiting for a producer to publish a file. Reading them from the snapshot +// instead made them wait: `categories` is a table an IMPORT upserts — it exists as the +// parent of the foreign key products.category_code — so a rename applied on the next +// catalog and on nothing else, and a station whose producer publishes weekly showed the +// old wording for a week with no way to tell why. +// +// The snapshot below therefore carries what the last import wrote, and the +// configuration carries what somebody has just typed. What the screen gets is the +// second one. +func TestTheCategoryLabelsFollowTheConfigurationAndNotTheLastImport(t *testing.T) { + imported := domain.NewCatalog([]domain.Product{ + {ID: "1", Name: "AIL", Reference: "0493021000003", Mode: domain.ByWeight, + UnitPrice: 532, CategoryCode: "vegetables", Qualification: domain.Weighable}, + }, []domain.Category{ + {Code: "vegetables", Label: "Légumes", Rank: 2, Color: "#27AE60", Visible: true}, + }) + + b := newBench(t, func(o *benchOptions) { + o.catalog = imported + o.config = func(c *domain.Config) { + for i := range c.Catalog.Categories { + if c.Catalog.Categories[i].Code != "vegetables" { + continue + } + c.Catalog.Categories[i].Label = "Légumes du jardin" + c.Catalog.Categories[i].Rank = 1 + c.Catalog.Categories[i].Color = "#1E8449" + } + } + }) + + page := decodeStatus[catalogDTO](t, b.get("/api/v1/catalog"), http.StatusOK) + + var shelf categoryDTO + for _, c := range page.Categories { + if c.Code == "vegetables" { + shelf = c + } + } + switch { + case shelf.Code == "": + t.Fatalf("la catégorie « vegetables » n'est pas servie : %+v", page.Categories) + case shelf.Label != "Légumes du jardin": + t.Errorf("libellé servi = %q : c'est celui du dernier import, pas celui du "+ + "fichier de configuration", shelf.Label) + case shelf.Rank != 1: + t.Errorf("rang servi = %d, attendu 1 : l'ordre de la barre vient de la "+ + "configuration", shelf.Rank) + case shelf.Color != "#1E8449": + t.Errorf("couleur servie = %q, attendu « #1E8449 »", shelf.Color) + } + if shelf.ProductCount != 1 { + t.Errorf("%d tuile(s) comptée(s) pour la catégorie, attendu 1 : l'effectif se "+ + "compte toujours sur le catalogue en service", shelf.ProductCount) + } +} + // TestWhatHasNoTileIsNotServedToTheScreen: a prepackaged product is not an error, it // is not the scale's business, and it has no tile (§10.3, ADR-021). func TestWhatHasNoTileIsNotServedToTheScreen(t *testing.T) { From 63861dbd03d08769b40e30bd568b97c6a1432b8b Mon Sep 17 00:00:00 2001 From: "Fabien F." Date: Tue, 4 Aug 2026 11:12:40 +0200 Subject: [PATCH 04/27] =?UTF-8?q?feat(domain):=20le=20seuil=20de=20puce=20?= =?UTF-8?q?entre=20dans=20la=20configuration,=20et=20son=20z=C3=A9ro=20se?= =?UTF-8?q?=20corrige?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/domain/config.go | 25 +++++++++++++++++++ internal/domain/config_json.go | 7 ++++++ internal/domain/config_test.go | 44 ++++++++++++++++++++++++++++++++++ internal/domain/profiles.go | 4 ++++ 4 files changed, 80 insertions(+) diff --git a/internal/domain/config.go b/internal/domain/config.go index fc119cd..cca9d81 100644 --- a/internal/domain/config.go +++ b/internal/domain/config.go @@ -162,6 +162,18 @@ const ( // because the typographic floor came down to 16 px: at 18, twelve columns on a 15" // cropped 38 prices and this ceiling would have had to be 11 (ADR-057). MaxGridColumns = 12 + // DefaultMinProductsForChip is how many weighable tiles a category needs, by + // default, before the grid gives it a filter chip. + // + // Five is what ADR-024 shipped, and it is kept as the default for that reason + // alone: what that ADR MEASURED is that a threshold has to exist -- in 2022 + // « Autres » led to a single product, a quarter of the navigation bar for one tile + // -- and no measurement says five rather than three or eight. That is what makes + // the number an operator's to set (ADR-059). + // + // It is NOT the zero value, and that is the whole difficulty this constant exists + // to answer: see Config.UnmarshalJSON. + DefaultMinProductsForChip = 5 ) // UIConfig holds the screen settings an operator has a legitimate choice about. @@ -225,6 +237,19 @@ type UIConfig struct { // which no measurement of a screen can answer. It is an OVERRIDE and never a // replacement (ADR-057). GridColumns int `json:"grid_columns"` + // MinProductsForChip is how many weighable tiles a category needs before the grid + // gives it a filter chip. Its default is DefaultMinProductsForChip, its floor is 1. + // + // It is a SETTING because what it settles -- « à partir de quand un rayon mérite son + // filtre » -- depends on the SHAPE of a cooperative's catalogue, which no measurement + // answers and which inverts from one export to the next: flv.csv gives A = 140, + // V = 118, L = 68, F = 29 where flv_1.csv gave L = 84, V = 58, F = 10, A = 1 + // (ADR-059, which amends ADR-024 without reversing it). + // + // Under the threshold a category loses its CHIP and never its tiles: its products + // stay in « Tout » and stay searchable. What really takes products off a screen is + // categories[].visible, and the two are not the same decision. + MinProductsForChip int `json:"min_products_for_chip"` } // ScaleConfig is the weighing device of this station. diff --git a/internal/domain/config_json.go b/internal/domain/config_json.go index aad4e27..ca3cb61 100644 --- a/internal/domain/config_json.go +++ b/internal/domain/config_json.go @@ -149,6 +149,13 @@ func (c *Config) UnmarshalJSON(raw []byte) error { if c.Update.Repository == "" { c.Update.Repository = DefaultUpdateRepository } + // Zero is not a threshold, it is a file that says nothing -- and the delivered file + // is one of them, on purpose (§11.2). Refusing it here would make a station refuse + // its own delivered configuration, which is the defect of 28/07/2026; correcting it + // is what the block above already does for a repository nobody named. + if c.UI.MinProductsForChip == 0 { + c.UI.MinProductsForChip = DefaultMinProductsForChip + } c.retired = nil scanRetired("", document, &c.retired) return nil diff --git a/internal/domain/config_test.go b/internal/domain/config_test.go index 9c5abcf..0d5b230 100644 --- a/internal/domain/config_test.go +++ b/internal/domain/config_test.go @@ -292,3 +292,47 @@ func TestTheDeliveredFileNeedNotCarryTheGridColumns(t *testing.T) { t.Fatalf("le silence du fichier livré est traité comme une faute : %s", fault.Message) } } + +// TestTheDeliveredFileNeedNotCarryTheChipThreshold is the symmetric of the test above, +// for the one setting whose safe default is NOT its zero value. +// +// grid_columns escapes this because GridColumnsAutomatic is zero on purpose -- « a +// BEHAVIOUR and not a number ». The chip threshold has no such luck: its default is 5, +// its floor is 1, and its zero means nothing at all. The three test helpers of this +// repository decode the delivered file into a ZERO Config, so without the normalisation +// in Config.UnmarshalJSON a station would refuse its own delivered configuration -- +// which is the defect of 28/07/2026, word for word. +func TestTheDeliveredFileNeedNotCarryTheChipThreshold(t *testing.T) { + config := loadDelivered(t) + if config.UI.MinProductsForChip != DefaultMinProductsForChip { + t.Fatalf("seuil relu à %d sur un fichier qui ne dit rien, attendu le défaut %d", + config.UI.MinProductsForChip, DefaultMinProductsForChip) + } +} + +// TestAHandWrittenZeroReadsBackAsTheDefault: zero has no legitimate reading here -- it +// would give a chip to a category with no tile, whose press opens an empty grid. It is +// CORRECTED rather than refused, exactly as an empty update.repository is, because +// telling « absent » from « zero » would cost a *int or a codec of its own for UIConfig. +func TestAHandWrittenZeroReadsBackAsTheDefault(t *testing.T) { + var config Config + if err := json.Unmarshal([]byte(`{"version":1,"ui":{"min_products_for_chip":0}}`), &config); err != nil { + t.Fatalf("décodage : %v", err) + } + if config.UI.MinProductsForChip != DefaultMinProductsForChip { + t.Fatalf("seuil relu à %d, attendu le défaut %d", + config.UI.MinProductsForChip, DefaultMinProductsForChip) + } +} + +// TestAThresholdTheFileNamesIsKept, so the normalisation above corrects the zero and +// nothing else. +func TestAThresholdTheFileNamesIsKept(t *testing.T) { + var config Config + if err := json.Unmarshal([]byte(`{"version":1,"ui":{"min_products_for_chip":12}}`), &config); err != nil { + t.Fatalf("décodage : %v", err) + } + if config.UI.MinProductsForChip != 12 { + t.Fatalf("seuil relu à %d, attendu 12", config.UI.MinProductsForChip) + } +} diff --git a/internal/domain/profiles.go b/internal/domain/profiles.go index 8a7f189..79d0828 100644 --- a/internal/domain/profiles.go +++ b/internal/domain/profiles.go @@ -70,6 +70,10 @@ func NeutralProfile() Config { // on the 24" of the parc, ten on a 4K. A profile that froze a count here would // hand every station of every cooperative the density measured on one of them. GridColumns: GridColumnsAutomatic, + // Written out although UnmarshalJSON would correct a zero anyway: this + // profile is read as the documentation of what a factory station does, and + // a field left to its zero value documents nothing. + MinProductsForChip: DefaultMinProductsForChip, }, Scale: ScaleConfig{ From c9c6b1ccfd55c7d9c41e9f978b38ae196b46edb6 Mon Sep 17 00:00:00 2001 From: "Fabien F." Date: Tue, 4 Aug 2026 11:20:25 +0200 Subject: [PATCH 05/27] =?UTF-8?q?feat(domain):=20contr=C3=B4le=2050,=20un?= =?UTF-8?q?=20plancher=20sur=20le=20seuil=20de=20puce=20et=20pas=20de=20pl?= =?UTF-8?q?afond?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/domain/validate.go | 1 + internal/domain/validate_order_test.go | 8 +++-- internal/domain/validate_settings.go | 24 ++++++++++++++ internal/domain/validate_test.go | 44 ++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 3 deletions(-) diff --git a/internal/domain/validate.go b/internal/domain/validate.go index bae4617..78c5eb0 100644 --- a/internal/domain/validate.go +++ b/internal/domain/validate.go @@ -101,6 +101,7 @@ func (c *Config) Validate(reg Registries) []Fault { faults = append(faults, c.validateDropDirectory(reg)...) // 46 faults = append(faults, c.validateUpdate()...) // 48 faults = append(faults, c.validateGrid()...) // 49 + faults = append(faults, c.validateChipThreshold()...) // 50 return faults } diff --git a/internal/domain/validate_order_test.go b/internal/domain/validate_order_test.go index 0f6d958..3a3aac2 100644 --- a/internal/domain/validate_order_test.go +++ b/internal/domain/validate_order_test.go @@ -33,8 +33,8 @@ import "testing" // station and a document that disagree about which control is 46 cost more than // the tidiness gained. // -// The two cases below are deliberately different in nature. The first breaks thirty -// fields at once and walks the whole numbering from control 1 to control 49. The +// The two cases below are deliberately different in nature. The first breaks thirty-one +// fields at once and walks the whole numbering from control 1 to control 50. The // second is the one a lookup by field could never hold: printer.options.transport is // reported THREE times, by controls 7, 8 and 42, and only its POSITION says which. func TestValidateReportsItsFaultsInTheOrderTheControlsAreNumbered(t *testing.T) { @@ -44,7 +44,7 @@ func TestValidateReportsItsFaultsInTheOrderTheControlsAreNumbered(t *testing.T) want []string }{ { - name: "trente champs cassés, du contrôle 1 au contrôle 49", + name: "trente-et-un champs cassés, du contrôle 1 au contrôle 50", break_: func(c *Config, _ *Registries) { c.Station.Number = 0 // 1 c.Network.Listen = "pas une adresse" // 2 @@ -74,6 +74,7 @@ func TestValidateReportsItsFaultsInTheOrderTheControlsAreNumbered(t *testing.T) c.Catalog.Images.Source = "jpeg" // 44 c.Update.Repository = "https://exemple.test/depot" // 48 c.UI.GridColumns = 1 // 49 + c.UI.MinProductsForChip = -1 // 50 }, want: []string{ "station.number", // 1 @@ -103,6 +104,7 @@ func TestValidateReportsItsFaultsInTheOrderTheControlsAreNumbered(t *testing.T) "catalog.images.source", // 44 "update.repository", // 48 "ui.grid_columns", // 49 + "ui.min_products_for_chip", // 50 }, }, { diff --git a/internal/domain/validate_settings.go b/internal/domain/validate_settings.go index 5c56337..40c7f0e 100644 --- a/internal/domain/validate_settings.go +++ b/internal/domain/validate_settings.go @@ -424,6 +424,30 @@ func (c *Config) validateGrid() []Fault { return faults } +// validateChipThreshold is control 50: ui.min_products_for_chip is at least 1. +// +// A floor and no ceiling. What a ceiling would guard against -- a threshold above the +// biggest shelf, which leaves the category bar with « Tout » alone -- is undone by coming +// back to the field, and no pair of bounds is true of every catalogue: the same number is +// generous on the 355-product export of 2026 and severe on the 107-product one of 2022, +// and those are the same cooperative. +// +// The floor is the half that has no legitimate reading: under 1, a category with no tile +// at all would get a chip, and pressing it would open an empty grid. Zero never reaches +// here -- Config.UnmarshalJSON corrects it to DefaultMinProductsForChip, because a file +// that says nothing must not be refused (§11.2) -- so what this control catches is a +// negative somebody typed. +func (c *Config) validateChipThreshold() []Fault { + var faults faultList + if c.UI.MinProductsForChip < 1 { + faults.add("ui.min_products_for_chip", + "%d n'est pas un nombre de produits : à partir de 1, une catégorie obtient sa "+ + "puce dès qu'elle a ce nombre de tuiles sur ce poste", + c.UI.MinProductsForChip) + } + return faults +} + // stabilityModes reports the two admissible values of stability.mode. func stabilityModes() []string { return []string{ModeAdvisory, ModeBlocking} } diff --git a/internal/domain/validate_test.go b/internal/domain/validate_test.go index 63eec0b..4a047b2 100644 --- a/internal/domain/validate_test.go +++ b/internal/domain/validate_test.go @@ -466,3 +466,47 @@ func TestControl49SaysWhatZeroMeans(t *testing.T) { } } } + +// TestControl50RefusesAThresholdUnderOne guards the value that has no reading. +// +// A floor and NO ceiling, deliberately: no pair of bounds is true of every catalogue -- +// the same number is generous on a 355-product export and severe on a 107-product one, +// and those two are the SAME cooperative four years apart. A threshold above the biggest +// shelf leaves the bar with « Tout » alone and is undone by coming back to the field; +// a threshold under 1 would give a chip to a category with no tile, whose press opens an +// empty grid. +func TestControl50RefusesAThresholdUnderOne(t *testing.T) { + for _, refused := range []int{-1, -5} { + t.Run(strconv.Itoa(refused), func(t *testing.T) { + config := loadDelivered(t) + config.UI.MinProductsForChip = refused + faults := config.Validate(testRegistries()) + if findFault(faults, "ui.min_products_for_chip") == nil { + t.Fatalf("%d est accepté par le contrôle 50 ; obtenu :\n%s", + refused, strings.Join(fieldsOf(faults), "\n")) + } + }) + } +} + +// TestControl50AcceptsOneAndAnythingAbove: 1 is « toute catégorie non vide a sa puce », +// and there is no upper bound to refuse. +func TestControl50AcceptsOneAndAnythingAbove(t *testing.T) { + for _, accepted := range []int{1, DefaultMinProductsForChip, 70, 999} { + t.Run(strconv.Itoa(accepted), func(t *testing.T) { + config := loadDelivered(t) + config.UI.MinProductsForChip = accepted + if fault := findFault(config.Validate(testRegistries()), "ui.min_products_for_chip"); fault != nil { + t.Fatalf("%d est refusé par le contrôle 50 : %s", accepted, fault.Message) + } + }) + } +} + +// TestControl50HasNothingToSayAboutTheDeliveredFile, which does not carry the key. +func TestControl50HasNothingToSayAboutTheDeliveredFile(t *testing.T) { + config := loadDelivered(t) + if fault := findFault(config.Validate(testRegistries()), "ui.min_products_for_chip"); fault != nil { + t.Fatalf("le silence du fichier livré est traité comme une faute : %s", fault.Message) + } +} From f12e6c211f8b97b0f0d6f6ee4ccc6114e2acdd05 Mon Sep 17 00:00:00 2001 From: "Fabien F." Date: Tue, 4 Aug 2026 11:23:12 +0200 Subject: [PATCH 06/27] =?UTF-8?q?docs(domain):=20l'en-t=C3=AAte=20de=20val?= =?UTF-8?q?idate=5Ftest.go=20dit=20le=20crit=C3=A8re,=20pas=20un=20d=C3=A9?= =?UTF-8?q?compte?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit « 48 controls » était déjà faux avant le contrôle 50 (il y en avait 49), et l'énumération des contrôles à station propre aurait dû gagner un 50 de plus : deux nombres qui pourrissent au premier contrôle ajouté ou retiré. L'en-tête dit maintenant ce qui distingue ces tests -- une configuration entière et un poste à eux, pas un seul champ muté sur le corpus -- sans dénombrer ni énumérer. --- internal/domain/validate_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/domain/validate_test.go b/internal/domain/validate_test.go index 4a047b2..731ceab 100644 --- a/internal/domain/validate_test.go +++ b/internal/domain/validate_test.go @@ -1,7 +1,7 @@ -// This file holds what the 48 controls answer BEYOND the corpus: everything at -// once, the admissible values a fault carries, what an EMPTY registry may and may -// not say, and the controls whose behaviour needs a station of its own to show -- -// 3, 29, 43, 44, 46, 48 and 49. +// This file holds what the numbered controls answer BEYOND the corpus: everything at +// once, the admissible values a fault carries, what an EMPTY registry may and may not +// say, and the controls whose behaviour a single mutated field cannot show -- the ones +// that need a whole delivered configuration and a station of its own. package domain From f60b2cb6bf3a4a8ace5d1d8799539ba4d0ce4fc8 Mon Sep 17 00:00:00 2001 From: "Fabien F." Date: Tue, 4 Aug 2026 11:33:11 +0200 Subject: [PATCH 07/27] =?UTF-8?q?docs(domain):=20trois=20autres=20=C2=AB?= =?UTF-8?q?=2048=20controls=20=C2=BB=20et=20un=20nom=20de=20test=20incompl?= =?UTF-8?q?et?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit validate.go, config.go et validate_options.go répétaient chacun le même décompte que l'en-tête de validate_test.go corrigé juste avant -- déjà faux avant le contrôle 50 (49 contrôles existaient déjà), et faux plus fort maintenant. Un nombre recopié à plusieurs endroits ne se met pas à jour tout seul ; les trois disent désormais « the controls » sans les compter, pour rester vrais quel que soit leur nombre. TestControl50HasNothingToSayAboutTheDeliveredFile portait un nom sans proposition complète après le deux-points, contrairement à la convention du fichier. Corrigé en même temps que sa réouverture. --- internal/domain/config.go | 2 +- internal/domain/validate.go | 2 +- internal/domain/validate_options.go | 2 +- internal/domain/validate_test.go | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/internal/domain/config.go b/internal/domain/config.go index cca9d81..4f9809c 100644 --- a/internal/domain/config.go +++ b/internal/domain/config.go @@ -11,7 +11,7 @@ import "time" // copy it onto a USB stick, and the process must start and show an administration // screen even when the database is corrupt. // -// Nothing here reads a clock, opens a file or a socket: the 48 controls that judge +// Nothing here reads a clock, opens a file or a socket: the controls that judge // these types are pure (validate.go), and the two questions a pure function cannot // answer -- "does this path exist?", "is this print queue really enumerated?" -- // arrive through Registries (options.go). diff --git a/internal/domain/validate.go b/internal/domain/validate.go index 78c5eb0..7060882 100644 --- a/internal/domain/validate.go +++ b/internal/domain/validate.go @@ -5,7 +5,7 @@ import ( "strings" ) -// This file holds the ENTRY POINT of the 48 controls a configuration has to pass, +// This file holds the ENTRY POINT of the controls a configuration has to pass, // and the ones that judge THE HARDWARE OF THE STATION AND THE LABEL IT PRINTS: the // number it answers to, the address it listens on, the three drivers it names, the // options they declare, the numbering plan, and the template with its offsets. diff --git a/internal/domain/validate_options.go b/internal/domain/validate_options.go index 7c48fb5..6701b0d 100644 --- a/internal/domain/validate_options.go +++ b/internal/domain/validate_options.go @@ -11,7 +11,7 @@ import ( // the schema THE CHOSEN DRIVER declares, rather than against anything this package // knows. // -// It is separate from validate.go for that reason: the 48 controls know what a +// It is separate from validate.go for that reason: the numbered controls know what a // configuration means, these know only what a driver said about itself. // validateOptions reports every fault the options of one driver break against the diff --git a/internal/domain/validate_test.go b/internal/domain/validate_test.go index 731ceab..2daa556 100644 --- a/internal/domain/validate_test.go +++ b/internal/domain/validate_test.go @@ -503,7 +503,8 @@ func TestControl50AcceptsOneAndAnythingAbove(t *testing.T) { } } -// TestControl50HasNothingToSayAboutTheDeliveredFile, which does not carry the key. +// TestControl50HasNothingToSayAboutTheDeliveredFile: the delivered file never sets +// this key, and its silence must not be mistaken for a fault. func TestControl50HasNothingToSayAboutTheDeliveredFile(t *testing.T) { config := loadDelivered(t) if fault := findFault(config.Validate(testRegistries()), "ui.min_products_for_chip"); fault != nil { From 223398cdb35fef87d8ad0094c1963202769a1ff0 Mon Sep 17 00:00:00 2001 From: "Fabien F." Date: Tue, 4 Aug 2026 11:40:16 +0200 Subject: [PATCH 08/27] feat(web): le seuil de puce voyage avec le catalogue et entre dans l'empreinte MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TestEveryFieldOfThePresentationEntersItsDigest couvre le nouveau champ par réflexion, sans y toucher ; le golden state.json est régénéré parce que sa valeur de presentation_digest bouge maintenant que ce champ y entre. --- internal/web/catalog.go | 6 ++++++ internal/web/catalog_test.go | 20 ++++++++++++++++++++ internal/web/testdata/state.json | 2 +- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/internal/web/catalog.go b/internal/web/catalog.go index ac31026..479096e 100644 --- a/internal/web/catalog.go +++ b/internal/web/catalog.go @@ -124,6 +124,11 @@ type catalogPresentationDTO struct { // setting and not its absence, and a front end reading `undefined` would have to // invent which of the two it meant. GridColumns int `json:"grid_columns"` + // MinProductsForChip is how many tiles a category needs before the grid gives it a + // filter chip (ADR-059). Stated here and applied by the grid, like the settings + // above: which categories end up with a chip depends on what the grid actually shows, + // and the grid is the only side that knows it. + MinProductsForChip int `json:"min_products_for_chip"` } // presentationOf carries the screen settings of one configuration. @@ -139,6 +144,7 @@ func presentationOf(ui domain.UIConfig) catalogPresentationDTO { Sound: ui.Sound, ShowByUnitProducts: ui.ShowByUnitProducts, GridColumns: ui.GridColumns, + MinProductsForChip: ui.MinProductsForChip, } } diff --git a/internal/web/catalog_test.go b/internal/web/catalog_test.go index ba1e698..6e60d93 100644 --- a/internal/web/catalog_test.go +++ b/internal/web/catalog_test.go @@ -244,6 +244,7 @@ func TestThePresentationDigestFollowsThePresentationAndNothingElse(t *testing.T) for name, tweak := range map[string]func(*domain.Config){ "le nombre de colonnes": func(c *domain.Config) { c.UI.GridColumns = 7 }, + "le seuil de puce": func(c *domain.Config) { c.UI.MinProductsForChip = 12 }, "les prix sur les tuiles": func(c *domain.Config) { c.UI.ShowGridPrices = !c.UI.ShowGridPrices }, @@ -419,6 +420,25 @@ func TestTheCategoryLabelsFollowTheConfigurationAndNotTheLastImport(t *testing.T } } +// TestTheChipThresholdTravelsWithTheCatalog: the grid decides which categories get a +// chip, and it decides it on a number this station sets (ADR-059). +// +// It rides in `presentation` with the other screen settings and for the same reason: the +// station states the setting, the grid applies it. Which categories end up with a chip is +// never computed here -- the payload stays the inventory of what this station shows. +func TestTheChipThresholdTravelsWithTheCatalog(t *testing.T) { + b := newBench(t, func(o *benchOptions) { + o.config = func(c *domain.Config) { c.UI.MinProductsForChip = 12 } + }) + + page := decodeStatus[catalogDTO](t, b.get("/api/v1/catalog"), http.StatusOK) + + if page.Options.MinProductsForChip != 12 { + t.Fatalf("presentation.min_products_for_chip = %d, attendu 12", + page.Options.MinProductsForChip) + } +} + // TestWhatHasNoTileIsNotServedToTheScreen: a prepackaged product is not an error, it // is not the scale's business, and it has no tile (§10.3, ADR-021). func TestWhatHasNoTileIsNotServedToTheScreen(t *testing.T) { diff --git a/internal/web/testdata/state.json b/internal/web/testdata/state.json index 030a965..005280c 100644 --- a/internal/web/testdata/state.json +++ b/internal/web/testdata/state.json @@ -136,6 +136,6 @@ "reason": "Le port de la balance ne peut pas être ouvert." }, "catalog_count": 1, - "presentation_digest": "1c08246a", + "presentation_digest": "c217a4b4", "unlogged_weighings_count": 3 } From 1d5618c36b89b5a18dd483291d9040ce74bd870b Mon Sep 17 00:00:00 2001 From: "Fabien F." Date: Tue, 4 Aug 2026 11:55:39 +0200 Subject: [PATCH 09/27] =?UTF-8?q?feat(front):=20la=20barre=20de=20cat?= =?UTF-8?q?=C3=A9gories=20suit=20le=20seuil=20que=20le=20poste=20sert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/lib/catalog.ts | 30 ++++++++++++++----- web/src/lib/session.svelte.ts | 5 +++- web/test/chips.test.ts | 56 +++++++++++++++++++++++++++++++++-- web/test/fixtures/odoo.ts | 5 +++- 4 files changed, 85 insertions(+), 11 deletions(-) diff --git a/web/src/lib/catalog.ts b/web/src/lib/catalog.ts index 1ad8360..33c6796 100644 --- a/web/src/lib/catalog.ts +++ b/web/src/lib/catalog.ts @@ -75,6 +75,15 @@ export interface Presentation { * ce nombre de colonnes partout — le fichier décrit une grille, pas une préférence. */ grid_columns: number + /** + * Le nombre de tuiles qu'une catégorie doit avoir sur ce poste pour obtenir sa puce + * de filtre. Défaut 5. + * + * En deçà, la catégorie perd sa PUCE et jamais ses tuiles : ses produits restent dans + * « Tout » et à la recherche. Ce qui retire vraiment des produits de l'écran est + * `categories[].visible`, et les deux ne sont pas la même décision (ADR-059). + */ + min_products_for_chip: number } /** Le corps de `GET /api/v1/catalog`. */ @@ -94,12 +103,14 @@ export interface Catalog { } /** - * Combien de produits pesables une catégorie doit atteindre pour mériter sa puce. + * Le seuil qu'applique un poste dont la configuration ne dit rien. * - * CONSTANTE DU CODE, pas un réglage : aucun exploitant n'a de choix légitime à - * faire là-dessus (ADR-025). En deçà, la catégorie reste dans « Tout » et ses - * produits restent atteignables par la recherche — jamais masqués. En 2022, - * « Autres » comptait UN produit : un quart de barre de navigation pour une tuile. + * C'ÉTAIT une constante du code (ADR-024, ADR-025). Ce qu'ADR-024 a mesuré est qu'un + * seuil doit EXISTER — en 2022, « Autres » menait à UN produit, un quart de barre de + * navigation pour une tuile —, et aucune mesure ne dit cinq plutôt que trois ou huit. + * C'est ce qui en fait le réglage `ui.min_products_for_chip` (ADR-059), dont ce nombre + * n'est plus que le défaut : il ne sert qu'aux postes dont le binaire est plus ancien + * que la clé. */ export const MIN_PRODUCTS_FOR_CHIP = 5 @@ -146,7 +157,8 @@ export function visibleProducts(catalog: Catalog): Product[] { /** * Construit la barre de filtres : « Tout » d'abord, puis une puce par catégorie - * PEUPLÉE. + * qui atteint le seuil que CE POSTE sert (`presentation.min_products_for_chip`, + * ADR-059). * * L'effectif est recompté sur les produits RÉELLEMENT SERVIS plutôt que repris de * `Category.product_count`. Le serveur envoie bien les deux, et un test vérifie @@ -164,8 +176,12 @@ export function chips(catalog: Catalog): Chip[] { const counts = new Map() for (const p of shown) counts.set(p.category_code, (counts.get(p.category_code) ?? 0) + 1) + // Le seuil vient du POSTE, et la constante n'est plus qu'un filet pour un service qui + // ne sert pas encore la clé (ADR-059). + const threshold = catalog.presentation.min_products_for_chip ?? MIN_PRODUCTS_FOR_CHIP + const populated = catalog.categories - .filter((c) => c.visible && (counts.get(c.code) ?? 0) >= MIN_PRODUCTS_FOR_CHIP) + .filter((c) => c.visible && (counts.get(c.code) ?? 0) >= threshold) .slice() .sort((a, b) => a.rank - b.rank || a.code.localeCompare(b.code)) .map((c) => ({ diff --git a/web/src/lib/session.svelte.ts b/web/src/lib/session.svelte.ts index 68830e9..b0bd7bd 100644 --- a/web/src/lib/session.svelte.ts +++ b/web/src/lib/session.svelte.ts @@ -1,5 +1,5 @@ import { fetchCatalog } from './api' -import type { Catalog, Presentation } from './catalog' +import { MIN_PRODUCTS_FOR_CHIP, type Catalog, type Presentation } from './catalog' import type { StateDTO } from './dto' import { linkHealth, type LinkHealth } from './link' @@ -18,6 +18,9 @@ const DEFAULT_PRESENTATION: Presentation = { // Automatique, comme le défaut du poste : la grille du squelette est celle // d'ADR-035 tant que personne n'a dit autre chose. grid_columns: 0, + // Le défaut livré, comme le défaut du poste : sans catalogue encore reçu, la barre + // du squelette n'a aucun seuil de POSTE à appliquer (ADR-059). + min_products_for_chip: MIN_PRODUCTS_FOR_CHIP, } /** diff --git a/web/test/chips.test.ts b/web/test/chips.test.ts index f3dcdd1..afad05a 100644 --- a/web/test/chips.test.ts +++ b/web/test/chips.test.ts @@ -6,6 +6,7 @@ import { filterProducts, visibleProducts, type Catalog, + type Presentation, } from '../src/lib/catalog' import { catalogFromExport, qualifyExport, readExport } from './fixtures/odoo' @@ -65,7 +66,7 @@ describe('flv_1.csv, 2022 — « Autres » ne compte qu’un seul produit', () = const products = visibleProducts(catalog) const bar = chips(catalog) - it('mesure bien une catégorie sous le seuil de 5', () => { + it('mesure bien une catégorie sous le seuil que sert ce poste', () => { const others = filterProducts(products, 'other', '') expect(others).toHaveLength(1) expect(others.length).toBeLessThan(MIN_PRODUCTS_FOR_CHIP) @@ -104,7 +105,7 @@ describe('flv_1.csv, 2022 — « Autres » ne compte qu’un seul produit', () = describe('le seuil et le masquage sont deux mécanismes distincts', () => { const catalog = catalogFromExport('flv.csv') - it('à 4 produits pas de puce, à 5 une puce : le seuil est bien MIN_PRODUCTS_FOR_CHIP', () => { + it('un produit sous le seuil servi pas de puce, un produit au seuil une puce', () => { const bulk = filterProducts(visibleProducts(catalog), 'bulk', '') const justUnder: Catalog = { ...catalog, products: bulk.slice(0, MIN_PRODUCTS_FOR_CHIP - 1) } const justAt: Catalog = { ...catalog, products: bulk.slice(0, MIN_PRODUCTS_FOR_CHIP) } @@ -131,3 +132,54 @@ describe('le seuil et le masquage sont deux mécanismes distincts', () => { expect(chips(hidden)[0]?.count).toBe(221) }) }) + +describe('le seuil vient de la configuration du poste', () => { + const catalog = catalogFromExport('flv.csv') + + it('à 5 — le défaut livré — les quatre rayons ont leur puce', () => { + const served: Catalog = { + ...catalog, + presentation: { ...catalog.presentation, min_products_for_chip: 5 }, + } + expect(chips(served).map((c) => c.code)).toEqual([ + ALL_CATEGORIES, + 'fruits', + 'vegetables', + 'bulk', + 'other', + ]) + }) + + it('à 70, Fruits (28) et Légumes (67) perdent la leur, Vrac (110) et Autres (126) la gardent', () => { + const served: Catalog = { + ...catalog, + presentation: { ...catalog.presentation, min_products_for_chip: 70 }, + } + expect(chips(served).map((c) => c.code)).toEqual([ALL_CATEGORIES, 'bulk', 'other']) + }) + + it('ne retire aucun produit de « Tout » en retirant une puce', () => { + const served: Catalog = { + ...catalog, + presentation: { ...catalog.presentation, min_products_for_chip: 70 }, + } + // 331 pesables, et le poste de référence ne masque rien : le compte de « Tout » ne + // bouge pas d'un produit quand deux puces disparaissent. + expect(chips(served)[0]?.count).toBe(331) + expect(visibleProducts(served)).toHaveLength(331) + }) + + it('retombe sur le défaut quand le poste ne sert pas la clé', () => { + // Un binaire plus ancien que ce réglage : la barre reste celle d'aujourd'hui. + const older: Partial = { ...catalog.presentation } + delete older.min_products_for_chip + const served = { ...catalog, presentation: older } as Catalog + expect(chips(served).map((c) => c.code)).toEqual([ + ALL_CATEGORIES, + 'fruits', + 'vegetables', + 'bulk', + 'other', + ]) + }) +}) diff --git a/web/test/fixtures/odoo.ts b/web/test/fixtures/odoo.ts index 269e373..4c0cd17 100644 --- a/web/test/fixtures/odoo.ts +++ b/web/test/fixtures/odoo.ts @@ -2,7 +2,7 @@ import { createHash } from 'node:crypto' import { readFileSync } from 'node:fs' import { dirname, resolve } from 'node:path' import { fileURLToPath } from 'node:url' -import type { Catalog, Category, Product } from '../../src/lib/catalog' +import { MIN_PRODUCTS_FOR_CHIP, type Catalog, type Category, type Product } from '../../src/lib/catalog' import { normalize } from '../../src/lib/normalize' /** @@ -170,6 +170,9 @@ export function catalogOf(rows: QualifiedRow[], revision = '"fixture"'): Catalog // Automatique, le défaut : les bancs qui portent sur le catalogue ne décrivent // pas un poste qui a réglé sa grille. `grid.test.ts` est celui qui le fait. grid_columns: 0, + // Le défaut livré : les bancs qui portent sur le catalogue ne décrivent pas un + // poste qui a réglé son seuil. `chips.test.ts` est celui qui le fait varier. + min_products_for_chip: MIN_PRODUCTS_FOR_CHIP, }, } } From a8ee348a9d4821adc74b932d6a6c215034b405bc Mon Sep 17 00:00:00 2001 From: "Fabien F." Date: Tue, 4 Aug 2026 11:55:45 +0200 Subject: [PATCH 10/27] =?UTF-8?q?docs(web):=20le=20commentaire=20du=20banc?= =?UTF-8?q?=20dit=20le=20crit=C3=A8re=20du=20seuil=20de=20puce,=20pas=20la?= =?UTF-8?q?=20constante=20TS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/web/catalog_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/web/catalog_test.go b/internal/web/catalog_test.go index 6e60d93..c2c41a6 100644 --- a/internal/web/catalog_test.go +++ b/internal/web/catalog_test.go @@ -41,8 +41,8 @@ func TestTheCatalogIsServedWholeWithAValidator(t *testing.T) { } // The FOUR shelves the configuration declares, and not the one the snapshot of this // bench happens to carry: the shelves belong to config.json (§10.2 bis). A shelf with - // no tile costs the screen nothing — the grid gives a chip only from - // MIN_PRODUCTS_FOR_CHIP products up — and the payload stays the inventory of what + // no tile costs the screen nothing — the grid gives a chip only once a shelf reaches + // the threshold this station serves — and the payload stays the inventory of what // this station is configured to show. What is asserted here is the effectif, which is // counted on the catalog in service. byCategory := map[string]int{} From 8f5f53611c45ae30a2b70856e141cc0f6f16eaf4 Mon Sep 17 00:00:00 2001 From: "Fabien F." Date: Tue, 4 Aug 2026 12:13:43 +0200 Subject: [PATCH 11/27] =?UTF-8?q?feat(admin):=20le=20seuil=20de=20puce=20s?= =?UTF-8?q?e=20r=C3=A8gle=20=C3=A0=20c=C3=B4t=C3=A9=20des=20colonnes=20de?= =?UTF-8?q?=20la=20grille?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/admin/lib/fields.ts | 1 + web/src/admin/pages/Catalog.svelte | 25 ++++++++++++++++++++ web/test/admin-catalog.test.ts | 38 ++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/web/src/admin/lib/fields.ts b/web/src/admin/lib/fields.ts index 5eed93a..36f2a66 100644 --- a/web/src/admin/lib/fields.ts +++ b/web/src/admin/lib/fields.ts @@ -29,6 +29,7 @@ export const FIELD_LABELS: Readonly> = { 'ui.show_grid_prices': 'Afficher les prix sur les tuiles', 'ui.show_by_unit_products': 'Afficher les produits vendus à l’unité', 'ui.grid_columns': 'Colonnes de la grille', + 'ui.min_products_for_chip': 'Produits minimum pour afficher une catégorie', 'scale.type': 'Protocole de la balance', 'scale.present': 'Ce poste a une balance', diff --git a/web/src/admin/pages/Catalog.svelte b/web/src/admin/pages/Catalog.svelte index e2d7336..83af1ce 100644 --- a/web/src/admin/pages/Catalog.svelte +++ b/web/src/admin/pages/Catalog.svelte @@ -111,6 +111,9 @@ */ const GRID_COLUMNS_CHOICES = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12] + /** The key that decides from how many tiles a category gets its filter chip. */ + const CHIP_THRESHOLD_PATH = 'ui.min_products_for_chip' + let imports = $state([]) let findings = $state([]) /** @@ -180,6 +183,9 @@ /** The column setting AS DRAFTED, `0` for as long as nobody has pinned one. */ const gridColumns = $derived(draft.number(GRID_COLUMNS_PATH)) + /** The chip threshold AS DRAFTED. */ + const chipThreshold = $derived(draft.number(CHIP_THRESHOLD_PATH)) + /** * The tiles the grid would draw at the DRAFT, switch above included. * @@ -872,6 +878,25 @@ fichier reçu, ni les étiquettes déjà imprimées.

+

+ + {#if preferences.showTechnicalNames}{CHIP_THRESHOLD_PATH}{/if} +

+ draft.set(CHIP_THRESHOLD_PATH, Number(event.currentTarget.value))} + /> +

+ En dessous de ce nombre, la catégorie n’a pas de puce dans la barre du bas. Ses + produits restent dans « Tout » et à la recherche : ce réglage ne retire aucune + tuile. Pour ne plus montrer une catégorie du tout, c’est sa case « visible » qu’il + faut décocher. +

+ ")),t.content}function Je(e,t){var n=w;n.nodes===null&&(n.nodes={start:e,end:t,a:null,t:null})}function tt(e,t){var n=(t&Zr)!==0,r=(t&Wr)!==0,i,s=!e.startsWith("");return()=>{i===void 0&&(i=lr(s?e:""+e),n||(i=De(i)));var f=r||Bn?document.importNode(i,!0):i.cloneNode(!0);if(n){var l=De(f),a=f.lastChild;Je(l,a)}else Je(f,f);return f}}function zi(e,t,n="svg"){var r=!e.startsWith(""),i=`<${n}>${r?e:""+e}`,s;return()=>{if(!s){var f=lr(i),l=De(f);s=De(l)}var a=s.cloneNode(!0);return Je(a,a),a}}function Ui(e,t){return zi(e,t,"svg")}function Us(e=""){{var t=le(e+"");return Je(t,t),t}}function qs(){var e=document.createDocumentFragment(),t=document.createComment(""),n=le();return e.append(t,n),Je(t,n),e}function Oe(e,t){e!==null&&e.before(t)}function Ue(e,t){var n=t==null?"":typeof t=="object"?`${t}`:t;n!==(e[Rt]??=e.nodeValue)&&(e[Rt]=n,e.nodeValue=`${n}`)}function Bs(e,t){return qi(e,t)}const st=new Map;function qi(e,{target:t,anchor:n,props:r={},events:i,context:s,intro:f=!0,transformError:l}){bi();var a=void 0,o=Ai(()=>{var c=n??t.appendChild(le());li(c,{pending:()=>{}},h=>{On({});var v=I;s&&(v.c=s),i&&(r.$$events=i),a=e(h,r)||{},Nn()},l);var d=new Set,u=h=>{for(var v=0;v{for(var h of d)for(const _ of[t,document]){var v=st.get(_),p=v.get(h);--p==0?(_.removeEventListener(h,Ft),v.delete(h),v.size===0&&st.delete(_)):v.set(h,p)}Dt.delete(u),c!==n&&c.parentNode?.removeChild(c)}});return jt.set(a,o),a}let jt=new WeakMap;function Gs(e,t){const n=jt.get(e);return n?(jt.delete(e),n(t)):Promise.resolve()}class Bi{anchor;#t=new Map;#f=new Map;#e=new Map;#l=new Set;#r=!0;constructor(t,n=!0){this.anchor=t,this.#r=n}#s=t=>{if(this.#t.has(t)){var n=this.#t.get(t),r=this.#f.get(n);if(r)_t(r),this.#l.delete(n);else{var i=this.#e.get(n);i&&(_t(i.effect),this.#f.set(n,i.effect),this.#e.delete(n),i.fragment.lastChild.remove(),this.anchor.before(i.fragment),r=i.effect)}for(const[s,f]of this.#t){if(this.#t.delete(s),s===t)break;const l=this.#e.get(f);l&&(D(l.effect),this.#e.delete(f))}for(const[s,f]of this.#f){if(s===n||this.#l.has(s))continue;const l=()=>{if(Array.from(this.#t.values()).includes(s)){var o=document.createDocumentFragment();Zt(f,o),o.append(le()),this.#e.set(s,{effect:f,fragment:o})}else D(f);this.#l.delete(s),this.#f.delete(s)};this.#r||!r?(this.#l.add(s),be(f,l,!1)):l()}}};#n=t=>{this.#t.delete(t);const n=Array.from(this.#t.values());for(const[r,i]of this.#e)n.includes(r)||(D(i.effect),this.#e.delete(r))};ensure(t,n){var r=b,i=Yn();if(n&&!this.#f.has(t)&&!this.#e.has(t))if(i){var s=document.createDocumentFragment(),f=le();s.append(f),this.#e.set(t,{effect:q(()=>n(f)),fragment:s})}else this.#f.set(t,q(()=>n(this.anchor)));if(this.#t.set(r,t),i){for(const[l,a]of this.#f)l===t?r.unskip_effect(a):r.skip_effect(a);for(const[l,a]of this.#e)l===t?r.unskip_effect(a.effect):r.skip_effect(a.effect);r.oncommit(this.#s),r.ondiscard(this.#n)}else this.#s(r)}}function hn(e,t,n=!1){var r=new Bi(e),i=n?Re:0;function s(f,l){r.ensure(f,l)}Xt(()=>{var f=!1;t((l,a=0)=>{f=!0,s(a,l)}),f||s(-1,null)},i)}function Vs(e,t){return t}function Gi(e,t,n){for(var r=[],i=t.length,s,f=t.length,l=0;l{if(s){if(s.pending.delete(d),s.done.add(d),s.pending.size===0){var u=e.outrogroups;Ht(e,pt(s.done)),u.delete(s),u.size===0&&(e.outrogroups=null)}}else f-=1},!1)}if(f===0){var a=r.length===0&&n!==null;if(a){var o=n,c=o.parentNode;Ei(c),c.append(o),e.items.clear()}Ht(e,t,!a)}else s={pending:new Set(t),done:new Set},(e.outrogroups??=new Set).add(s)}function Ht(e,t,n=!0){var r;if(e.pending.size>0){r=new Set;for(const f of e.pending.values())for(const l of f)r.add(e.items.get(l).e)}for(var i=0;i{var y=n();return mn(y)?y:y==null?[]:pt(y)}),u,h=new Map,v=!0;function p(y){(S.effect.f&H)===0&&(S.pending.delete(y),S.fallback=c,Yi(S,u,f,t,r),c!==null&&(u.length===0?(c.f&$)===0?_t(c):(c.f^=$,Ye(c,null,f)):be(c,()=>{c=null})))}function _(y){S.pending.delete(y)}var g=Xt(()=>{u=T(d);for(var y=u.length,E=new Set,x=b,Y=Yn(),P=0;Ps(f)):(c=q(()=>s(vn??=le())),c.f|=$)),y>E.size&&xr(),!v)if(h.set(x,E),Y){for(const[wt,mt]of l)E.has(wt)||x.skip_effect(mt.e);x.oncommit(p),x.ondiscard(_)}else p(x);T(d)}),S={effect:g,items:l,pending:h,outrogroups:null,fallback:c};v=!1}function qe(e){for(;e!==null&&(e.f&G)===0;)e=e.next;return e}function Yi(e,t,n,r,i){var s=(r&Br)!==0,f=t.length,l=e.items,a=qe(e.effect.first),o,c=null,d,u=[],h=[],v,p,_,g;if(s)for(g=0;g0){var re=(r&Tn)!==0&&f===0?n:null;if(s){for(g=0;g{if(d!==void 0)for(_ of d)_.nodes?.a?.apply()})}function Xi(e,t,n,r,i,s,f,l){var a=(f&Ur)!==0?(f&Gr)===0?wi(n,!1,!1):Se(n):null,o=(f&qr)!==0?Se(i):null;return{v:a,i:o,e:q(()=>(s(t,a??n,o??i,l),()=>{e.delete(r)}))}}function Ye(e,t,n){if(e.nodes)for(var r=e.nodes.start,i=e.nodes.end,s=t&&(t.f&$)===0?t.nodes.start:n;r!==null;){var f=$e(r);if(s.before(r),r===i)return;r=f}}function de(e,t,n){t===null?e.effect.first=n:t.next=n,n===null?e.effect.last=t:n.prev=t}const _n=[...` -\r\f \v\uFEFF`];function Ki(e,t,n){var r=e==null?"":""+e;if(t&&(r=r?r+" "+t:t),n){for(var i of Object.keys(n))if(n[i])r=r?r+" "+i:i;else if(r.length)for(var s=i.length,f=0;(f=r.indexOf(i,f))>=0;){var l=f+s;(f===0||_n.includes(r[f-1]))&&(l===r.length||_n.includes(r[l]))?r=(f===0?"":r.substring(0,f))+r.substring(l+1):f=l}}return r===""?null:r}function pn(e,t=!1){var n=t?" !important;":";",r="";for(var i of Object.keys(e)){var s=e[i];s!=null&&s!==""&&(r+=" "+i+": "+s+n)}return r}function Tt(e){return e[0]!=="-"||e[1]!=="-"?e.toLowerCase():e}function Zi(e,t){if(t){var n="",r,i;if(Array.isArray(t)?(r=t[0],i=t[1]):r=t,e){e=String(e).replaceAll(/\s*\/\*.*?\*\/\s*/g,"").trim();var s=!1,f=0,l=!1,a=[];r&&a.push(...Object.keys(r).map(Tt)),i&&a.push(...Object.keys(i).map(Tt));var o=0,c=-1;const p=e.length;for(var d=0;d{var f,l;return Zn(()=>{f=l,l=[],Wt(()=>{Ot(n(...l),e)||(t(e,...l),f&&Ot(n(...f),e)&&t(null,...f))})}),()=>{let a=s;for(;a!==i&&a.parent!==null&&a.parent.f&Ct;)a=a.parent;const o=()=>{l&&Ot(n(...l),e)&&t(null,...l)},c=a.teardown;a.teardown=()=>{o(),c?.()}}}),e}function se(e,t,n,r){var i=!0,s=(n&Xr)!==0,f=(n&Kr)!==0,l=r,a=!0,o=void 0,c=()=>f&&i?(o??=Ze(r),T(o)):(a&&(a=!1,l=f?Wt(r):r),l);let d;if(s){var u=we in e||Or in e;d=Ce(e,t)?.set??(u&&t in e?E=>e[t]=E:void 0)}var h,v=!1;s?[h,v]=ii(()=>e[t]):h=e[t],h===void 0&&r!==void 0&&(h=c(),d&&(Dr(),d(h)));var p;if(p=()=>{var E=e[t];return E===void 0?c():(a=!0,E)},(n&Yr)===0)return p;if(d){var _=e.$$legacy;return(function(E,x){return arguments.length>0?((!x||_||v)&&d(x?p():E),E):p()})}var g=!1,S=((n&Vr)!==0?Ze:Ln)(()=>(g=!1,p()));s&&T(S);var y=w;return(function(E,x){if(arguments.length>0){const Y=x?T(S):s?ke(E):E;return ae(S,Y),g=!0,l!==void 0&&(l=Y),E}return ue&&g||(y.f&H)!==0?S.v:T(S)})}const es="5";typeof window<"u"&&((window.__svelte??={}).v??=new Set).add(es);var ts=Ui('');function Zs(e,t){const n=se(t,"size",3,"1.5rem"),r={search:"M11 4a7 7 0 1 0 0 14 7 7 0 0 0 0-14M16.5 16.5 21 21",tare:"M8 4h8M9 4v3.2a3 3 0 0 1-.5 1.7l-.8 1.2a4 4 0 0 0-.7 2.2V18a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2v-5.7a4 4 0 0 0-.7-2.2l-.8-1.2a3 3 0 0 1-.5-1.7V4M7 14h10",printer:"M7 9V4h10v5M7 20h10v-6H7zM7 17H5a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-2",alert:"M12 3.5 21.5 20h-19zM12 10v4M12 17.2v.1",backspace:"M9 5h11a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H9l-7-7zM17 9l-6 6M11 9l6 6",check:"M4 12.5 9.5 18 20 7",close:"M6 6l12 12M18 6 6 18",settings:"M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6M19.4 15a1.6 1.6 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.6 1.6 0 0 0-1.8-.3 1.6 1.6 0 0 0-1 1.5v.2a2 2 0 1 1-4 0v-.1a1.6 1.6 0 0 0-1-1.5 1.6 1.6 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.6 1.6 0 0 0 .3-1.8 1.6 1.6 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.6 1.6 0 0 0 1.5-1 1.6 1.6 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.6 1.6 0 0 0 1.8.3H9a1.6 1.6 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.6 1.6 0 0 0 1 1.5 1.6 1.6 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.6 1.6 0 0 0-.3 1.8V9a1.6 1.6 0 0 0 1.5 1h.2a2 2 0 1 1 0 4h-.1a1.6 1.6 0 0 0-1.5 1"};var i=ts();let s;var f=Q(i);Ge(()=>{s=ot(i,"",s,{width:n(),height:n()}),zt(f,"d",r[t.name])}),Oe(e,i)}async function Ws(){const e=await fetch("/api/v1/catalog",{headers:{accept:"application/json"}});if(!e.ok)throw new Error(`GET /api/v1/catalog: ${e.status}`);return await e.json()}async function Js(e){await Qt("/api/v1/weigh",e)}async function Qs(e,t){await Qt("/api/v1/reprint",{job_id:e,key:t})}async function $s(){await Qt("/api/v1/dismiss",{})}function ef(e,t){fetch("/api/v1/ui/error",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({message:e,stack:t}),keepalive:!0}).catch(()=>{})}function tf(e){fetch("/api/v1/ui/layout-notice",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({message:e}),keepalive:!0}).catch(()=>{})}async function Qt(e,t){const n=await fetch(e,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)});if(!n.ok)throw new Error(`POST ${e}: ${n.status}`)}const or={Œ:"oe",œ:"oe",Æ:"ae",æ:"ae",ß:"ss",Ø:"o",ø:"o"},ns=new RegExp(`[${Object.keys(or).join("")}]`,"gu"),rs=/\p{Mn}/u,is=/[\p{L}\p{Nd}]/u;function ss(e){const n=e.replace(ns,s=>or[s]).normalize("NFD");let r="",i=!1;for(const s of n)if(!rs.test(s)){if(is.test(s)){i&&r.length>0&&(r+=" "),i=!1,r+=s.toLowerCase();continue}i=!0}return r}const fs=5,ur="",as="by_unit";function ls(e){const t=new Set(e.categories.filter(r=>!r.visible).map(r=>r.code)),n=!e.presentation.show_by_unit_products;return t.size===0&&!n?e.products:e.products.filter(r=>!t.has(r.category_code)&&!(n&&r.mode===as))}function nf(e){const t=ls(e),n=new Map;for(const i of t)n.set(i.category_code,(n.get(i.category_code)??0)+1);const r=e.categories.filter(i=>i.visible&&(n.get(i.code)??0)>=fs).slice().sort((i,s)=>i.rank-s.rank||i.code.localeCompare(s.code)).map(i=>({code:i.code,label:i.label,color:i.color,count:n.get(i.code)??0}));return[{code:ur,label:"Tout",color:"var(--ink-muted)",count:t.length},...r]}function rf(e,t,n){const r=ss(n),i=r.length===0?[]:r.split(" ");return e.filter(s=>t!==ur&&s.category_code!==t?!1:i.every(f=>s.search.includes(f)))}const os=4.5,us=.1,cs={r:138,g:134,b:124};function cr(e){const t=/^#([\da-f])([\da-f])([\da-f])$/iu.exec(e);if(t!==null){const[,f,l,a]=t;return{r:Number.parseInt(f+f,16),g:Number.parseInt(l+l,16),b:Number.parseInt(a+a,16)}}const n=/^#([\da-f]{2})([\da-f]{2})([\da-f]{2})$/iu.exec(e);if(n===null)return cs;const[,r,i,s]=n;return{r:Number.parseInt(r,16),g:Number.parseInt(i,16),b:Number.parseInt(s,16)}}function Nt(e){const t=e/255;return t<=.03928?t/12.92:((t+.055)/1.055)**2.4}function ds({r:e,g:t,b:n}){return .2126*Nt(e)+.7152*Nt(t)+.0722*Nt(n)}function hs(e){return 1.05/(ds(e)+.05)}function vs(e){const{r:t,g:n,b:r}=cr(e),i=s=>Math.round(255-us*(255-s));return`rgb(${i(t)}, ${i(n)}, ${i(r)})`}function _s(e){let t=cr(e);for(let n=0;n<16&&hs(t)Ut;a-=ps){const o=a/dr;if(Ts(s,f,o,l)<=bs(a,r))return a}return Ut}const Es=/(?<=[-‐–—])(?!\d)/u;function ys(e,t){const n=[];for(const r of e.split(/\s+/u).filter(i=>i.length>0)){const i=r.split(Es).filter(s=>s.length>0);for(const[s,f]of i.entries())n.push({width:t(f),length:[...f].length,spaced:s===0})}return n}function Ss(e){return Math.max(0,e("a a")-e("aa"))}function Ts(e,t,n,r){let i=1,s=0;for(const f of e){const l=f.width*n,a=f.spaced?t*n:0;if(s>0&&s+a+l<=r){s+=a+l;continue}if(s>0&&(i++,s=0),l<=r){s=l;continue}i+=As(l,f.length,r)-1,s=r}return i}function As(e,t,n){const r=Math.max(1,Math.floor(n*t/e));return Math.ceil(t/r)}function af(e,t){const r=document.createElement("canvas").getContext("2d");if(r===null||typeof r.measureText!="function")return null;r.font=`${t} ${dr}px ${e}`;const i=new Map;return s=>{const f=i.get(s);if(f!==void 0)return f;const l=r.measureText(s).width;return i.set(s,l),l}}var Ms=tt(''),Os=tt(''),Ns=tt(' '),ks=tt(''),Cs=tt('');function lf(e,t){On(t,!0);const n=se(t,"nameSizePx",3,$t),r=se(t,"categoryColor",3,"#8a867c"),i=se(t,"primaryCode",3,""),s=se(t,"tierAbbrev",19,()=>({})),f=se(t,"selected",3,!1),l=se(t,"rejected",3,!1),a=se(t,"busy",3,!1),o=se(t,"showPrice",3,!0);let c=ie("");const d=rt(()=>t.product.image_url!==""&&t.product.image_url!==T(c)),u=rt(()=>p(t.product.name)),h=rt(()=>vs(r())),v=rt(()=>_s(r()));function p(z){const F=z.match(/\p{L}/u);return F===null?"·":F[0].toUpperCase()}var _=Cs();let g;var S=Q(_);let y;var E=Q(S);{var x=z=>{var F=Ms();Ge(()=>zt(F,"src",t.product.image_url)),Li("error",F,()=>ae(c,t.product.image_url,!0)),Oe(z,F)},Y=z=>{var F=Os();let Te;var Ae=Q(F);Ge(()=>{Te=ot(F,"",Te,{color:T(v)}),Ue(Ae,T(u))}),Oe(z,F)};hn(E,z=>{T(d)?z(x):z(Y,-1)})}var P=it(S,2),J=Q(P);let re;var k=Q(J),wt=it(P,2);{var mt=z=>{var F=ks();Vi(F,21,()=>t.product.prices,Te=>Te.code,(Te,Ae)=>{var bt=Ns();let en;var Et=Q(bt);let tn;var hr=Q(Et),nn=it(Et,2),vr=Q(nn),_r=it(nn,2),pr=Q(_r);Ge(gr=>{en=At(bt,1,"price svelte-1ctu6qg",null,en,{secondary:T(Ae).code!==i()}),tn=At(Et,1,"abbrev svelte-1ctu6qg",null,tn,{hollow:T(Ae).code!==i()}),Ue(hr,s()[T(Ae).code]??""),Ue(vr,T(Ae).text),Ue(pr,gr)},[()=>t.product.price_suffix.trim()]),Oe(Te,bt)}),Oe(z,F)};hn(wt,z=>{o()&&z(mt)})}Ge(()=>{g=At(_,1,"tile touch-target svelte-1ctu6qg",null,g,{selected:f(),rejected:l()}),zt(_,"data-product-id",t.product.id),_.disabled=a(),y=ot(S,"",y,{background:T(h)}),re=ot(J,"",re,{"font-size":`${n()??""}px`}),Ue(k,t.product.name)}),Di("pointerdown",_,()=>t.onpick(t.product)),Oe(e,_),Nn()}Fi(["pointerdown"]);const xs=0,Ps="repeat(auto-fill, minmax(var(--tile-min), 1fr))";function of(e){return e===xs?Ps:`repeat(${String(e)}, minmax(0, 1fr))`}export{b as $,ie as A,Vs as B,of as C,ff as D,sf as E,af as F,Ys as G,ke as H,Zs as I,Ws as J,ur as K,Js as L,rf as M,ms as N,nf as O,Qs as P,ls as Q,dr as R,$s as S,lf as T,tf as U,ef as V,Bs as W,Xt as X,Re as Y,Bi as Z,Ds as _,se as a,Xn as a0,mn as a1,Is as a2,Fs as a3,Zn as a4,zs as a5,I as a6,Ls as a7,Xs as a8,qs as a9,Us as aa,xs as ab,Ut as ac,Li as ad,Gs as ae,Di as b,Oe as c,Fi as d,Mi as e,Nn as f,tt as g,T as h,hn as i,zt as j,ot as k,At as l,Ue as m,rt as n,Q as o,On as p,js as q,Vi as r,it as s,Ge as t,Wt as u,_s as v,vs as w,Hs as x,Ks as y,ae as z}; +\r\f \v\uFEFF`];function Ki(e,t,n){var r=e==null?"":""+e;if(t&&(r=r?r+" "+t:t),n){for(var i of Object.keys(n))if(n[i])r=r?r+" "+i:i;else if(r.length)for(var s=i.length,f=0;(f=r.indexOf(i,f))>=0;){var l=f+s;(f===0||_n.includes(r[f-1]))&&(l===r.length||_n.includes(r[l]))?r=(f===0?"":r.substring(0,f))+r.substring(l+1):f=l}}return r===""?null:r}function pn(e,t=!1){var n=t?" !important;":";",r="";for(var i of Object.keys(e)){var s=e[i];s!=null&&s!==""&&(r+=" "+i+": "+s+n)}return r}function Tt(e){return e[0]!=="-"||e[1]!=="-"?e.toLowerCase():e}function Zi(e,t){if(t){var n="",r,i;if(Array.isArray(t)?(r=t[0],i=t[1]):r=t,e){e=String(e).replaceAll(/\s*\/\*.*?\*\/\s*/g,"").trim();var s=!1,f=0,l=!1,a=[];r&&a.push(...Object.keys(r).map(Tt)),i&&a.push(...Object.keys(i).map(Tt));var o=0,c=-1;const p=e.length;for(var d=0;d{var f,l;return Zn(()=>{f=l,l=[],Wt(()=>{Ot(n(...l),e)||(t(e,...l),f&&Ot(n(...f),e)&&t(null,...f))})}),()=>{let a=s;for(;a!==i&&a.parent!==null&&a.parent.f&Ct;)a=a.parent;const o=()=>{l&&Ot(n(...l),e)&&t(null,...l)},c=a.teardown;a.teardown=()=>{o(),c?.()}}}),e}function se(e,t,n,r){var i=!0,s=(n&Xr)!==0,f=(n&Kr)!==0,l=r,a=!0,o=void 0,c=()=>f&&i?(o??=Ze(r),T(o)):(a&&(a=!1,l=f?Wt(r):r),l);let d;if(s){var u=we in e||Or in e;d=Ce(e,t)?.set??(u&&t in e?E=>e[t]=E:void 0)}var h,v=!1;s?[h,v]=ii(()=>e[t]):h=e[t],h===void 0&&r!==void 0&&(h=c(),d&&(Dr(),d(h)));var p;if(p=()=>{var E=e[t];return E===void 0?c():(a=!0,E)},(n&Yr)===0)return p;if(d){var _=e.$$legacy;return(function(E,x){return arguments.length>0?((!x||_||v)&&d(x?p():E),E):p()})}var g=!1,S=((n&Vr)!==0?Ze:Ln)(()=>(g=!1,p()));s&&T(S);var y=w;return(function(E,x){if(arguments.length>0){const Y=x?T(S):s?ke(E):E;return ae(S,Y),g=!0,l!==void 0&&(l=Y),E}return ue&&g||(y.f&H)!==0?S.v:T(S)})}const es="5";typeof window<"u"&&((window.__svelte??={}).v??=new Set).add(es);var ts=Ui('');function Zs(e,t){const n=se(t,"size",3,"1.5rem"),r={search:"M11 4a7 7 0 1 0 0 14 7 7 0 0 0 0-14M16.5 16.5 21 21",tare:"M8 4h8M9 4v3.2a3 3 0 0 1-.5 1.7l-.8 1.2a4 4 0 0 0-.7 2.2V18a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2v-5.7a4 4 0 0 0-.7-2.2l-.8-1.2a3 3 0 0 1-.5-1.7V4M7 14h10",printer:"M7 9V4h10v5M7 20h10v-6H7zM7 17H5a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-2",alert:"M12 3.5 21.5 20h-19zM12 10v4M12 17.2v.1",backspace:"M9 5h11a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H9l-7-7zM17 9l-6 6M11 9l6 6",check:"M4 12.5 9.5 18 20 7",close:"M6 6l12 12M18 6 6 18",settings:"M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6M19.4 15a1.6 1.6 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.6 1.6 0 0 0-1.8-.3 1.6 1.6 0 0 0-1 1.5v.2a2 2 0 1 1-4 0v-.1a1.6 1.6 0 0 0-1-1.5 1.6 1.6 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.6 1.6 0 0 0 .3-1.8 1.6 1.6 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.6 1.6 0 0 0 1.5-1 1.6 1.6 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.6 1.6 0 0 0 1.8.3H9a1.6 1.6 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.6 1.6 0 0 0 1 1.5 1.6 1.6 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.6 1.6 0 0 0-.3 1.8V9a1.6 1.6 0 0 0 1.5 1h.2a2 2 0 1 1 0 4h-.1a1.6 1.6 0 0 0-1.5 1"};var i=ts();let s;var f=Q(i);Ge(()=>{s=ot(i,"",s,{width:n(),height:n()}),zt(f,"d",r[t.name])}),Oe(e,i)}async function Ws(){const e=await fetch("/api/v1/catalog",{headers:{accept:"application/json"}});if(!e.ok)throw new Error(`GET /api/v1/catalog: ${e.status}`);return await e.json()}async function Js(e){await Qt("/api/v1/weigh",e)}async function Qs(e,t){await Qt("/api/v1/reprint",{job_id:e,key:t})}async function $s(){await Qt("/api/v1/dismiss",{})}function ef(e,t){fetch("/api/v1/ui/error",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({message:e,stack:t}),keepalive:!0}).catch(()=>{})}function tf(e){fetch("/api/v1/ui/layout-notice",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({message:e}),keepalive:!0}).catch(()=>{})}async function Qt(e,t){const n=await fetch(e,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)});if(!n.ok)throw new Error(`POST ${e}: ${n.status}`)}const or={Œ:"oe",œ:"oe",Æ:"ae",æ:"ae",ß:"ss",Ø:"o",ø:"o"},ns=new RegExp(`[${Object.keys(or).join("")}]`,"gu"),rs=/\p{Mn}/u,is=/[\p{L}\p{Nd}]/u;function ss(e){const n=e.replace(ns,s=>or[s]).normalize("NFD");let r="",i=!1;for(const s of n)if(!rs.test(s)){if(is.test(s)){i&&r.length>0&&(r+=" "),i=!1,r+=s.toLowerCase();continue}i=!0}return r}const fs=5,ur="",as="by_unit";function ls(e){const t=new Set(e.categories.filter(r=>!r.visible).map(r=>r.code)),n=!e.presentation.show_by_unit_products;return t.size===0&&!n?e.products:e.products.filter(r=>!t.has(r.category_code)&&!(n&&r.mode===as))}function nf(e){const t=ls(e),n=new Map;for(const s of t)n.set(s.category_code,(n.get(s.category_code)??0)+1);const r=e.presentation.min_products_for_chip??fs,i=e.categories.filter(s=>s.visible&&(n.get(s.code)??0)>=r).slice().sort((s,f)=>s.rank-f.rank||s.code.localeCompare(f.code)).map(s=>({code:s.code,label:s.label,color:s.color,count:n.get(s.code)??0}));return[{code:ur,label:"Tout",color:"var(--ink-muted)",count:t.length},...i]}function rf(e,t,n){const r=ss(n),i=r.length===0?[]:r.split(" ");return e.filter(s=>t!==ur&&s.category_code!==t?!1:i.every(f=>s.search.includes(f)))}const os=4.5,us=.1,cs={r:138,g:134,b:124};function cr(e){const t=/^#([\da-f])([\da-f])([\da-f])$/iu.exec(e);if(t!==null){const[,f,l,a]=t;return{r:Number.parseInt(f+f,16),g:Number.parseInt(l+l,16),b:Number.parseInt(a+a,16)}}const n=/^#([\da-f]{2})([\da-f]{2})([\da-f]{2})$/iu.exec(e);if(n===null)return cs;const[,r,i,s]=n;return{r:Number.parseInt(r,16),g:Number.parseInt(i,16),b:Number.parseInt(s,16)}}function Nt(e){const t=e/255;return t<=.03928?t/12.92:((t+.055)/1.055)**2.4}function ds({r:e,g:t,b:n}){return .2126*Nt(e)+.7152*Nt(t)+.0722*Nt(n)}function hs(e){return 1.05/(ds(e)+.05)}function vs(e){const{r:t,g:n,b:r}=cr(e),i=s=>Math.round(255-us*(255-s));return`rgb(${i(t)}, ${i(n)}, ${i(r)})`}function _s(e){let t=cr(e);for(let n=0;n<16&&hs(t)Ut;a-=ps){const o=a/dr;if(Ts(s,f,o,l)<=bs(a,r))return a}return Ut}const Es=/(?<=[-‐–—])(?!\d)/u;function ys(e,t){const n=[];for(const r of e.split(/\s+/u).filter(i=>i.length>0)){const i=r.split(Es).filter(s=>s.length>0);for(const[s,f]of i.entries())n.push({width:t(f),length:[...f].length,spaced:s===0})}return n}function Ss(e){return Math.max(0,e("a a")-e("aa"))}function Ts(e,t,n,r){let i=1,s=0;for(const f of e){const l=f.width*n,a=f.spaced?t*n:0;if(s>0&&s+a+l<=r){s+=a+l;continue}if(s>0&&(i++,s=0),l<=r){s=l;continue}i+=As(l,f.length,r)-1,s=r}return i}function As(e,t,n){const r=Math.max(1,Math.floor(n*t/e));return Math.ceil(t/r)}function af(e,t){const r=document.createElement("canvas").getContext("2d");if(r===null||typeof r.measureText!="function")return null;r.font=`${t} ${dr}px ${e}`;const i=new Map;return s=>{const f=i.get(s);if(f!==void 0)return f;const l=r.measureText(s).width;return i.set(s,l),l}}var Ms=tt(''),Os=tt(''),Ns=tt(' '),ks=tt(''),Cs=tt('');function lf(e,t){On(t,!0);const n=se(t,"nameSizePx",3,$t),r=se(t,"categoryColor",3,"#8a867c"),i=se(t,"primaryCode",3,""),s=se(t,"tierAbbrev",19,()=>({})),f=se(t,"selected",3,!1),l=se(t,"rejected",3,!1),a=se(t,"busy",3,!1),o=se(t,"showPrice",3,!0);let c=ie("");const d=rt(()=>t.product.image_url!==""&&t.product.image_url!==T(c)),u=rt(()=>p(t.product.name)),h=rt(()=>vs(r())),v=rt(()=>_s(r()));function p(z){const F=z.match(/\p{L}/u);return F===null?"·":F[0].toUpperCase()}var _=Cs();let g;var S=Q(_);let y;var E=Q(S);{var x=z=>{var F=Ms();Ge(()=>zt(F,"src",t.product.image_url)),Li("error",F,()=>ae(c,t.product.image_url,!0)),Oe(z,F)},Y=z=>{var F=Os();let Te;var Ae=Q(F);Ge(()=>{Te=ot(F,"",Te,{color:T(v)}),Ue(Ae,T(u))}),Oe(z,F)};hn(E,z=>{T(d)?z(x):z(Y,-1)})}var P=it(S,2),J=Q(P);let re;var k=Q(J),wt=it(P,2);{var mt=z=>{var F=ks();Vi(F,21,()=>t.product.prices,Te=>Te.code,(Te,Ae)=>{var bt=Ns();let en;var Et=Q(bt);let tn;var hr=Q(Et),nn=it(Et,2),vr=Q(nn),_r=it(nn,2),pr=Q(_r);Ge(gr=>{en=At(bt,1,"price svelte-1ctu6qg",null,en,{secondary:T(Ae).code!==i()}),tn=At(Et,1,"abbrev svelte-1ctu6qg",null,tn,{hollow:T(Ae).code!==i()}),Ue(hr,s()[T(Ae).code]??""),Ue(vr,T(Ae).text),Ue(pr,gr)},[()=>t.product.price_suffix.trim()]),Oe(Te,bt)}),Oe(z,F)};hn(wt,z=>{o()&&z(mt)})}Ge(()=>{g=At(_,1,"tile touch-target svelte-1ctu6qg",null,g,{selected:f(),rejected:l()}),zt(_,"data-product-id",t.product.id),_.disabled=a(),y=ot(S,"",y,{background:T(h)}),re=ot(J,"",re,{"font-size":`${n()??""}px`}),Ue(k,t.product.name)}),Di("pointerdown",_,()=>t.onpick(t.product)),Oe(e,_),Nn()}Fi(["pointerdown"]);const xs=0,Ps="repeat(auto-fill, minmax(var(--tile-min), 1fr))";function of(e){return e===xs?Ps:`repeat(${String(e)}, minmax(0, 1fr))`}export{Ds as $,ie as A,Vs as B,of as C,ff as D,sf as E,af as F,Ys as G,ke as H,Zs as I,Ws as J,ur as K,Js as L,fs as M,ms as N,rf as O,nf as P,Qs as Q,dr as R,ls as S,lf as T,$s as U,tf as V,ef as W,Bs as X,Xt as Y,Re as Z,Bi as _,se as a,b as a0,Xn as a1,mn as a2,Is as a3,Fs as a4,Zn as a5,zs as a6,I as a7,Ls as a8,Xs as a9,qs as aa,Us as ab,xs as ac,Ut as ad,Li as ae,Gs as af,Di as b,Oe as c,Fi as d,Mi as e,Nn as f,tt as g,T as h,hn as i,zt as j,ot as k,At as l,Ue as m,rt as n,Q as o,On as p,js as q,Vi as r,it as s,Ge as t,Wt as u,_s as v,vs as w,Hs as x,Ks as y,ae as z}; diff --git a/internal/web/dist/assets/index-CY0bHh3L.js b/internal/web/dist/assets/index-CY0bHh3L.js new file mode 100644 index 0000000..50cabbd --- /dev/null +++ b/internal/web/dist/assets/index-CY0bHh3L.js @@ -0,0 +1,2 @@ +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/mount-zw3SIkYn.js","assets/app-BaRSXv80.js","assets/app-Cfxw4Luj.css","assets/mount-MmpjwdB2.css"])))=>i.map(i=>d[i]); +import{e as Ie,u as je,d as re,p as Y,a as q,i as W,I as ee,t as z,b as N,c as w,f as Z,g as E,h as t,s as h,j as ae,k as te,l as se,m as T,n as v,o as u,q as Pe,r as fe,w as De,v as Te,x as ne,y as Ce,R as Re,z as b,A as D,B as qe,T as Le,C as Ne,N as Oe,D as Me,E as We,F as Be,G as Fe,H as ze,M as He,J as Ue,K as Ve,L as Ge,O as Xe,P as Ke,Q as Je,S as Qe,U as Ye,V as Ze,W as $e,X as et}from"./app-BaRSXv80.js";class be{#e=new WeakMap;#t;#n;static entries=new WeakMap;constructor(e){this.#n=e}observe(e,n){var r=this.#e.get(e)||new Set;return r.add(n),this.#e.set(e,r),this.#s().observe(e,this.#n),()=>{var l=this.#e.get(e);l.delete(n),l.size===0&&(this.#e.delete(e),this.#t.unobserve(e))}}#s(){return this.#t??(this.#t=new ResizeObserver(e=>{for(var n of e){be.entries.set(n.target,n);for(var r of this.#e.get(n.target)||[])r(n)}}))}}var tt=new be({box:"border-box"});function he(s,e,n){var r=tt.observe(s,()=>n(s[e]));Ie(()=>(je(()=>n(s[e])),r))}const nt="modulepreload",at=function(s){return"/"+s},ye={},st=function(e,n,r){let l=Promise.resolve();if(n&&n.length>0){let _=function(o){return Promise.all(o.map(i=>Promise.resolve(i).then(y=>({status:"fulfilled",value:y}),y=>({status:"rejected",reason:y}))))};document.getElementsByTagName("link");const c=document.querySelector("meta[property=csp-nonce]"),d=c?.nonce||c?.getAttribute("nonce");l=_(n.map(o=>{if(o=at(o),o in ye)return;ye[o]=!0;const i=o.endsWith(".css"),y=i?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${o}"]${y}`))return;const k=document.createElement("link");if(k.rel=i?"stylesheet":nt,i||(k.as="script"),k.crossOrigin="",k.href=o,d&&k.setAttribute("nonce",d),document.head.appendChild(k),i)return new Promise((m,I)=>{k.addEventListener("load",m),k.addEventListener("error",()=>I(new Error(`Unable to preload CSS for ${o}`)))})}))}function f(c){const d=new Event("vite:preloadError",{cancelable:!0});if(d.payload=c,window.dispatchEvent(d),!d.defaultPrevented)throw c}return l.then(c=>{for(const d of c||[])d.status==="rejected"&&f(d.reason);return e().catch(f)})};var rt=E('

kg

',1),it=E('

kg

Poids indisponible

',1),lt=E('');function ot(s,e){Y(e,!0);const n=q(e,"taring",3,!1),r=v(()=>{if(e.linkBanner!=="")return e.linkBanner;const A=e.snapshot?.message??null;if(A!==null)return A.text;switch(e.snapshot?.state){case"product_armed":return"Posez votre produit";case"awaiting_stability":return"Pesée en cours…";case"printing":return"Étiquette en cours…";case"entering_tare":return"Tapez la tare en grammes";case"scale_lost":return"Le poids n’est plus disponible.";default:return"Touchez votre produit, l’étiquette sort"}}),l=v(()=>e.snapshot===null?"var(--waiting)":e.snapshot.state==="faulted"||e.snapshot.state==="scale_lost"?"var(--fault)":e.snapshot.state==="rejected"?"var(--warning)":e.snapshot.state==="succeeded"||e.snapshot.weight.latched?"var(--ready)":"var(--waiting)"),f=v(()=>e.showWeight&&e.snapshot!==null&&e.snapshot.weight.available&&!e.snapshot.weight.expired),c=v(()=>{const A=e.snapshot?.message;return A!=null?A.level:e.snapshot?.state==="faulted"||e.snapshot?.state==="scale_lost"?"error":e.snapshot?.state==="rejected"?"warn":"info"});var d=lt(),_=u(d);let o;var i=u(_);{var y=A=>{var O=rt(),G=Pe(O),X=u(G),K=h(G,2),F=u(K);z(()=>{T(X,e.snapshot.weight.net_text),T(F,`tare ${e.snapshot.weight.tare_g??""} g`)}),w(A,O)},k=A=>{var O=it();w(A,O)};W(i,A=>{t(f)&&e.snapshot?A(y):A(k,-1)})}var m=h(_,2);let I;var g=u(m),P=h(m,2);let B;var R=u(P);ee(R,{name:"tare",size:"1.4em"});var H=h(P,2);let V;z(()=>{ae(d,"data-state",e.snapshot?.state??"initializing"),o=te(_,"",o,{"border-left-color":t(l)}),I=se(m,1,"instruction svelte-4gizvm",null,I,{warn:t(c)==="warn",error:t(c)==="error"}),T(g,t(r)),B=se(P,1,"tare-key touch-target svelte-4gizvm",null,B,{active:n()}),V=te(H,"",V,{background:t(l)})}),N("click",P,function(...A){e.ontare?.apply(this,A)}),w(s,d),Z()}re(["click"]);var ct=E(''),ut=E('');function dt(s,e){Y(e,!0);var n=ut(),r=u(n);fe(r,21,()=>e.chips,d=>d.code,(d,_)=>{var o=ct();let i,y;var k=h(u(o),2),m=u(k),I=h(k,2),g=u(I);z(P=>{i=se(o,1,"chip touch-target svelte-atgty1",null,i,{active:t(_).code===e.active}),ae(o,"aria-pressed",t(_).code===e.active),y=te(o,"",y,P),T(m,t(_).label),T(g,t(_).count)},[()=>({"--chip-ink":Te(t(_).color),"--chip-wash":De(t(_).color)})]),N("click",o,()=>e.onselect(t(_).code)),w(d,o)});var l=h(r,2);let f;var c=u(l);ee(c,{name:"search",size:"1.75rem"}),z(()=>{f=se(l,1,"chip touch-target search-key svelte-atgty1",null,f,{active:e.searchFieldOpen}),ae(l,"aria-pressed",e.searchFieldOpen)}),N("click",l,function(...d){e.onopensearch?.apply(this,d)}),w(s,n),Z()}re(["click"]);var vt=E(''),gt=E('

'),ht=E('

');function we(s,e){Y(e,!0);const n=q(e,"action",3,null);var r=ht(),l=u(r),f=u(l),c=u(f);ee(c,{name:"alert",size:"3rem"});var d=h(f,2),_=u(d),o=h(d,2),i=u(o),y=h(o,2);{var k=g=>{var P=vt(),B=u(P);z(()=>T(B,n().label)),N("click",P,function(...R){n().run?.apply(this,R)}),w(g,P)};W(y,g=>{n()!==null&&g(k)})}var m=h(y,2);{var I=g=>{var P=gt(),B=u(P);z(()=>T(B,e.code)),w(g,P)};W(m,g=>{e.code!==""&&g(I)})}z(()=>{T(_,e.title),T(i,e.detail)}),w(s,r),Z()}re(["click"]);var ft=E('
'),pt=E('

Chargement du catalogue…

',1),_t=E('

'),mt=E('

'),bt=E('
'),yt=E('
'),wt=E('
');function Et(s,e){Y(e,!0);const n=q(e,"gridColumns",3,0),r=q(e,"colors",19,()=>({})),l=q(e,"primaryCode",3,""),f=q(e,"tierAbbrev",19,()=>({})),c=q(e,"selectedID",3,null),d=q(e,"rejectedID",3,null),_=q(e,"busyID",3,null),o=q(e,"showPrices",3,!0),i=q(e,"loading",3,!1),y=q(e,"emptyMessage",3,"Aucun produit ne correspond."),k=q(e,"emptyHint",3,""),m=8,I=26,g="'Inter Variable', system-ui, sans-serif",P="'Inter Variable'",B=16;let R=D(0),H=D(0),V=D(!1);ne(()=>{const x=document.fonts;x!==void 0&&x.load(`700 ${Re}px ${P}`).catch(()=>[]).then(()=>{b(V,!0)})});const A=v(()=>(t(V),Be(g,700)));let O=D(null),G=D(0),X=D(0);const K=v(()=>n()>0?n():t(R)<=0||t(H)<=0?0:Math.max(1,Math.floor((t(R)+m)/(t(H)+m)))),F=v(()=>t(K)>0&&t(R)>0?(t(R)-(t(K)-1)*m)/t(K):0),p=v(()=>n()>0&&t(F)>0&&t(H)>0?t(F)/t(H):1),C=v(()=>We(t(p))),M=v(()=>n()>0?`grid-template-columns: ${Ne(n())}`:void 0),$=v(()=>t(F)>0?t(F)-I:0),ie=v(()=>t(G)>0?t(G):t($));ne(()=>{t(F),t(p),e.products.length;const x=t(O)?.querySelector(".name-box");b(G,x?.clientWidth??0,!0)});const oe=v(()=>{const x=new Map;if(t(A)===null||t(ie)<=0)return x;const L=t(X)>0?t(X):Oe;for(const j of e.products)x.set(j.id,Me(j.name,t(ie),t(A),L,t(C)));return x});var J=wt(),ce=u(J),le=h(ce,2),ue=h(le,2);{var pe=x=>{var L=pt(),j=Pe(L);fe(j,21,()=>({length:B}),qe,(a,S)=>{var U=ft();w(a,U)}),z(()=>te(j,t(M))),he(j,"clientWidth",a=>b(R,a)),w(x,L)},de=x=>{var L=mt(),j=u(L),a=u(j),S=h(j,2);{var U=Q=>{var ge=_t(),_e=u(ge);z(()=>T(_e,k())),w(Q,ge)};W(S,Q=>{k()!==""&&Q(U)})}z(()=>T(a,y())),w(x,L)},ve=x=>{var L=yt();fe(L,21,()=>e.products,j=>j.id,(j,a)=>{var S=bt(),U=u(S);{let Q=v(()=>t(oe).get(t(a).id)??t(C)),ge=v(()=>t(a).id===c()),_e=v(()=>t(a).id===d()),Se=v(()=>t(a).id===_());Le(U,{get product(){return t(a)},get nameSizePx(){return t(Q)},get categoryColor(){return r()[t(a).category_code]},get primaryCode(){return l()},get tierAbbrev(){return f()},get selected(){return t(ge)},get rejected(){return t(_e)},get busy(){return t(Se)},get showPrice(){return o()},get onpick(){return e.onpick}})}w(j,S)}),z(()=>{ae(L,"data-tile-count",e.products.length),te(L,t(M))}),he(L,"clientWidth",j=>b(R,j)),w(x,L)};W(ue,x=>{i()?x(pe):e.products.length===0?x(de,1):x(ve,-1)})}Ce(J,x=>b(O,x),()=>t(O)),z(()=>{ae(J,"data-tile-scale",t(p)),te(J,`--tile-scale: ${t(p)??""}`)}),he(ce,"clientHeight",x=>b(X,x)),he(le,"clientWidth",x=>b(H,x)),w(s,J),Z()}var kt=E(''),xt=E('
');function At(s,e){Y(e,!0);let n=D(null);ne(()=>{t(n)?.focus()});var r=xt(),l=u(r);ee(l,{name:"search",size:"1.75rem"});var f=h(l,2);Ce(f,i=>b(n,i),()=>t(n));var c=h(f,2);{var d=i=>{var y=kt();N("click",y,function(...k){e.onclose?.apply(this,k)}),w(i,y)};W(c,i=>{e.query!==""&&i(d)})}var _=h(c,2),o=u(_);ee(o,{name:"close",size:"1.5rem"}),z(()=>Fe(f,e.query)),N("input",f,i=>e.onquery(i.currentTarget.value)),N("keydown",f,i=>{i.key==="Escape"?(i.preventDefault(),e.onclose()):i.key==="Enter"&&(i.preventDefault(),e.onenter())}),N("click",_,function(...i){e.onclose?.apply(this,i)}),w(s,r),Z()}re(["input","keydown","click"]);function Pt(s){if(s==="")return"";const e=new Date(s);if(Number.isNaN(e.getTime()))return"";const n=l=>String(l).padStart(2,"0");return`${`${n(e.getDate())}/${n(e.getMonth()+1)}/${e.getFullYear()}`} ${n(e.getHours())}:${n(e.getMinutes())}:${n(e.getSeconds())}`}function Ct(s){return s.mode==="by_unit"?`${s.product_name} ${s.quantity} ${s.quantity>1?"unités":"unité"}`:`${s.product_name} ${s.net_text} kg`}var zt=E('Catalogue du '),St=E('Catalogue en attente'),It=E(' '),jt=E('aucune pour le moment'),Dt=E(' '),Tt=E(''),Rt=E('
Dernière étiquette
');function qt(s,e){Y(e,!0);const n=q(e,"catalogAt",3,""),r=q(e,"productCount",3,0),l=q(e,"appVersion",3,""),f=v(()=>[r()>0?`${r()} produits pesables`:"",l()!==""?`application ${l()}`:""].filter(p=>p!=="").join(" · "));var c=Rt();let d;var _=u(c),o=u(_);{var i=p=>{var C=zt(),M=h(u(C)),$=u(M);z(()=>T($,n())),w(p,C)},y=p=>{var C=St();w(p,C)};W(o,p=>{n()!==""?p(i):p(y,-1)})}var k=h(o,2);{var m=p=>{var C=It(),M=u(C);z(()=>T(M,t(f))),w(p,C)};W(k,p=>{t(f)!==""&&p(m)})}var I=h(_,4),g=h(u(I),2);{var P=p=>{var C=jt();w(p,C)},B=p=>{var C=Dt(),M=u(C);z($=>T(M,$),[()=>Ct(e.label)]),w(p,C)};W(g,p=>{e.label===null?p(P):p(B,-1)})}var R=h(I,2);{var H=p=>{var C=Tt();z(()=>C.disabled=!e.available),N("click",C,function(...M){e.onreprint?.apply(this,M)}),w(p,C)};W(R,p=>{e.label!==null&&p(H)})}var V=h(R,2),A=u(V);let O;var G=h(A,2),X=u(G),K=h(V,2),F=u(K);ee(F,{name:"settings",size:"1.75rem"}),z(()=>{d=se(c,1,"bar svelte-161y12f",null,d,{live:e.label!==null}),O=se(A,1,"dot svelte-161y12f",null,O,{fault:!e.healthy}),ae(A,"aria-label",e.healthy?"Matériel disponible":"Matériel indisponible"),T(X,e.healthy?"Balance et imprimante disponibles":"Matériel indisponible")}),N("click",K,function(...p){e.onadmin?.apply(this,p)}),w(s,c),Z()}re(["click"]);var Lt=E(''),Nt=E('

Tare g

');function Ot(s,e){Y(e,!0);const n=["1","2","3","4","5","6","7","8","9","0"];var r=Nt(),l=u(r),f=h(u(l),2),c=u(f),d=h(l,2);fe(d,20,()=>n,m=>m,(m,I)=>{var g=Lt(),P=u(g);z(()=>T(P,I)),N("click",g,()=>e.ondigit(I)),w(m,g)});var _=h(d,2),o=u(_),i=h(o,2),y=h(i,2),k=u(y);ee(k,{name:"check",size:"1.5rem"}),z(()=>T(c,e.grams===""?"0":e.grams)),N("click",o,function(...m){e.onclear?.apply(this,m)}),N("click",i,function(...m){e.oncancel?.apply(this,m)}),N("click",y,function(...m){e.onconfirm?.apply(this,m)}),w(s,r),Z()}re(["click"]);function Mt(s){return s===null||s.state==="initializing"?!1:s.scale.connected&&s.printer.health!=="faulted"}const Ee=1500,Wt=2e3,ke=5e3;function Bt(s,e){return e?s>=ke?{showWeight:!1,banner:"Poids indisponible",mustReconnect:!0}:s>=Wt?{showWeight:!1,banner:"Poids indisponible",mustReconnect:!1}:s>=Ee?{showWeight:!1,banner:"",mustReconnect:!1}:{showWeight:!0,banner:"",mustReconnect:!1}:{showWeight:s=ke}}const Ft=250,Ht={show_grid_prices:!0,idle_timeout_s:45,reprint_window_s:60,sound:!0,show_by_unit_products:!1,grid_columns:0,min_products_for_chip:He};class Ut{#e=D(null);get state(){return t(this.#e)}set state(e){b(this.#e,e,!0)}#t=D(null);get catalog(){return t(this.#t)}set catalog(e){b(this.#t,e,!0)}#n=D(ze({showWeight:!0,banner:"",mustReconnect:!1}));get link(){return t(this.#n)}set link(e){b(this.#n,e,!0)}#s=D("");get catalogError(){return t(this.#s)}set catalogError(e){b(this.#s,e,!0)}#r=null;#i=null;#l=Date.now();#a=!1;#o=!1;get presentation(){return this.catalog?.presentation??Ht}start(){this.#u(),this.#c(),this.#i=setInterval(()=>this.#v(),Ft)}stop(){this.#i!==null&&clearInterval(this.#i),this.#i=null,this.#r?.close(),this.#r=null,this.#a=!1}#c(){const e=new EventSource("/api/v1/stream");this.#r=e,e.onopen=()=>{this.#a=!0,this.#l=Date.now()},e.addEventListener("state",n=>this.#d(n)),e.onerror=()=>{this.#a=!1}}#d(e){this.#l=Date.now(),this.#a=!0;const n=JSON.parse(e.data),r=this.state;if(this.state=n,this.catalog===null)return;const l=n.catalog_count!==this.catalog.product_count,f=r!==null&&n.presentation_digest!==r.presentation_digest;(l||f)&&this.#u()}#v(){const e=Bt(Date.now()-this.#l,this.#a);this.link=e,e.mustReconnect&&(this.#r?.close(),this.#l=Date.now(),this.#c())}async#u(){if(!this.#o){this.#o=!0;try{this.catalog=await Ue(),this.catalogError=""}catch{this.catalog===null&&(this.catalogError="Catalogue indisponible. Prévenez un responsable.")}finally{this.#o=!1}}}}const xe="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function Ae(){let s=Date.now();const e=new Array(26);for(let r=9;r>=0;r--)e[r]=xe[s%32],s=Math.floor(s/32);const n=new Uint8Array(16);crypto.getRandomValues(n);for(let r=0;r<16;r++)e[10+r]=xe[n[r]%32];return e.join("")}var Vt=E('
');function Gt(s,e){Y(e,!0);const n=new Ut;let r=D(ze(Ve)),l=D(""),f=D(!1);const c=v(()=>t(l)!==""||t(f));let d=D(null),_=D(-1),o=D(null);const i=v(()=>n.catalog),y=v(()=>t(i)===null?[]:Qe(t(i))),k=v(()=>t(i)===null?[]:Ke(t(i))),m=v(()=>Xe(t(y),t(r),t(l))),I=v(()=>{const a={};for(const S of t(i)?.categories??[])a[S.code]=S.color;return a}),g=v(()=>n.state),P=v(()=>n.presentation),B=v(()=>Mt(t(g))),R=v(()=>{if(n.catalogError!=="")return{message:n.catalogError,hint:"Le poste réessaie tout seul."};if(t(y).length>0)return{message:"Aucun produit ne correspond.",hint:"Effacez des lettres ou changez de rayon."};if(t(i)!==null&&t(i).products.length>0)return{message:t(i).products.every(U=>U.mode==="by_unit")&&!t(P).show_by_unit_products?"Aucun produit à montrer : ce poste masque les produits vendus à l’unité, et le catalogue reçu ne contient que ceux-là.":"Aucun produit à montrer : les réglages d’affichage de ce poste masquent tout ce qu’il a reçu.",hint:"Le fichier est bien arrivé : prévenez un responsable, c’est un réglage de ce poste qui vide la grille."};const a=t(g)?.station??0;return{message:a>0?`Catalogue vide. En attente du fichier flv_${a}.csv.`:"Catalogue vide. En attente du fichier du poste.",hint:"Prévenez un responsable : aucun produit ne peut être pesé."}}),H=v(()=>t(g)?.state==="rejected"?t(g).product?.id??null:null),V=v(()=>t(g)?.state==="faulted"||t(g)?.state==="out_of_service"),A=["product_armed","weight_present","weight_stable","awaiting_stability","validating","printing"],O=v(()=>t(g)!==null&&A.includes(t(g).state)?t(g).product?.id??null:null),G=v(()=>Pt(t(i)?.updated_at??""));ne(()=>(n.start(),()=>n.stop())),ne(()=>{const a=n.state?.revision??-1;t(d)!==null&&a>t(_)&&(b(d,null),b(_,-1))});async function X(a){if(t(d)===null){b(d,a.id,!0),b(_,n.state?.revision??-1,!0);try{await Ge({product_id:a.id,tare_g:n.state?.weight.tare_g??0,units:1,manual_weight_g:0,seen_weight_g:n.state?.weight.gross_g??0,measurement_seq:n.state?.weight.seq??0,key:Ae()})}catch{b(d,null)}}}async function K(){const a=n.state?.reprint.job_id;a===void 0||a===""||await Je(a,Ae())}function F(){b(l,""),b(f,!1)}function p(){b(f,!0)}function C(){const a=t(m).length===1?t(m)[0]:void 0;a!==void 0&&X(a)}function M(a){if(!(a.metaKey||a.ctrlKey||a.altKey)&&!(a.target instanceof HTMLElement&&a.target.tagName==="INPUT")&&!(t(o)!==null||t(V))){if(a.key==="Escape"){a.preventDefault(),F();return}if(a.key==="Backspace"){a.preventDefault(),b(l,t(l).slice(0,-1),!0);return}if(a.key==="Enter"){C();return}a.key.length===1&&/[a-zA-Z0-9 ]/.test(a.key)&&(a.key===" "&&t(l)===""||(a.preventDefault(),b(l,t(l)+a.key)))}}ne(()=>(window.addEventListener("keydown",M),()=>window.removeEventListener("keydown",M)));async function $(){(await st(()=>import("./mount-zw3SIkYn.js"),__vite__mapDeps([0,1,2,3]))).mountAdmin(document.body)}var ie=Vt(),oe=u(ie);{let a=v(()=>t(o)!==null);ot(oe,{get snapshot(){return t(g)},get showWeight(){return n.link.showWeight},get linkBanner(){return n.link.banner},get taring(){return t(a)},ontare:()=>b(o,"")})}var J=h(oe,2);{var ce=a=>{Ot(a,{get grams(){return t(o)},ondigit:S=>b(o,(t(o)??"")+S),onclear:()=>b(o,""),oncancel:()=>b(o,null),onconfirm:()=>b(o,null)})};W(J,a=>{t(o)!==null&&a(ce)})}var le=h(J,2);{let a=v(()=>t(i)?.pricing.primary_code??""),S=v(()=>Object.fromEntries((t(i)?.pricing.tiers??[]).map(Q=>[Q.code,Q.abbrev]))),U=v(()=>t(i)===null&&n.catalogError==="");Et(le,{get products(){return t(m)},get colors(){return t(I)},get primaryCode(){return t(a)},get tierAbbrev(){return t(S)},get selectedID(){return t(O)},get rejectedID(){return t(H)},get busyID(){return t(d)},get showPrices(){return t(P).show_grid_prices},get gridColumns(){return t(P).grid_columns},get loading(){return t(U)},get emptyMessage(){return t(R).message},get emptyHint(){return t(R).hint},onpick:X})}var ue=h(le,2);{var pe=a=>{At(a,{get query(){return t(l)},onquery:S=>b(l,S,!0),onclose:F,onenter:C})};W(ue,a=>{t(c)&&a(pe)})}var de=h(ue,2);dt(de,{get chips(){return t(k)},get active(){return t(r)},get searchFieldOpen(){return t(c)},onselect:a=>b(r,a,!0),onopensearch:p});var ve=h(de,2);{let a=v(()=>t(g)?.last_label??null),S=v(()=>t(g)?.reprint.available??!1),U=v(()=>t(i)?.app_version??"");qt(ve,{get label(){return t(a)},get available(){return t(S)},get catalogAt(){return t(G)},get productCount(){return t(y).length},get appVersion(){return t(U)},get healthy(){return t(B)},onreprint:K,onadmin:$})}var x=h(ve,2);{var L=a=>{{let S=v(()=>t(g).message?.text??"Prévenez un responsable.");we(a,{title:"Poste indisponible",get detail(){return t(S)},get code(){return t(g).fault_code},action:{label:"J’ai compris",run:()=>{Ye()}}})}},j=a=>{we(a,{title:"Poste hors service",detail:"Ce poste ne peut pas peser. Prévenez un responsable.",get code(){return t(g).fault_code}})};W(x,a=>{t(g)?.state==="faulted"?a(L):t(g)?.state==="out_of_service"&&a(j,1)})}w(s,ie),Z()}const Xt=5,Kt="ResizeObserver loop";function Jt(){let s=!1,e=!1;const n=(c,d)=>{s||(s=!0,$e(c,d),Qt(),setTimeout(()=>location.reload(),Xt*1e3))},r=c=>{e||(e=!0,Ze(c))},l=c=>{if((c.error===void 0||c.error===null)&&c.message.startsWith(Kt)){r(c.message);return}n(c.message,c.error?.stack??"")},f=c=>{n(String(c.reason),c.reason?.stack??"")};return window.addEventListener("error",l),window.addEventListener("unhandledrejection",f),()=>{window.removeEventListener("error",l),window.removeEventListener("unhandledrejection",f)}}function Qt(){const s=document.createElement("div");s.className="fatal",s.append(me("h1","Une erreur est survenue"),me("p","L’écran va se recharger tout seul."),me("p","ERR-UI-01","code")),document.body.appendChild(s)}function me(s,e,n=""){const r=document.createElement(s);return r.textContent=e,n!==""&&(r.className=n),r}Jt();document.addEventListener("contextmenu",s=>{Yt(s.target)||s.preventDefault()});document.addEventListener("dragstart",s=>s.preventDefault());et(Gt,{target:document.getElementById("app")});function Yt(s){return s instanceof Element&&s.closest("[data-admin]")!==null} diff --git a/internal/web/dist/assets/index-DQl0r1eZ.js b/internal/web/dist/assets/index-DQl0r1eZ.js deleted file mode 100644 index 06e0afe..0000000 --- a/internal/web/dist/assets/index-DQl0r1eZ.js +++ /dev/null @@ -1,2 +0,0 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/mount-DmM_mBVD.js","assets/app-CFgWi82J.js","assets/app-Cfxw4Luj.css","assets/mount-MmpjwdB2.css"])))=>i.map(i=>d[i]); -import{e as je,u as De,d as re,p as Y,a as R,i as W,I as ee,t as C,b as N,c as w,f as Z,g as E,h as t,s as h,j as ae,k as te,l as se,m as T,n as v,o as u,q as Pe,r as fe,w as Ie,v as Te,x as ne,y as ze,R as qe,z as b,A as I,B as Re,T as Le,C as Ne,N as Oe,D as Me,E as We,F as Be,G as Fe,H as Ce,J as He,K as Ue,L as Ve,M as Ge,O as Ke,P as Xe,Q as Je,S as Qe,U as Ye,V as Ze,W as $e}from"./app-CFgWi82J.js";class be{#e=new WeakMap;#t;#n;static entries=new WeakMap;constructor(e){this.#n=e}observe(e,n){var r=this.#e.get(e)||new Set;return r.add(n),this.#e.set(e,r),this.#s().observe(e,this.#n),()=>{var l=this.#e.get(e);l.delete(n),l.size===0&&(this.#e.delete(e),this.#t.unobserve(e))}}#s(){return this.#t??(this.#t=new ResizeObserver(e=>{for(var n of e){be.entries.set(n.target,n);for(var r of this.#e.get(n.target)||[])r(n)}}))}}var et=new be({box:"border-box"});function he(s,e,n){var r=et.observe(s,()=>n(s[e]));je(()=>(De(()=>n(s[e])),r))}const tt="modulepreload",nt=function(s){return"/"+s},ye={},at=function(e,n,r){let l=Promise.resolve();if(n&&n.length>0){let m=function(o){return Promise.all(o.map(i=>Promise.resolve(i).then(y=>({status:"fulfilled",value:y}),y=>({status:"rejected",reason:y}))))};document.getElementsByTagName("link");const c=document.querySelector("meta[property=csp-nonce]"),d=c?.nonce||c?.getAttribute("nonce");l=m(n.map(o=>{if(o=nt(o),o in ye)return;ye[o]=!0;const i=o.endsWith(".css"),y=i?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${o}"]${y}`))return;const k=document.createElement("link");if(k.rel=i?"stylesheet":tt,i||(k.as="script"),k.crossOrigin="",k.href=o,d&&k.setAttribute("nonce",d),document.head.appendChild(k),i)return new Promise((_,j)=>{k.addEventListener("load",_),k.addEventListener("error",()=>j(new Error(`Unable to preload CSS for ${o}`)))})}))}function f(c){const d=new Event("vite:preloadError",{cancelable:!0});if(d.payload=c,window.dispatchEvent(d),!d.defaultPrevented)throw c}return l.then(c=>{for(const d of c||[])d.status==="rejected"&&f(d.reason);return e().catch(f)})};var st=E('

kg

',1),rt=E('

kg

Poids indisponible

',1),it=E('');function lt(s,e){Y(e,!0);const n=R(e,"taring",3,!1),r=v(()=>{if(e.linkBanner!=="")return e.linkBanner;const A=e.snapshot?.message??null;if(A!==null)return A.text;switch(e.snapshot?.state){case"product_armed":return"Posez votre produit";case"awaiting_stability":return"Pesée en cours…";case"printing":return"Étiquette en cours…";case"entering_tare":return"Tapez la tare en grammes";case"scale_lost":return"Le poids n’est plus disponible.";default:return"Touchez votre produit, l’étiquette sort"}}),l=v(()=>e.snapshot===null?"var(--waiting)":e.snapshot.state==="faulted"||e.snapshot.state==="scale_lost"?"var(--fault)":e.snapshot.state==="rejected"?"var(--warning)":e.snapshot.state==="succeeded"||e.snapshot.weight.latched?"var(--ready)":"var(--waiting)"),f=v(()=>e.showWeight&&e.snapshot!==null&&e.snapshot.weight.available&&!e.snapshot.weight.expired),c=v(()=>{const A=e.snapshot?.message;return A!=null?A.level:e.snapshot?.state==="faulted"||e.snapshot?.state==="scale_lost"?"error":e.snapshot?.state==="rejected"?"warn":"info"});var d=it(),m=u(d);let o;var i=u(m);{var y=A=>{var O=st(),G=Pe(O),K=u(G),X=h(G,2),F=u(X);C(()=>{T(K,e.snapshot.weight.net_text),T(F,`tare ${e.snapshot.weight.tare_g??""} g`)}),w(A,O)},k=A=>{var O=rt();w(A,O)};W(i,A=>{t(f)&&e.snapshot?A(y):A(k,-1)})}var _=h(m,2);let j;var g=u(_),P=h(_,2);let B;var q=u(P);ee(q,{name:"tare",size:"1.4em"});var H=h(P,2);let V;C(()=>{ae(d,"data-state",e.snapshot?.state??"initializing"),o=te(m,"",o,{"border-left-color":t(l)}),j=se(_,1,"instruction svelte-4gizvm",null,j,{warn:t(c)==="warn",error:t(c)==="error"}),T(g,t(r)),B=se(P,1,"tare-key touch-target svelte-4gizvm",null,B,{active:n()}),V=te(H,"",V,{background:t(l)})}),N("click",P,function(...A){e.ontare?.apply(this,A)}),w(s,d),Z()}re(["click"]);var ot=E(''),ct=E('');function ut(s,e){Y(e,!0);var n=ct(),r=u(n);fe(r,21,()=>e.chips,d=>d.code,(d,m)=>{var o=ot();let i,y;var k=h(u(o),2),_=u(k),j=h(k,2),g=u(j);C(P=>{i=se(o,1,"chip touch-target svelte-atgty1",null,i,{active:t(m).code===e.active}),ae(o,"aria-pressed",t(m).code===e.active),y=te(o,"",y,P),T(_,t(m).label),T(g,t(m).count)},[()=>({"--chip-ink":Te(t(m).color),"--chip-wash":Ie(t(m).color)})]),N("click",o,()=>e.onselect(t(m).code)),w(d,o)});var l=h(r,2);let f;var c=u(l);ee(c,{name:"search",size:"1.75rem"}),C(()=>{f=se(l,1,"chip touch-target search-key svelte-atgty1",null,f,{active:e.searchFieldOpen}),ae(l,"aria-pressed",e.searchFieldOpen)}),N("click",l,function(...d){e.onopensearch?.apply(this,d)}),w(s,n),Z()}re(["click"]);var dt=E(''),vt=E('

'),gt=E('

');function we(s,e){Y(e,!0);const n=R(e,"action",3,null);var r=gt(),l=u(r),f=u(l),c=u(f);ee(c,{name:"alert",size:"3rem"});var d=h(f,2),m=u(d),o=h(d,2),i=u(o),y=h(o,2);{var k=g=>{var P=dt(),B=u(P);C(()=>T(B,n().label)),N("click",P,function(...q){n().run?.apply(this,q)}),w(g,P)};W(y,g=>{n()!==null&&g(k)})}var _=h(y,2);{var j=g=>{var P=vt(),B=u(P);C(()=>T(B,e.code)),w(g,P)};W(_,g=>{e.code!==""&&g(j)})}C(()=>{T(m,e.title),T(i,e.detail)}),w(s,r),Z()}re(["click"]);var ht=E('
'),ft=E('

Chargement du catalogue…

',1),pt=E('

'),mt=E('

'),_t=E('
'),bt=E('
'),yt=E('
');function wt(s,e){Y(e,!0);const n=R(e,"gridColumns",3,0),r=R(e,"colors",19,()=>({})),l=R(e,"primaryCode",3,""),f=R(e,"tierAbbrev",19,()=>({})),c=R(e,"selectedID",3,null),d=R(e,"rejectedID",3,null),m=R(e,"busyID",3,null),o=R(e,"showPrices",3,!0),i=R(e,"loading",3,!1),y=R(e,"emptyMessage",3,"Aucun produit ne correspond."),k=R(e,"emptyHint",3,""),_=8,j=26,g="'Inter Variable', system-ui, sans-serif",P="'Inter Variable'",B=16;let q=I(0),H=I(0),V=I(!1);ne(()=>{const x=document.fonts;x!==void 0&&x.load(`700 ${qe}px ${P}`).catch(()=>[]).then(()=>{b(V,!0)})});const A=v(()=>(t(V),Be(g,700)));let O=I(null),G=I(0),K=I(0);const X=v(()=>n()>0?n():t(q)<=0||t(H)<=0?0:Math.max(1,Math.floor((t(q)+_)/(t(H)+_)))),F=v(()=>t(X)>0&&t(q)>0?(t(q)-(t(X)-1)*_)/t(X):0),p=v(()=>n()>0&&t(F)>0&&t(H)>0?t(F)/t(H):1),z=v(()=>We(t(p))),M=v(()=>n()>0?`grid-template-columns: ${Ne(n())}`:void 0),$=v(()=>t(F)>0?t(F)-j:0),ie=v(()=>t(G)>0?t(G):t($));ne(()=>{t(F),t(p),e.products.length;const x=t(O)?.querySelector(".name-box");b(G,x?.clientWidth??0,!0)});const oe=v(()=>{const x=new Map;if(t(A)===null||t(ie)<=0)return x;const L=t(K)>0?t(K):Oe;for(const D of e.products)x.set(D.id,Me(D.name,t(ie),t(A),L,t(z)));return x});var J=yt(),ce=u(J),le=h(ce,2),ue=h(le,2);{var pe=x=>{var L=ft(),D=Pe(L);fe(D,21,()=>({length:B}),Re,(a,S)=>{var U=ht();w(a,U)}),C(()=>te(D,t(M))),he(D,"clientWidth",a=>b(q,a)),w(x,L)},de=x=>{var L=mt(),D=u(L),a=u(D),S=h(D,2);{var U=Q=>{var ge=pt(),me=u(ge);C(()=>T(me,k())),w(Q,ge)};W(S,Q=>{k()!==""&&Q(U)})}C(()=>T(a,y())),w(x,L)},ve=x=>{var L=bt();fe(L,21,()=>e.products,D=>D.id,(D,a)=>{var S=_t(),U=u(S);{let Q=v(()=>t(oe).get(t(a).id)??t(z)),ge=v(()=>t(a).id===c()),me=v(()=>t(a).id===d()),Se=v(()=>t(a).id===m());Le(U,{get product(){return t(a)},get nameSizePx(){return t(Q)},get categoryColor(){return r()[t(a).category_code]},get primaryCode(){return l()},get tierAbbrev(){return f()},get selected(){return t(ge)},get rejected(){return t(me)},get busy(){return t(Se)},get showPrice(){return o()},get onpick(){return e.onpick}})}w(D,S)}),C(()=>{ae(L,"data-tile-count",e.products.length),te(L,t(M))}),he(L,"clientWidth",D=>b(q,D)),w(x,L)};W(ue,x=>{i()?x(pe):e.products.length===0?x(de,1):x(ve,-1)})}ze(J,x=>b(O,x),()=>t(O)),C(()=>{ae(J,"data-tile-scale",t(p)),te(J,`--tile-scale: ${t(p)??""}`)}),he(ce,"clientHeight",x=>b(K,x)),he(le,"clientWidth",x=>b(H,x)),w(s,J),Z()}var Et=E(''),kt=E('
');function xt(s,e){Y(e,!0);let n=I(null);ne(()=>{t(n)?.focus()});var r=kt(),l=u(r);ee(l,{name:"search",size:"1.75rem"});var f=h(l,2);ze(f,i=>b(n,i),()=>t(n));var c=h(f,2);{var d=i=>{var y=Et();N("click",y,function(...k){e.onclose?.apply(this,k)}),w(i,y)};W(c,i=>{e.query!==""&&i(d)})}var m=h(c,2),o=u(m);ee(o,{name:"close",size:"1.5rem"}),C(()=>Fe(f,e.query)),N("input",f,i=>e.onquery(i.currentTarget.value)),N("keydown",f,i=>{i.key==="Escape"?(i.preventDefault(),e.onclose()):i.key==="Enter"&&(i.preventDefault(),e.onenter())}),N("click",m,function(...i){e.onclose?.apply(this,i)}),w(s,r),Z()}re(["input","keydown","click"]);function At(s){if(s==="")return"";const e=new Date(s);if(Number.isNaN(e.getTime()))return"";const n=l=>String(l).padStart(2,"0");return`${`${n(e.getDate())}/${n(e.getMonth()+1)}/${e.getFullYear()}`} ${n(e.getHours())}:${n(e.getMinutes())}:${n(e.getSeconds())}`}function Pt(s){return s.mode==="by_unit"?`${s.product_name} ${s.quantity} ${s.quantity>1?"unités":"unité"}`:`${s.product_name} ${s.net_text} kg`}var zt=E('Catalogue du '),Ct=E('Catalogue en attente'),St=E(' '),jt=E('aucune pour le moment'),Dt=E(' '),It=E(''),Tt=E('
Dernière étiquette
');function qt(s,e){Y(e,!0);const n=R(e,"catalogAt",3,""),r=R(e,"productCount",3,0),l=R(e,"appVersion",3,""),f=v(()=>[r()>0?`${r()} produits pesables`:"",l()!==""?`application ${l()}`:""].filter(p=>p!=="").join(" · "));var c=Tt();let d;var m=u(c),o=u(m);{var i=p=>{var z=zt(),M=h(u(z)),$=u(M);C(()=>T($,n())),w(p,z)},y=p=>{var z=Ct();w(p,z)};W(o,p=>{n()!==""?p(i):p(y,-1)})}var k=h(o,2);{var _=p=>{var z=St(),M=u(z);C(()=>T(M,t(f))),w(p,z)};W(k,p=>{t(f)!==""&&p(_)})}var j=h(m,4),g=h(u(j),2);{var P=p=>{var z=jt();w(p,z)},B=p=>{var z=Dt(),M=u(z);C($=>T(M,$),[()=>Pt(e.label)]),w(p,z)};W(g,p=>{e.label===null?p(P):p(B,-1)})}var q=h(j,2);{var H=p=>{var z=It();C(()=>z.disabled=!e.available),N("click",z,function(...M){e.onreprint?.apply(this,M)}),w(p,z)};W(q,p=>{e.label!==null&&p(H)})}var V=h(q,2),A=u(V);let O;var G=h(A,2),K=u(G),X=h(V,2),F=u(X);ee(F,{name:"settings",size:"1.75rem"}),C(()=>{d=se(c,1,"bar svelte-161y12f",null,d,{live:e.label!==null}),O=se(A,1,"dot svelte-161y12f",null,O,{fault:!e.healthy}),ae(A,"aria-label",e.healthy?"Matériel disponible":"Matériel indisponible"),T(K,e.healthy?"Balance et imprimante disponibles":"Matériel indisponible")}),N("click",X,function(...p){e.onadmin?.apply(this,p)}),w(s,c),Z()}re(["click"]);var Rt=E(''),Lt=E('

Tare g

');function Nt(s,e){Y(e,!0);const n=["1","2","3","4","5","6","7","8","9","0"];var r=Lt(),l=u(r),f=h(u(l),2),c=u(f),d=h(l,2);fe(d,20,()=>n,_=>_,(_,j)=>{var g=Rt(),P=u(g);C(()=>T(P,j)),N("click",g,()=>e.ondigit(j)),w(_,g)});var m=h(d,2),o=u(m),i=h(o,2),y=h(i,2),k=u(y);ee(k,{name:"check",size:"1.5rem"}),C(()=>T(c,e.grams===""?"0":e.grams)),N("click",o,function(..._){e.onclear?.apply(this,_)}),N("click",i,function(..._){e.oncancel?.apply(this,_)}),N("click",y,function(..._){e.onconfirm?.apply(this,_)}),w(s,r),Z()}re(["click"]);function Ot(s){return s===null||s.state==="initializing"?!1:s.scale.connected&&s.printer.health!=="faulted"}const Ee=1500,Mt=2e3,ke=5e3;function Wt(s,e){return e?s>=ke?{showWeight:!1,banner:"Poids indisponible",mustReconnect:!0}:s>=Mt?{showWeight:!1,banner:"Poids indisponible",mustReconnect:!1}:s>=Ee?{showWeight:!1,banner:"",mustReconnect:!1}:{showWeight:!0,banner:"",mustReconnect:!1}:{showWeight:s=ke}}const Bt=250,Ft={show_grid_prices:!0,idle_timeout_s:45,reprint_window_s:60,sound:!0,show_by_unit_products:!1,grid_columns:0};class Ht{#e=I(null);get state(){return t(this.#e)}set state(e){b(this.#e,e,!0)}#t=I(null);get catalog(){return t(this.#t)}set catalog(e){b(this.#t,e,!0)}#n=I(Ce({showWeight:!0,banner:"",mustReconnect:!1}));get link(){return t(this.#n)}set link(e){b(this.#n,e,!0)}#s=I("");get catalogError(){return t(this.#s)}set catalogError(e){b(this.#s,e,!0)}#r=null;#i=null;#l=Date.now();#a=!1;#o=!1;get presentation(){return this.catalog?.presentation??Ft}start(){this.#u(),this.#c(),this.#i=setInterval(()=>this.#v(),Bt)}stop(){this.#i!==null&&clearInterval(this.#i),this.#i=null,this.#r?.close(),this.#r=null,this.#a=!1}#c(){const e=new EventSource("/api/v1/stream");this.#r=e,e.onopen=()=>{this.#a=!0,this.#l=Date.now()},e.addEventListener("state",n=>this.#d(n)),e.onerror=()=>{this.#a=!1}}#d(e){this.#l=Date.now(),this.#a=!0;const n=JSON.parse(e.data),r=this.state;if(this.state=n,this.catalog===null)return;const l=n.catalog_count!==this.catalog.product_count,f=r!==null&&n.presentation_digest!==r.presentation_digest;(l||f)&&this.#u()}#v(){const e=Wt(Date.now()-this.#l,this.#a);this.link=e,e.mustReconnect&&(this.#r?.close(),this.#l=Date.now(),this.#c())}async#u(){if(!this.#o){this.#o=!0;try{this.catalog=await He(),this.catalogError=""}catch{this.catalog===null&&(this.catalogError="Catalogue indisponible. Prévenez un responsable.")}finally{this.#o=!1}}}}const xe="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function Ae(){let s=Date.now();const e=new Array(26);for(let r=9;r>=0;r--)e[r]=xe[s%32],s=Math.floor(s/32);const n=new Uint8Array(16);crypto.getRandomValues(n);for(let r=0;r<16;r++)e[10+r]=xe[n[r]%32];return e.join("")}var Ut=E('
');function Vt(s,e){Y(e,!0);const n=new Ht;let r=I(Ce(Ue)),l=I(""),f=I(!1);const c=v(()=>t(l)!==""||t(f));let d=I(null),m=I(-1),o=I(null);const i=v(()=>n.catalog),y=v(()=>t(i)===null?[]:Je(t(i))),k=v(()=>t(i)===null?[]:Ke(t(i))),_=v(()=>Ge(t(y),t(r),t(l))),j=v(()=>{const a={};for(const S of t(i)?.categories??[])a[S.code]=S.color;return a}),g=v(()=>n.state),P=v(()=>n.presentation),B=v(()=>Ot(t(g))),q=v(()=>{if(n.catalogError!=="")return{message:n.catalogError,hint:"Le poste réessaie tout seul."};if(t(y).length>0)return{message:"Aucun produit ne correspond.",hint:"Effacez des lettres ou changez de rayon."};if(t(i)!==null&&t(i).products.length>0)return{message:t(i).products.every(U=>U.mode==="by_unit")&&!t(P).show_by_unit_products?"Aucun produit à montrer : ce poste masque les produits vendus à l’unité, et le catalogue reçu ne contient que ceux-là.":"Aucun produit à montrer : les réglages d’affichage de ce poste masquent tout ce qu’il a reçu.",hint:"Le fichier est bien arrivé : prévenez un responsable, c’est un réglage de ce poste qui vide la grille."};const a=t(g)?.station??0;return{message:a>0?`Catalogue vide. En attente du fichier flv_${a}.csv.`:"Catalogue vide. En attente du fichier du poste.",hint:"Prévenez un responsable : aucun produit ne peut être pesé."}}),H=v(()=>t(g)?.state==="rejected"?t(g).product?.id??null:null),V=v(()=>t(g)?.state==="faulted"||t(g)?.state==="out_of_service"),A=["product_armed","weight_present","weight_stable","awaiting_stability","validating","printing"],O=v(()=>t(g)!==null&&A.includes(t(g).state)?t(g).product?.id??null:null),G=v(()=>At(t(i)?.updated_at??""));ne(()=>(n.start(),()=>n.stop())),ne(()=>{const a=n.state?.revision??-1;t(d)!==null&&a>t(m)&&(b(d,null),b(m,-1))});async function K(a){if(t(d)===null){b(d,a.id,!0),b(m,n.state?.revision??-1,!0);try{await Ve({product_id:a.id,tare_g:n.state?.weight.tare_g??0,units:1,manual_weight_g:0,seen_weight_g:n.state?.weight.gross_g??0,measurement_seq:n.state?.weight.seq??0,key:Ae()})}catch{b(d,null)}}}async function X(){const a=n.state?.reprint.job_id;a===void 0||a===""||await Xe(a,Ae())}function F(){b(l,""),b(f,!1)}function p(){b(f,!0)}function z(){const a=t(_).length===1?t(_)[0]:void 0;a!==void 0&&K(a)}function M(a){if(!(a.metaKey||a.ctrlKey||a.altKey)&&!(a.target instanceof HTMLElement&&a.target.tagName==="INPUT")&&!(t(o)!==null||t(V))){if(a.key==="Escape"){a.preventDefault(),F();return}if(a.key==="Backspace"){a.preventDefault(),b(l,t(l).slice(0,-1),!0);return}if(a.key==="Enter"){z();return}a.key.length===1&&/[a-zA-Z0-9 ]/.test(a.key)&&(a.key===" "&&t(l)===""||(a.preventDefault(),b(l,t(l)+a.key)))}}ne(()=>(window.addEventListener("keydown",M),()=>window.removeEventListener("keydown",M)));async function $(){(await at(()=>import("./mount-DmM_mBVD.js"),__vite__mapDeps([0,1,2,3]))).mountAdmin(document.body)}var ie=Ut(),oe=u(ie);{let a=v(()=>t(o)!==null);lt(oe,{get snapshot(){return t(g)},get showWeight(){return n.link.showWeight},get linkBanner(){return n.link.banner},get taring(){return t(a)},ontare:()=>b(o,"")})}var J=h(oe,2);{var ce=a=>{Nt(a,{get grams(){return t(o)},ondigit:S=>b(o,(t(o)??"")+S),onclear:()=>b(o,""),oncancel:()=>b(o,null),onconfirm:()=>b(o,null)})};W(J,a=>{t(o)!==null&&a(ce)})}var le=h(J,2);{let a=v(()=>t(i)?.pricing.primary_code??""),S=v(()=>Object.fromEntries((t(i)?.pricing.tiers??[]).map(Q=>[Q.code,Q.abbrev]))),U=v(()=>t(i)===null&&n.catalogError==="");wt(le,{get products(){return t(_)},get colors(){return t(j)},get primaryCode(){return t(a)},get tierAbbrev(){return t(S)},get selectedID(){return t(O)},get rejectedID(){return t(H)},get busyID(){return t(d)},get showPrices(){return t(P).show_grid_prices},get gridColumns(){return t(P).grid_columns},get loading(){return t(U)},get emptyMessage(){return t(q).message},get emptyHint(){return t(q).hint},onpick:K})}var ue=h(le,2);{var pe=a=>{xt(a,{get query(){return t(l)},onquery:S=>b(l,S,!0),onclose:F,onenter:z})};W(ue,a=>{t(c)&&a(pe)})}var de=h(ue,2);ut(de,{get chips(){return t(k)},get active(){return t(r)},get searchFieldOpen(){return t(c)},onselect:a=>b(r,a,!0),onopensearch:p});var ve=h(de,2);{let a=v(()=>t(g)?.last_label??null),S=v(()=>t(g)?.reprint.available??!1),U=v(()=>t(i)?.app_version??"");qt(ve,{get label(){return t(a)},get available(){return t(S)},get catalogAt(){return t(G)},get productCount(){return t(y).length},get appVersion(){return t(U)},get healthy(){return t(B)},onreprint:X,onadmin:$})}var x=h(ve,2);{var L=a=>{{let S=v(()=>t(g).message?.text??"Prévenez un responsable.");we(a,{title:"Poste indisponible",get detail(){return t(S)},get code(){return t(g).fault_code},action:{label:"J’ai compris",run:()=>{Qe()}}})}},D=a=>{we(a,{title:"Poste hors service",detail:"Ce poste ne peut pas peser. Prévenez un responsable.",get code(){return t(g).fault_code}})};W(x,a=>{t(g)?.state==="faulted"?a(L):t(g)?.state==="out_of_service"&&a(D,1)})}w(s,ie),Z()}const Gt=5,Kt="ResizeObserver loop";function Xt(){let s=!1,e=!1;const n=(c,d)=>{s||(s=!0,Ze(c,d),Jt(),setTimeout(()=>location.reload(),Gt*1e3))},r=c=>{e||(e=!0,Ye(c))},l=c=>{if((c.error===void 0||c.error===null)&&c.message.startsWith(Kt)){r(c.message);return}n(c.message,c.error?.stack??"")},f=c=>{n(String(c.reason),c.reason?.stack??"")};return window.addEventListener("error",l),window.addEventListener("unhandledrejection",f),()=>{window.removeEventListener("error",l),window.removeEventListener("unhandledrejection",f)}}function Jt(){const s=document.createElement("div");s.className="fatal",s.append(_e("h1","Une erreur est survenue"),_e("p","L’écran va se recharger tout seul."),_e("p","ERR-UI-01","code")),document.body.appendChild(s)}function _e(s,e,n=""){const r=document.createElement(s);return r.textContent=e,n!==""&&(r.className=n),r}Xt();document.addEventListener("contextmenu",s=>{Qt(s.target)||s.preventDefault()});document.addEventListener("dragstart",s=>s.preventDefault());$e(Vt,{target:document.getElementById("app")});function Qt(s){return s instanceof Element&&s.closest("[data-admin]")!==null} diff --git a/internal/web/dist/assets/mount-DmM_mBVD.js b/internal/web/dist/assets/mount-DmM_mBVD.js deleted file mode 100644 index c4a3597..0000000 --- a/internal/web/dist/assets/mount-DmM_mBVD.js +++ /dev/null @@ -1,99 +0,0 @@ -import{X as Pr,Y as Tr,Z as zr,_ as Ea,$ as Jt,e as jr,a0 as ir,a1 as Rr,a2 as Or,a3 as Ar,u as ma,a4 as Ca,a5 as Nr,a6 as or,x as qt,A as G,h as e,z as v,H as ft,d as ct,a as Be,s as n,i as _,t as f,l as Mt,j as he,m as o,b as Ce,c as l,o as s,g as c,p as Me,I as Ir,f as We,n as u,q as te,a7 as Ia,r as Te,G as Zt,a8 as ta,a9 as Rt,B as da,aa as ot,ab as Xt,ac as ka,C as Dr,J as ur,y as sa,T as Fr,ad as Kt,F as Ur,E as Mr,D as Wr,M as Br,K as Vr,W as Gr,ae as Hr}from"./app-CFgWi82J.js";function cr(a){throw new Error("https://svelte.dev/e/lifecycle_outside_component")}function Jr(a,t,...r){var i=new zr(a);Pr(()=>{const d=t()??null;i.ensure(d,d&&(p=>d(p,...r)))},Tr)}function Pa(a,t,r=!1){if(a.multiple){if(t==null)return;if(!Rr(t))return Or();for(var i of a.options)i.selected=t.includes(la(i));return}for(i of a.options){var d=la(i);if(Ar(d,t)){i.selected=!0;return}}(!r||t!==void 0)&&(a.selectedIndex=-1)}function dr(a){var t=new MutationObserver(()=>{"__value"in a&&Pa(a,a.__value)});t.observe(a,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["value"]}),ir(()=>{t.disconnect()})}function Kr(a,t,r=t){var i=new WeakSet,d=!0;Ea(a,"change",p=>{var k=p?"[selected]":":checked",T;if(a.multiple)T=[].map.call(a.querySelectorAll(k),la);else{var m=a.querySelector(k)??a.querySelector("option:not([disabled])");T=m&&la(m)}r(T),a.__value=T,Jt!==null&&i.add(Jt)}),jr(()=>{var p=t();if(a===document.activeElement){var k=Jt;if(i.has(k))return}if(Pa(a,p,d),d&&p===void 0){var T=a.querySelector(":checked");T!==null&&(p=la(T),r(p))}a.__value=p,d=!1}),dr(a)}function la(a){return"__value"in a?a.__value:a.value}function ia(a,t,r=t){var i=new WeakSet;Ea(a,"input",async d=>{var p=d?a.defaultValue:a.value;if(p=ba(a)?wa(p):p,r(p),Jt!==null&&i.add(Jt),await Nr(),p!==(p=t())){var k=a.selectionStart,T=a.selectionEnd,m=a.value.length;if(a.value=p??"",T!==null){var j=a.value.length;k===T&&T===m&&j>m?(a.selectionStart=j,a.selectionEnd=j):(a.selectionStart=k,a.selectionEnd=Math.min(T,j))}}}),ma(t)==null&&a.value&&(r(ba(a)?wa(a.value):a.value),Jt!==null&&i.add(Jt)),Ca(()=>{var d=t();if(a===document.activeElement){var p=Jt;if(i.has(p))return}ba(a)&&d===wa(a.value)||a.type==="date"&&!d&&!a.value||d!==a.value&&(a.value=d??"")})}function Da(a,t,r=t){Ea(a,"change",i=>{var d=i?a.defaultChecked:a.checked;r(d)}),ma(t)==null&&r(a.checked),Ca(()=>{var i=t();a.checked=!!i})}function ba(a){var t=a.type;return t==="number"||t==="range"}function wa(a){return a===""?null:+a}function Fa(a,t,r,i,d){var p=()=>{i(r[a])};r.addEventListener(t,p),d?Ca(()=>{r[a]=d()}):p(),(r===document.body||r===window||r===document)&&ir(()=>{r.removeEventListener(t,p)})}function Ta(a){or===null&&cr(),qt(()=>{const t=ma(a);if(typeof t=="function")return t})}function $r(a){or===null&&cr(),Ta(()=>()=>ma(a))}const Yr="ERR-CFG-02";class zt extends Error{constructor(t,r,i="",d=[]){super(r),this.status=t,this.code=i,this.faults=d,this.name="AdminError"}status;code;faults;get needsPassword(){return this.status===401}get needsFirstPassword(){return this.status===409&&this.code===Yr}get needsCredentials(){return this.needsPassword||this.needsFirstPassword}}function fa(a){return a instanceof zt&&a.needsCredentials}function Xr(){return Wt("/admin/api/health")}function Qr(){return He("/admin/api/troubleshooting/reprint",{})}function Zr(){return He("/admin/api/troubleshooting/reload-catalog",{})}function en(a){return He("/admin/api/troubleshooting/manual-entry",{on:a})}function tn(){return He("/admin/api/troubleshooting/roll-changed",{})}function an(a){return He("/admin/api/troubleshooting/fallback-printer",{on:a})}function rn(){return He("/admin/api/troubleshooting/test-scale",{})}function nn(){return He("/admin/api/troubleshooting/test-printer",{})}function sn(){return He("/admin/api/troubleshooting/test-label",{})}async function vr(a){const t=new FormData;t.append("file",a,a.name);const r=await fetch("/admin/api/catalog/import",{method:"POST",body:t});return Ra(r,"POST /admin/api/catalog/import")}const ln="/admin/api/diagnostic.zip";function on(a){return He("/admin/api/session",{password:a})}async function un(){await fetch("/admin/api/session",{method:"DELETE"})}function cn(a,t){return He("/admin/api/session/recovery",{code:a,password:t})}function za(){return Wt("/admin/api/config")}function dn(a){return ja("PUT","/admin/api/config",a)}function vn(){return He("/admin/api/config/reload",{})}function pn(){return He("/admin/api/restart",{})}function fn(){return He("/admin/api/reboot",{})}function gn(){return ja("DELETE","/admin/api/reboot",{})}function mn(){return He("/admin/api/config/confirm",{})}async function hn(){return(await Wt("/admin/api/config/versions")).versions}function _n(a){return He("/admin/api/config/restore",{version:a})}async function bn(){return(await Wt("/admin/api/ports")).ports}async function wn(){return(await Wt("/admin/api/printers")).printers}async function yn(){return(await He("/admin/api/printers/discover",{})).printers}function xn(a){return He("/admin/api/scale/detect",{port:a})}async function Ua(a,t){return(await He("/admin/api/scale/capture",{port:a,seconds:t})).frames}function pr(a){return He("/admin/api/printer/test?what="+a,{})}function kn(a,t,r,i){const d=new URLSearchParams({template:a,demo:t?"1":"0",dual:r?"1":"0"});return d.set("t",String(i)),"/admin/api/label/preview.png?"+d.toString()}async function qn(a){const t=new URLSearchParams(a);return(await Wt("/admin/api/journal?"+t)).weighings}function Sn(a){return"/admin/api/journal/export.csv?"+new URLSearchParams(a).toString()}async function Ln(a){const t=new URLSearchParams(a);return(await Wt("/admin/api/technical?"+t)).entries}function En(a){const t=a===void 0?"":"?id="+String(a);return Wt("/admin/api/imports"+t)}function Cn(){return He("/admin/api/catalog/reload",{})}function Pn(){return He("/admin/api/catalog/forget-quarantine",{})}function Ma(a,t){return He("/admin/api/products/"+encodeURIComponent(a)+"/decision",t)}function Tn(a){return He("/admin/api/replay",{frame:a})}async function Wt(a){const t=await fetch(a,{headers:{accept:"application/json"}});return Ra(t,"GET "+a)}function He(a,t){return ja("POST",a,t)}async function ja(a,t,r){const i=await fetch(t,{method:a,headers:{"content-type":"application/json"},body:JSON.stringify(r)});return Ra(i,a+" "+t)}async function Ra(a,t){const r=await a.text();if(!a.ok){const i=fr(r);throw new zt(a.status,jn(r,t,a.status)+zn(a),i?.code??"",i?.faults??[])}if(r!=="")return JSON.parse(r)}function zn(a){if(a.status!==429)return"";const t=Number(a.headers.get("Retry-After")??"");if(!Number.isFinite(t)||t<=0)return"";const r=Math.ceil(t/60);return r>1?` Réessayez dans ${String(r)} minutes.`:" Réessayez dans une minute."}function jn(a,t,r){const i=fr(a);return i?.message!==void 0&&i.message!==""?i.message:`${t} a répondu ${String(r)}.`}function fr(a){try{return JSON.parse(a)}catch{return null}}function Rn(){return Wt("/admin/api/update")}function On(){return He("/admin/api/update/check",{})}function An(a){return He("/admin/api/update/apply",{version:a})}class Nn{constructor(t){this.admin=t}admin;#t=G(null);get config(){return e(this.#t)}set config(t){v(this.#t,t,!0)}#r=G("");get fingerprint(){return e(this.#r)}set fingerprint(t){v(this.#r,t,!0)}#a=G(ft([]));get retired(){return e(this.#a)}set retired(t){v(this.#a,t,!0)}#e=G(null);get pending(){return e(this.#e)}set pending(t){v(this.#e,t,!0)}#n=G(ft([]));get faults(){return e(this.#n)}set faults(t){v(this.#n,t,!0)}#l=G(!1);get dirty(){return e(this.#l)}set dirty(t){v(this.#l,t,!0)}async load(){const t=await this.admin.load(()=>za());t!==null&&(this.config=t.config,this.fingerprint=t.config_fingerprint,this.retired=t.retired_keys??[],this.pending=t.pending_confirmation,this.faults=[],this.dirty=!1)}value(t){let r=this.config;for(const i of t.split(".")){if(r===null||typeof r!="object")return;r=r[i]}return r}text(t){const r=this.value(t);return r==null?"":String(r)}number(t){const r=this.value(t);return typeof r=="number"?r:Number(r??0)}flag(t){return this.value(t)===!0}set(t,r){if(this.config===null)return;const i=t.split("."),d=i.pop();if(d===void 0)return;let p=this.config;for(const k of i){const T=p[k];(T===null||typeof T!="object")&&(p[k]={}),p=p[k]}p[d]=r,this.dirty=!0}unset(t){const r=t.split("."),i=r.pop();if(i===void 0||this.config===null)return!1;let d=this.config;for(const p of r){if(d===null||typeof d!="object")return!1;d=d[p]}return d===null||typeof d!="object"?!1:(delete d[i],this.dirty=!0,!0)}async save(){if(this.config===null)return!1;this.faults=[];const t=this.config;let r;try{r=await dn(t)}catch(i){if(fa(i))throw i;return this.admin.report(i),this.faults=In(this.admin),!1}return this.config=r.config,this.fingerprint=r.config_fingerprint,this.retired=r.retired_keys??[],this.pending=r.pending_confirmation,this.dirty=!1,!0}async confirm(){try{await mn()}catch(t){if(fa(t))throw t;this.admin.report(t);return}this.admin.notice="La configuration est confirmée.",this.pending=null,await this.admin.refresh()}dropRetired(t){if(t.includes("[")){this.admin.actionError=`« ${t} » est logée dans un tableau : cet écran ne sait pas la retirer de là. Modifiez le fichier de configuration lui-même.`;return}this.unset(t)&&(this.retired=this.retired.filter(r=>r!==t))}}function In(a){return a.lastFaults}const Dn={"station.number":"Numéro du poste","station.name":"Nom du poste","station.coop":"Nom de la coopérative","network.listen":"Adresse d’écoute","network.admin_on_lan":"Administration accessible depuis le réseau","ui.language":"Langue","ui.sound":"Son","ui.idle_timeout_s":"Retour à l’accueil après (secondes)","ui.reprint_window_s":"Réimpression possible pendant (secondes)","ui.show_grid_prices":"Afficher les prix sur les tuiles","ui.show_by_unit_products":"Afficher les produits vendus à l’unité","ui.grid_columns":"Colonnes de la grille","scale.type":"Protocole de la balance","scale.present":"Ce poste a une balance","scale.manual_entry_allowed":"Saisie manuelle autorisée","scale.degrade_after_s":"Passer en mode dégradé après (secondes)","scale.options.port":"Port série","scale.options.baud":"Vitesse (bauds)","scale.options.bits":"Bits de données","scale.options.parity":"Parité","scale.options.stop":"Bits d’arrêt","scale.options.backoff_min_ms":"Attente minimale avant réessai (ms)","scale.options.backoff_max_ms":"Attente maximale avant réessai (ms)","printer.type":"Pilote d’impression","printer.template":"Gabarit d’étiquette","printer.options.transport":"Transport","printer.options.queue":"File d’impression","printer.options.path":"Fichier de périphérique","printer.options.address":"Adresse réseau","printer.options.darkness":"Noircissement","printer.options.speed":"Vitesse d’impression","printer.options.offset_x":"Décalage horizontal (dots)","printer.options.offset_y":"Décalage vertical (dots)","printer.options.invert_bits":"Inverser les points","printer.options.copies":"Exemplaires","printer.options.roll_capacity":"Étiquettes par rouleau","pricing.amount_rounding":"Arrondi du montant","pricing.unit_price_rounding":"Arrondi du prix au kilo","pricing.primary_code":"Tarif principal","pricing.reference_code":"Tarif de référence","barcode.verify_reference_check_digit":"Vérifier la clé de contrôle","limits.empty_max_g":"Plateau considéré vide en dessous de (g)","limits.basket_check_enabled":"Vérifier la présence du panier","limits.basket_min_g":"Poids du panier, borne basse (g)","limits.basket_max_g":"Poids du panier, borne haute (g)","limits.min_weight_g":"Poids minimum accepté","limits.max_weight_g":"Poids maximum accepté","limits.max_tare_g":"Tare maximale","limits.min_units":"Unités minimum","limits.max_units":"Unités maximum","limits.max_amount_cents":"Montant maximum (centimes)","stability.mode":"Exigence de stabilité","stability.min_duration_ms":"Durée de stabilité (ms)","stability.tolerance_g":"Tolérance de stabilité (g)","stability.timeout_ms":"Délai d’attente de stabilité (ms)","stability.on_timeout":"Au bout du délai","stability.min_latch_rate":"Taux d’accroche minimal","stability.latch_rate_window_ms":"Fenêtre de mesure du taux (ms)","stability.expiry_floor_ms":"Péremption, plancher (ms)","stability.expiry_ceiling_ms":"Péremption, plafond (ms)","stability.expiry_factor":"Péremption, facteur","catalog.type":"Où le poste va chercher le catalogue","catalog.options.directory":"Répertoire surveillé","catalog.options.url":"Adresse du serveur","catalog.options.username":"Compte","catalog.options.password":"Mot de passe","catalog.options.separator":"Séparateur du CSV","catalog.options.poll_interval_s":"Vérifier toutes les (secondes)","catalog.options.stable_polls":"Vérifications avant lecture","catalog.options.max_file_size_mb":"Taille maximale du fichier (Mo)","catalog.options.max_image_size_kb":"Taille maximale d’une image (Ko)","catalog.options.min_readable_ratio":"Part minimale de lignes lisibles","catalog.options.max_weighable_drop":"Baisse maximale des produits pesables","catalog.options.max_archives":"Archives conservées","catalog.options.archive_days":"Archives conservées (jours)","catalog.options.failures_before_reject":"Échecs avant mise en quarantaine","catalog.images.source":"Origine des photos","catalog.images.path":"Répertoire des photos","catalog.fallback_category":"Rayon par défaut","journal.max_rows":"Pesées conservées","journal.max_days":"Pesées conservées (jours)","journal.max_technical":"Événements techniques conservés","admin.session_minutes":"Durée d’une session (minutes)","admin.attempts_per_minute":"Tentatives par minute","maintenance.weekly_integrity_check":"Contrôle d’intégrité hebdomadaire","maintenance.disk_alert_mb":"Alerte disque en dessous de (Mo)"};function Ct(a){return Dn[a]??a}const Fn={station:"l’identité du poste",network:"le réseau",ui:"l’écran client",scale:"la balance",printer:"l’imprimante",pricing:"les tarifs",barcode:"le code-barres",limits:"les garde-fous",stability:"la stabilité",catalog:"le catalogue",journal:"le journal",maintenance:"la maintenance"};function Un(a){return Fn[a]??a}const Mn={scale:"balance",printer:"imprimante",catalog:"catalogue",ui:"écran",config:"configuration",http:"réseau",system:"système"};function gr(a){return Mn[a]??"origine inconnue"}const qa="openscale.admin.preferences",mr="technical";class Wn{#t=G(ft(Bn()));get showTechnicalNames(){return e(this.#t)}set showTechnicalNames(t){v(this.#t,t,!0)}toggleTechnicalNames(){this.showTechnicalNames=!this.showTechnicalNames,Vn(this.showTechnicalNames)}}function Bn(){try{return globalThis.localStorage?.getItem(qa)===mr}catch{return!1}}function Vn(a){try{a?globalThis.localStorage?.setItem(qa,mr):globalThis.localStorage?.removeItem(qa)}catch{}}const jt=new Wn,Gn=3e3,Hn=["hardware","label","rules","catalog","journal","station","update"];function Wa(a){return Hn.includes(a)}function Jn(a){return a instanceof zt&&a.needsCredentials}class Kn{constructor(t=Gn){this.periodMs=t}periodMs;#t=G(null);get health(){return e(this.#t)}set health(t){v(this.#t,t,!0)}#r=G("dashboard");get page(){return e(this.#r)}set page(t){v(this.#r,t,!0)}#a=G(!1);get expert(){return e(this.#a)}set expert(t){v(this.#a,t,!0)}#e=G("");get linkError(){return e(this.#e)}set linkError(t){v(this.#e,t,!0)}#n=G("");get actionError(){return e(this.#n)}set actionError(t){v(this.#n,t,!0)}#l=G(!1);get needsFirstPassword(){return e(this.#l)}set needsFirstPassword(t){v(this.#l,t,!0)}#d=G("");get notice(){return e(this.#d)}set notice(t){v(this.#d,t,!0)}#v=G(!1);get busy(){return e(this.#v)}set busy(t){v(this.#v,t,!0)}#p=G(ft([]));get lastFaults(){return e(this.#p)}set lastFaults(t){v(this.#p,t,!0)}#f=G(null);get pending(){return e(this.#f)}set pending(t){v(this.#f,t,!0)}#i=null;#o=null;start(){this.refresh(),this.#i=setInterval(()=>{this.refresh()},this.periodMs)}stop(){this.#i!==null&&clearInterval(this.#i),this.#i=null}async refresh(){try{this.health=await Xr(),this.linkError=""}catch(t){this.linkError=Ba(t)}}#u(){this.busy=!0,this.notice="",this.actionError="",this.needsFirstPassword=!1}#s(t){this.actionError=Ba(t),this.needsFirstPassword=t instanceof zt&&t.needsFirstPassword,this.#m(t)}async run(t){this.#u();let r=null;try{r=await t(),this.notice=r.message}catch(i){this.#s(i)}finally{this.busy=!1}return await this.refresh(),r}async login(t){this.#u();try{return await on(t),this.expert=!0,this.notice="Session d’administration ouverte.",!0}catch(r){return this.expert=!1,this.#s(r),!1}finally{this.busy=!1}}async recover(t,r){this.#u();try{const i=await cn(t,r);return this.expert=!0,this.notice=i.warning??"Le mot de passe est remplacé et la session est ouverte.",!0}catch(i){return this.#s(i),!1}finally{this.busy=!1}}async protect(t){try{return await t()}catch(r){if(!Jn(r))return this.#s(r),null;if(!await this.#g(r))return null;try{return await t()}catch(i){return this.#s(i),null}}}#g(t){return this.pending={kind:t.needsFirstPassword?"first-password":"password",message:t.message},new Promise(r=>{this.#o=r})}async answerPassword(t){await this.login(t)&&this.#c(!0)}async answerRecovery(t,r){await this.recover(t,r)&&this.#c(!0)}cancelPassword(){this.#c(!1)}#c(t){this.pending=null,this.notice="";const r=this.#o;this.#o=null,r?.(t)}async logout(){await un(),this.expert=!1,this.page="dashboard",this.notice="Session d’administration fermée."}open(t){this.actionError="",this.notice="",this.needsFirstPassword=!1,this.page=t}async load(t){try{const r=await t();return this.actionError="",this.lastFaults=[],r}catch(r){return this.lastFaults=r instanceof zt?r.faults:[],this.#s(r),null}}report(t){this.lastFaults=t instanceof zt?t.faults:[],this.#s(t)}#m(t){t instanceof zt&&t.needsPassword&&(this.expert=!1)}}function Ba(a){return a instanceof zt?a.message:a instanceof Error?"Le poste n’a pas répondu : "+a.message:"Le poste n’a pas répondu."}var $n=c('clé'),Yn=c('');function Pe(a,t){const r=Be(t,"kind",3,"read"),i=Be(t,"busy",3,!1),d=Be(t,"disabled",3,!1),p=Be(t,"protected",3,!1),k=Be(t,"act",3,void 0);var T=Yn();let m;var j=s(T),q=n(j);{var D=L=>{var W=$n();l(L,W)};_(q,L=>{p()&&L(D)})}f(()=>{m=Mt(T,1,`act ${r()??""}`,"svelte-5tpq0o",m,{"touch-target":r()==="destructive",busy:i()}),he(T,"data-kind",r()),he(T,"data-act",k()),T.disabled=d()||i(),o(j,`${(i()?"En cours…":t.label)??""} `)}),Ce("click",T,function(...L){t.onrun?.apply(this,L)}),l(a,T)}ct(["click"]);var Xn=c(`

Le code de secours a été tiré à l’installation de ce poste et imprimé - sur sa fiche, rangée dans le classeur du magasin. Il n’existe nulle part ailleurs.

`,1),Qn=c(`

Oublié ? Le code de secours de la fiche d’installation en repose un. - Un responsable peut aussi le faire en ligne de commande, avec openscale config password.

`,1),Zn=c('

'),es=c('');function ts(a,t){Me(t,!0);const r=u(()=>t.admin.pending?.kind==="first-password");let i=G(""),d=G("");const p=8,k=u(()=>e(r)?e(d).trim().length>0&&e(i).length>=p:e(i).length>0);async function T(){!e(k)||t.admin.busy||(e(r)?await t.admin.answerRecovery(e(d),e(i)):await t.admin.answerPassword(e(i)),v(i,""),v(d,""))}var m=es(),j=s(m),q=s(j),D=s(q),L=s(D);Ir(L,{name:"settings",size:"1.5rem"});var W=n(D,2),z=s(W),R=n(q,2),K=s(R),F=n(R,2);{var E=C=>{var y=Xn(),S=n(te(y),2),O=n(s(S),2);Ia(O);var A=n(S,2),h=s(A);h.textContent="Nouveau mot de passe — 8 caractères au moins";var V=n(h,2);Ce("keydown",O,X=>X.key==="Enter"&&void T()),ia(O,()=>e(d),X=>v(d,X)),Ce("keydown",V,X=>X.key==="Enter"&&void T()),ia(V,()=>e(i),X=>v(i,X)),l(C,y)},w=C=>{var y=Qn(),S=te(y),O=n(s(S),2);Ia(O),Ce("keydown",O,A=>A.key==="Enter"&&void T()),ia(O,()=>e(i),A=>v(i,A)),l(C,y)};_(F,C=>{e(r)?C(E):C(w,-1)})}var g=n(F,2);{var U=C=>{var y=Zn(),S=s(y);f(()=>o(S,t.admin.actionError)),l(C,y)};_(g,C=>{t.admin.actionError!==""&&C(U)})}var B=n(g,2),$=s(B);Pe($,{label:"Annuler",onrun:()=>t.admin.cancelPassword()});var J=n($,2);{let C=u(()=>e(r)?"Poser ce mot de passe":"Continuer"),y=u(()=>!e(k)||t.admin.busy);Pe(J,{kind:"write",get label(){return e(C)},get disabled(){return e(y)},onrun:()=>{T()}})}f(()=>{o(z,e(r)?"Ce poste n’a pas encore de mot de passe":"Mot de passe d’administration"),o(K,t.admin.pending?.message??"")}),l(a,m),We()}ct(["keydown"]);var as=c(' '),rs=c(""),ns=c(''),ss=c(''),ls=c(""),is=c(""),os=c('

'),us=c(" "),cs=c('

'),ds=c('
');function at(a,t){Me(t,!0);const r=Be(t,"hint",3,""),i=Be(t,"kind",3,"text"),d=Be(t,"disabled",3,!1),p=Be(t,"fault",3,""),k=Be(t,"allowed",19,()=>[]),T=Be(t,"choices",19,()=>[]),m=u(()=>`field-${t.path.replace(/\./gu,"-")}`);var j=ds();let q;var D=s(j),L=s(D),W=s(L),z=n(L,2);{var R=C=>{var y=as(),S=s(y);f(()=>o(S,t.path)),l(C,y)};_(z,C=>{jt.showTechnicalNames&&C(R)})}var K=n(D,2);{var F=C=>{var y=ns();Te(y,21,T,O=>O.value,(O,A)=>{var h=rs(),V=s(h),X={};f(()=>{o(V,e(A).label),X!==(X=e(A).value)&&(h.value=(h.__value=e(A).value)??"")}),l(O,h)});var S;dr(y),f(()=>{he(y,"id",e(m)),y.disabled=d(),S!==(S=t.value)&&(y.value=(y.__value=t.value)??"",Pa(y,t.value))}),Ce("change",y,O=>t.onchange(O.currentTarget.value)),l(C,y)},E=C=>{var y=ss();f(()=>{he(y,"id",e(m)),he(y,"type",i()),Zt(y,t.value),y.disabled=d(),he(y,"list",k().length>0?e(m)+"-allowed":void 0)}),Ce("input",y,S=>t.onchange(S.currentTarget.value)),l(C,y)};_(K,C=>{T().length>0?C(F):C(E,-1)})}var w=n(K,2);{var g=C=>{var y=is();Te(y,20,k,S=>S,(S,O)=>{var A=ls(),h={};f(()=>{h!==(h=O)&&(A.value=(A.__value=O)??"")}),l(S,A)}),f(()=>he(y,"id",e(m)+"-allowed")),l(C,y)};_(w,C=>{k().length>0&&C(g)})}var U=n(w,2);{var B=C=>{var y=os(),S=s(y);f(()=>o(S,r())),l(C,y)};_(U,C=>{r()!==""&&C(B)})}var $=n(U,2);{var J=C=>{var y=cs(),S=s(y),O=n(S);{var A=h=>{var V=us(),X=s(V);f(ae=>o(X,`Valeurs acceptées : ${ae??""}.`),[()=>k().join(", ")]),l(h,V)};_(O,h=>{k().length>0&&h(A)})}f(()=>o(S,`${p()??""} `)),l(C,y)};_($,C=>{p()!==""&&C(J)})}f(()=>{q=Mt(j,1,"field svelte-1oilv5s",null,q,{refused:p()!==""}),he(D,"for",e(m)),o(W,t.label)}),l(a,j),We()}ct(["change","input"]);var vs=c('

'),ps=c('

');function ze(a,t){const r=Be(t,"note",3,"");var i=ps(),d=s(i),p=s(d),k=n(d,2);{var T=j=>{var q=vs(),D=s(q);f(()=>o(D,r())),l(j,q)};_(k,j=>{r()!==""&&j(T)})}var m=n(k,2);Jr(m,()=>t.children),f(()=>o(p,t.title)),l(a,i)}function Ye(a,t){return a.faults.find(r=>r.field===t)?.message??""}function Qt(a,t){return a.faults.find(r=>r.field===t)?.allowed??[]}var fs=c('

Lecture des réglages du poste…

'),gs=c(`

Le poste y cherche le fichier , et le supprime - une fois lu : c’est ce qui dit au producteur que la livraison est prise.

`,1),ms=c(`

Sur un serveur WebDAV, le dépôt d’un fichier CSV depuis cet écran n’est plus - possible : le poste n’a plus de répertoire local où l’écrire. C’est le seul recours - du jour de la mise en service.

`,1),hs=c(`

Ce poste ne déclare aucune source : choisissez-en une ci-dessus, sinon il n’ira - chercher aucun catalogue.

`),_s=c('
',1);function bs(a,t){Me(t,!0);const r={local_drop:["catalog.options.directory"],webdav:["catalog.options.url","catalog.options.username","catalog.options.password"]},i=u(()=>t.draft.text("catalog.type"));function d(p){t.draft.set("catalog.type",p);for(const[k,T]of Object.entries(r))if(k!==p)for(const m of T)t.draft.unset(m)}ze(a,{title:"Où le poste va chercher le catalogue",children:(p,k)=>{var T=_s(),m=te(T),j=s(m),q=s(j),D=n(j,2),L=s(D),W=n(m,2);{var z=E=>{var w=fs();l(E,w)},R=E=>{var w=gs(),g=te(w);{let J=u(()=>Ct("catalog.options.directory")),C=u(()=>t.draft.text("catalog.options.directory")),y=u(()=>Ye(t.draft,"catalog.options.directory"));at(g,{get label(){return e(J)},path:"catalog.options.directory",get value(){return e(C)},hint:"Laissez vide pour le répertoire du poste, celui que le service crée lui-même. Un répertoire nommé ici doit exister : le poste ne le crée pas.",get fault(){return e(y)},onchange:S=>t.draft.set("catalog.options.directory",S)})}var U=n(g,2),B=n(s(U)),$=s(B);f(()=>o($,`flv_${t.station??""}.csv`)),l(E,w)},K=E=>{var w=ms(),g=te(w);{let $=u(()=>Ct("catalog.options.url")),J=u(()=>t.draft.text("catalog.options.url")),C=u(()=>Ye(t.draft,"catalog.options.url"));at(g,{get label(){return e($)},path:"catalog.options.url",get value(){return e(J)},get fault(){return e(C)},onchange:y=>t.draft.set("catalog.options.url",y)})}var U=n(g,2);{let $=u(()=>Ct("catalog.options.username")),J=u(()=>t.draft.text("catalog.options.username")),C=u(()=>Ye(t.draft,"catalog.options.username"));at(U,{get label(){return e($)},path:"catalog.options.username",get value(){return e(J)},get fault(){return e(C)},onchange:y=>t.draft.set("catalog.options.username",y)})}var B=n(U,2);{let $=u(()=>Ct("catalog.options.password")),J=u(()=>Ye(t.draft,"catalog.options.password"));at(B,{get label(){return e($)},path:"catalog.options.password",kind:"password",value:"",hint:"Laissez vide : le mot de passe actuel est conservé.",get fault(){return e(J)},onchange:C=>t.draft.set("catalog.options.password",C)})}l(E,w)},F=E=>{var w=hs();l(E,w)};_(W,E=>{t.draft.config===null?E(z):e(i)==="local_drop"?E(R,1):e(i)==="webdav"?E(K,2):E(F,-1)})}f(()=>{ta(q,e(i)==="local_drop"),ta(L,e(i)==="webdav")}),Ce("change",q,()=>d("local_drop")),Ce("change",L,()=>d("webdav")),l(p,T)},$$slots:{default:!0}}),We()}ct(["change"]);const Va=1e3;function va(a){const t=Oa(a);return t===null?"":[ea(t.getDate()),ea(t.getMonth()+1),String(t.getFullYear())].join("/")}function Pt(a){const t=Oa(a);return t===null?"":`${va(a)} à ${ea(t.getHours())}:${ea(t.getMinutes())}`}function ws(a){const t=Oa(a);return t===null?"":[ea(t.getHours()),ea(t.getMinutes()),ea(t.getSeconds())].join(":")}function Sa(a){const t=a/1e6;return t<1e3?`${Math.round(t)} Mo`:`${hr(t/1e3)} Go`}function ne(a){const t=String(Math.abs(Math.trunc(a))),r=[];for(let i=t.length;i>0;i-=3)r.unshift(t.slice(Math.max(0,i-3),i));return(a<0?"-":"")+r.join(" ")}function ua(a){return a0}function ks(a){const t=a.motive!=="";return{canWithdraw:t||!pa(!1,a.waiverInForce),canOfferAgain:t||!pa(!0,a.waiverInForce),canSaveWaiver:xs(a.typedWaiver)&&(t||!pa(a.offeredInForce,a.typedWaiver)),canDropWaiver:t||!pa(a.offeredInForce,null)}}function _r(a){const t=[];return a.offered||t.push("retiré de la grille"),a.min_weight_g!==null&&t.push(`peut peser à partir de ${ne(a.min_weight_g)} g`),t.length===0?"aucune restriction":t.join(" · ")}function ca(a,t,r,i){const d=t>1?i:r;return a>=t?`${ne(t)} ${d}.`:`${ne(a)} lignes affichées sur ${ne(t)} ${d}.`}function qs(a,t){return t>a?`${ne(a)} produits affichés sur ${ne(t)} trouvés — précisez votre recherche.`:`${ne(t)} ${t>1?"produits trouvés":"produit trouvé"}.`}function Ss(a){return`${ne(a)} ${a>1?"imports affichés":"import affiché"} : le poste n’en publie jamais plus de vingt.`}function ya(a,t,r,i){const d=`${ne(r)} ${r>1?t:a}`;return r<=i?d+".":`${d} — seules les ${ne(i)} premières lignes sont affichées.`}function Ls(a,t){const r=`${ne(a)} ${a>1?"produits retirés":"produit retiré"}`;return t===""?r+".":`${r} depuis l’import du ${Pt(t)}.`}var Es=c('

Aucune décision locale : la grille est celle du fichier.

'),Cs=c('
  • '),Ps=c('

      ',1);function Ts(a,t){Me(t,!0);const r=20,i=u(()=>t.decisions.slice(0,r));ze(a,{title:"Décisions en vigueur",note:"Ce qu’un humain a décidé, produit par produit. C’est ici que se reprend un produit retiré de la grille.",children:(d,p)=>{var k=Rt(),T=te(k);{var m=q=>{var D=Es();l(q,D)},j=q=>{var D=Ps(),L=te(D),W=s(L),z=n(L,2),R=s(z);Te(R,21,()=>e(i),K=>K.product_id,(K,F)=>{var E=Cs(),w=s(E),g=n(w,2),U=s(g),B=n(g,2),$=s(B),J=n(B,2),C=s(J),y=n(J,2),S=s(y),O=n(y,2),A=s(O);f((h,V,X)=>{o(U,h),o($,e(F).product_id),o(C,V),o(S,e(F).reason),o(A,X)},[()=>t.nameOf(e(F).product_id),()=>_r(e(F)),()=>va(e(F).decided_at)]),Ce("click",w,()=>t.onchoose(e(F).product_id)),l(K,E)}),f(K=>o(W,K),[()=>ca(e(i).length,t.decisions.length,"décision en vigueur","décisions en vigueur")]),l(q,D)};_(T,q=>{t.decisions.length===0?q(m):q(j,-1)})}l(d,k)},$$slots:{default:!0}}),We()}ct(["click"]);function zs(a){return a==="loading"?"Lecture des signalements du dernier import…":"Les signalements du dernier import n’ont pas pu être lus : cet écran ne sait pas ce qu’ils disent."}function js(a){return a==="loading"?"Lecture de l’historique des imports…":"L’historique des imports n’a pas pu être lu : cet écran ne sait pas ce qu’il contient."}function Aa(a,t,r){return a!==void 0?a:t==="loading"?"Lecture du nom…":t==="unread"?r:"Produit absent du catalogue en service"}var Ga=c('

      '),Rs=c(' '),Os=c('
    • '),As=c('

        ',1);function xa(a,t){Me(t,!0);const r=Be(t,"remedy",3,""),i=50,d=u(()=>t.findings.slice(0,i)),p=u(()=>t.findings.length>e(d).length);ze(a,{get title(){return t.title},get note(){return t.note},children:(k,T)=>{var m=Rt(),j=te(m);{var q=W=>{var z=Ga(),R=s(z);f(K=>{he(z,"data-unread",t.list),o(R,K)},[()=>zs(t.state)]),l(W,z)},D=W=>{var z=Ga(),R=s(z);f(()=>o(R,t.none)),l(W,z)},L=W=>{var z=As(),R=te(z),K=s(R),F=n(K);{var E=U=>{var B=ot();f(()=>o(B,r())),l(U,B)};_(F,U=>{e(p)&&r()!==""&&U(E)})}var w=n(R,2),g=s(w);Te(g,21,()=>e(d),da,(U,B)=>{var $=Os(),J=s($),C=s(J),y=n(J,2);{var S=M=>{var b=Rs(),I=s(b);f(()=>o(I,e(B).product_name)),l(M,b)};_(y,M=>{e(B).product_name!==""&&M(S)})}var O=n(y,2),A=s(O),h=n(O,2),V=s(h),X=n(h,2),ae=s(X);f(M=>{o(C,`ligne ${M??""}`),o(A,e(B).product_id),o(V,e(B).value),o(ae,e(B).message)},[()=>ne(e(B).csv_line)]),l(U,$)}),f(U=>{he(R,"data-tally",t.list),o(K,`${U??""} `),he(w,"data-rows",t.list)},[()=>ca(e(d).length,t.findings.length,t.singular,t.plural)]),l(W,z)};_(j,W=>{t.state!=="read"?W(q):t.findings.length===0?W(D,1):W(L,-1)})}l(k,m)},$$slots:{default:!0}}),We()}function br(a,t){const r=a.rows_read_count,i=a.images_decoded_count,d=r-i,p=[{count:wt(a.weighable_count),label:"pesables",note:`(${wt(i)} avec photo, ${wt(d)} sans)`,link:""},{count:wt(a.not_weighable_count),label:"non pesables",note:Ws(t),link:""}];if(a.anomalies_count>0&&p.push({count:wt(a.anomalies_count),label:"anomalies",note:"à corriger dans Odoo",link:Ha(a.anomalies_count)}),a.unit_mismatches_count>0){const k=a.unit_mismatches_count;p.push({count:"+ "+wt(k),label:k===1?"unité divergente":"unités divergentes",note:"pesable, unité à corriger",link:Ha(k)})}return{headline:`Catalogue du ${va(a.occurred_at)} — ${wt(r)} produits reçus`,lines:p,oneLine:Ns(a)}}function Ns(a){const t=[`${wt(a.rows_read_count)} reçus`,`${wt(a.weighable_count)} pesables`,`${wt(a.not_weighable_count)} non pesables`,`${wt(a.anomalies_count)} anomalies`],r=a.unit_mismatches_count;return r>0&&t.push(`${wt(r)} ${r===1?"unité divergente":"unités divergentes"}`),t.join(" · ")}const Is={applied:"appliqué",unchanged:"identique au précédent",rejected:"refusé",failed:"échec"},Ds={local_drop:"dépôt local",webdav:"WebDAV",manual:"déposé sur l’écran"};function ha(a){return Is[a]??"résultat inconnu"}function wr(a){return Ds[a]??"source inconnue"}function Fs(a,t){const r=[a.file_name===""?"Le dernier fichier":a.file_name,ha(a.result)],i=Pt(a.occurred_at);i!==""&&r.push("le "+i),r.push("via "+wr(a.source));const d=a.reason===""?"":" "+a.reason;return`${r.join(" ")} — ${br(a,t).oneLine}.${d}`}function Us(a){return a===""?"Aucun nouvel import enregistré à cet instant, et ce poste ne publie pas ce qu’il surveille.":"Aucun nouvel import enregistré à cet instant. Le poste surveille "+a+" : le fichier n’y était pas, ou il n’a pas encore fini d’arriver."}function Ms(a){return"Ce poste n’a pas de journal : il ne pourra rien dire de l’issue de cette relecture."+(a===""?"":" Le poste surveille "+a+".")}function Ha(a){return a===1?"voir la ligne":`voir les ${wt(a)} lignes`}function Ws(a){return a.map(t=>`${Bs(t)} (${wt(t.count)})`).join(", ")}function Bs(a){switch(a.code){case"PREPACKAGED_PRODUCT":return"préemballés";case"INTERNAL_CODE_NOT_WEIGHABLE":return a.value===""?"code interne":"code interne "+a.value;case"NO_BARCODE":return"sans code-barres";default:return a.code}}function wt(a){return String(a)}var Vs=c('

        '),Gs=c('

        Aucun import dans l’historique.

        '),Hs=c(' '),Js=c('

        QuandFichierSourceRésultatMotifLuesPesablesNon pesablesAnomaliesRetirés
        ',1);function Ks(a,t){Me(t,!0);function r(i){const d=ha(i.result);return i.code===""?d:`${d} (${i.code})`}ze(a,{title:"Vingt derniers imports",children:(i,d)=>{var p=Rt(),k=te(p);{var T=q=>{var D=Vs(),L=s(D);f(W=>o(L,W),[()=>js(t.state)]),l(q,D)},m=q=>{var D=Gs();l(q,D)},j=q=>{var D=Js(),L=te(D),W=s(L),z=n(L,2),R=s(z),K=n(s(R));Te(K,21,()=>t.imports,F=>F.id,(F,E)=>{var w=Hs(),g=s(w),U=s(g),B=n(g),$=s(B),J=n(B),C=s(J),y=n(J),S=s(y),O=n(y),A=s(O),h=n(O),V=s(h),X=n(h),ae=s(X),M=n(X),b=s(M),I=n(M),Y=s(I),ue=n(I),fe=s(ue);f((pe,ce,N,Q,re,Z,ie,we)=>{o(U,pe),o($,e(E).file_name),o(C,ce),o(S,N),o(A,e(E).reason),o(V,Q),o(ae,re),o(b,Z),o(Y,ie),o(fe,we)},[()=>Pt(e(E).occurred_at),()=>wr(e(E).source),()=>r(e(E)),()=>ne(e(E).rows_read_count),()=>ne(e(E).weighable_count),()=>ne(e(E).not_weighable_count),()=>ne(e(E).anomalies_count),()=>ne(e(E).products_withdrawn_count)]),l(F,w)}),f(F=>o(W,F),[()=>Ss(t.imports.length)]),l(q,D)};_(k,q=>{t.state!=="read"?q(T):t.imports.length===0?q(m,1):q(j,-1)})}l(i,p)},$$slots:{default:!0}}),We()}var $s=c(' '),Ys=c(''),Xs=c('
        '),Qs=c('

        ');function yr(a,t){Me(t,!0);const r=u(()=>br(t.record,t.motives));var i=Qs(),d=s(i),p=s(d),k=n(d,2);Te(k,21,()=>e(r).lines,j=>j.label,(j,q)=>{var D=Xs(),L=s(D),W=s(L),z=n(L,2),R=s(z),K=s(R),F=n(R,2);{var E=U=>{var B=$s(),$=s(B);f(()=>o($,e(q).note)),l(U,B)};_(F,U=>{e(q).note!==""&&U(E)})}var w=n(F,2);{var g=U=>{var B=Ys(),$=s(B);f(()=>o($,e(q).link)),Ce("click",B,()=>t.onshowrows(e(q).label==="anomalies"?"anomalies":"units")),l(U,B)};_(w,U=>{e(q).link!==""&&t.onshowrows!==void 0&&U(g)})}f(()=>{he(D,"data-row",e(q).label),o(W,e(q).count),o(K,e(q).label)}),l(j,D)});var T=n(k,2),m=s(T);f(()=>{o(p,e(r).headline),o(m,e(r).oneLine)}),l(a,i),We()}ct(["click"]);var Zs=c(" "),el=c(' '),tl=c('');function Na(a,t){Me(t,!0);const r=Be(t,"hint",3,"");var i=tl(),d=s(i),p=n(d,2),k=s(p),T=s(k),m=n(k,2);{var j=L=>{var W=Zs(),z=s(W);f(()=>o(z,t.path)),l(L,W)};_(m,L=>{jt.showTechnicalNames&&L(j)})}var q=n(m,2);{var D=L=>{var W=el(),z=s(W);f(()=>o(z,r())),l(L,W)};_(q,L=>{r()!==""&&L(D)})}f(L=>{he(i,"data-flag",t.path),he(i,"data-on",L),ta(d,t.on),o(T,t.label)},[()=>String(t.on)]),Ce("change",d,L=>t.onchange(L.currentTarget.checked)),l(a,i),We()}ct(["change"]);function Ja(a){const t=`${ne(a.columns)} ${a.columns>1?"colonnes":"colonne"}`,r=`${ne(a.rows)} ${a.rows>1?"rangées":"rangée"}`;return`${t} × ${r}`}function al(a){const t=a.layout,r=[];if(a.columns===Xt)return r.push(t===null?"Automatique : la grille suit la largeur de l’écran. Un écran plus large en montre davantage sans qu’on y revienne.":`Automatique : ${Ja(t)} sur cet écran. Un écran plus large en montrera davantage sans qu’on y revienne.`),r;if(t===null)return r.push(`${ne(a.columns)} ${a.columns>1?"colonnes":"colonne"} sur tous les écrans. Cet écran ne sait pas dire combien de rangées cela fait ici.`),r;const i=t.columns*t.rows;if(r.push(`${Ja(t)} — ${ne(i)} ${i>1?"tuiles":"tuile"} d’un coup, sur cet écran (${String(a.viewport.width)} × ${String(a.viewport.height)}).`),a.tileCount>0){const p=Math.ceil(a.tileCount/i);r.push(a.tileCount>1?`Les ${ne(a.tileCount)} tuiles de la grille tiennent en ${ne(p)} ${p>1?"écrans":"écran"}.`:"La seule tuile de la grille tient en un écran.")}const d=a.floor;if(d!==null&&d.names>0){const p=d.names>1;r.push(`${ne(d.names)} ${p?"noms":"nom"} sur ${ne(a.tileCount)} ${p?"atteignent":"atteint"} le plancher de ${ne(ka)} px : `+(d.rows>1?`leurs ${ne(d.rows)} rangées peuvent être plus hautes que les autres.`:"leur rangée peut être plus haute que les autres."))}return r}function rl(a,t,r){if(a==="loading")return"Lecture du catalogue en service…";if(a==="unread")return"Le catalogue en service n’a pas pu être lu : cet écran ne sait pas combien de produits se vendent à l’unité.";if(t===0)return"Aucun produit vendu à l’unité dans le catalogue en service.";const i=t>1,d=i?"produits vendus à l’unité sont":"produit vendu à l’unité est",p=r?`${i?"montrés":"montré"} dans la grille de ce poste`:`${i?"masqués":"masqué"} sur ce poste`;return`${ne(t)} ${d} ${p}.`}function nl(a){return["localhost","127.0.0.1"].includes(a)?"":"Cet écran n’est pas celui du poste : ce compte vaut pour l’écran que vous lisez."}const sl=-1,ll=10;class xr{#t=G("");get sentence(){return e(this.#t)}set sentence(t){v(this.#t,t,!0)}#r=0;#a="";#e=0;begin(t){this.sentence="",this.#r=t.last_import_id,this.#a=t.watched,this.#e=ll}forget(){this.sentence="",this.#e=0}observe(t){if(this.#e===0)return;if(t.counters.journal_rows_count===sl){this.#n(Ms(this.#a));return}const r=t.catalog;if(r!==null&&r.id!==this.#r){this.#n(Fs(r,t.catalog_motives));return}this.#e-=1,this.#e===0&&(this.sentence=Us(this.#a))}#n(t){this.sentence=t,this.#e=0}}var il=c('

        Aucun import enregistré sur ce poste.

        '),ol=c('

        '),ul=c(`

        « Oublier la quarantaine » fait relire un fichier que le poste avait écarté : c’est le - seul geste de cette page qui puisse remettre en service un catalogue refusé.

        `,1),cl=c(""),dl=c(''),vl=c('

        '),pl=c('

        '),fl=c('
        '),gl=c(`

        Un produit masqué reste vendable : la caisse lit toujours son code-barres, et une - étiquette déjà imprimée reste valable. Ce réglage ne fait que retirer sa tuile.

        Colonnes de la grille

        Se tromper ne coûte rien d’autre que de revenir ici : le réglage ne change ni le - fichier reçu, ni les étiquettes déjà imprimées.

        `,1),ml=c('

        '),hl=c(`

        Déposez ici le fichier clé

        Ce dépôt remplace toute la grille par le fichier apporté : il change ce que le poste - vend, et le mot de passe est donc demandé au moment du dépôt.

        `,1),_l=c('

        Aucun import enregistré : rien n’a encore pu être retiré.

        '),bl=c('

        Aucun produit retiré par le dernier import.

        '),wl=c(`

        Ils restent enregistrés avec leur historique : une étiquette déjà collée reste - lisible en caisse, et un produit qui revient dans un prochain fichier retrouve sa - tuile. Ce poste n’en publie encore que le nombre — leurs noms se lisent dans Odoo, - en comparant avec l’export précédent.

        `,1),yl=c('
      • '),xl=c(`

          Un produit retiré de la grille ne se trouve plus ici : il se reprend dans - « Décisions en vigueur », plus bas.

          `,1),kl=c(`

          Sans motif, le poste refuse la décision. Le seul acte qui s’en passe est celui qui - EFFACE la décision en vigueur : proposer de nouveau un produit qui ne porte aucune - dérogation.

          `),ql=c(`

          Produit choisi :

          Ce produit est-il proposé dans la grille ?

          La dérogation s’enregistre seule : elle ne remet pas dans la grille un produit - qui en a été retiré, et retirer un produit n’efface pas sa dérogation.

          `),Sl=c(' ',1),Ll=c('
          ');function El(a,t){Me(t,!0);const r=Be(t,"admin",7),i=20,d=["NO_BARCODE","PREPACKAGED_PRODUCT","INTERNAL_CODE_NOT_WEIGHABLE"],p="ui.grid_columns",k=[3,4,5,6,7,8,9,10,11,12];let T=G(ft([])),m=G(ft([])),j=G(null);const q=u(()=>e(j)?.products??[]),D=u(()=>e(j)?.presentation??{}),L=u(()=>e(j)?.pricing?.primary_code??"");let W=G(""),z=G(""),R=G(""),K=G(""),F=G(!1),E=G(""),w=G(""),g=G("loading"),U=G("loading");const B=u(()=>r().busy||e(w)!==""),$=u(()=>e(m).filter(H=>H.issue==="anomaly")),J=u(()=>e(m).filter(H=>d.includes(H.code))),C=u(()=>e(m).filter(H=>H.code==="UNIT_MISMATCH")),y=u(()=>new Map(e(q).map(H=>[H.id,H.name]))),S=u(()=>e(q).filter(H=>H.mode==="by_unit").length),O=u(()=>rl(e(U),e(S),t.draft.flag("ui.show_by_unit_products"))),A=u(()=>t.draft.number(p)),h=u(()=>X("ui.show_by_unit_products",e(D).show_by_unit_products??!1)?e(q):e(q).filter(H=>H.mode!=="by_unit")),V=u(()=>e(h)[0]===void 0?null:{...e(h)[0],name:"·",image_url:"",prices:e(h)[0].prices??[]});function X(H,ge){return t.draft.value(H)===void 0?ge:t.draft.flag(H)}let ae=G(null),M=G(null),b=G(null),I=G(null),Y=G(null),ue=G(0),fe=G(null);qt(()=>{const H=()=>{v(ue,e(ue)+1)};return window.addEventListener("resize",H),()=>window.removeEventListener("resize",H)}),qt(()=>{const H=e(A);e(ue),v(fe,Q(H),!0)});const pe=u(()=>{const H=e(fe);if(H===null||H.contentWidthPx<=0||H.nameBoxPx<=0||e(h).length===0)return null;const ge=Z();if(ge===null)return null;const _e=Ur(ge.family,ge.weight);if(_e===null)return null;const ke=Mr(H.tileScale),Ke=new Set;let Je=0;for(const[et,Ie]of e(h).entries())Wr(Ie.name,H.contentWidthPx,_e,H.nameBoxPx,ke)>ka||(Je+=1,Ke.add(Math.floor(et/H.columns)));return{names:Je,rows:Ke.size}}),ce=u(()=>al({columns:e(A),layout:e(fe),tileCount:e(h).length,floor:e(pe),viewport:{width:window.innerWidth,height:window.innerHeight}})),N=u(()=>e(fe)===null?"":nl(window.location.hostname));function Q(H){const ge=e(ae),_e=e(M),ke=e(b),Ke=e(I),Je=e(Y);if(ge===null||_e===null||Ke===null||Je===null||ke===null)return null;_e.style.gridTemplateColumns=Dr(H);const et=re(_e),Ie=et[0];if(Ie===void 0)return null;const Oe=Ke.clientWidth,$e=H===Xt||Oe<=0?1:Ie/Oe;ge.style.setProperty("--tile-scale",String($e));const Ge=Number.parseFloat(getComputedStyle(_e).rowGap),tt=Je.clientHeight,ut=ke.offsetHeight,xe=ke.querySelector(".name-box");return!Number.isFinite(Ge)||tt<=0||ut<=0||xe===null?null:{columns:et.length,rows:Math.max(1,Math.floor((tt+Ge)/(ut+Ge))),contentWidthPx:xe.clientWidth,nameBoxPx:xe.clientHeight,tileScale:$e}}function re(H){const ge=getComputedStyle(H).gridTemplateColumns.split(/\s+/u).filter(ke=>ke!=="");if(ge.length===0)return[];const _e=ge.map(ke=>ke.endsWith("px")?Number.parseFloat(ke):Number.NaN);return _e.every(ke=>Number.isFinite(ke)&&ke>0)?_e:[]}function Z(){const H=e(b)?.querySelector(".name")??null;if(H===null)return null;const ge=getComputedStyle(H),_e=Number.parseInt(ge.fontWeight,10);return ge.fontFamily===""||!Number.isFinite(_e)?null:{family:ge.fontFamily,weight:_e}}const ie=u(()=>e(W)===""?[]:Br(e(q),Vr,e(W))),we=u(()=>e(ie).slice(0,i)),Fe=u(()=>qs(e(we).length,e(ie).length)),x=u(()=>[...t.health.decisions].sort((H,ge)=>ge.decided_at.localeCompare(H.decided_at))),se=u(()=>e(z)===""?null:e(x).find(H=>H.product_id===e(z))??null),ve=u(()=>e(se)===null?!0:e(se).offered),oe=u(()=>e(se)?.min_weight_g??null),le=u(()=>ys(e(K))),je=u(()=>e(R).trim()),qe=u(()=>ks({motive:e(je),offeredInForce:e(ve),waiverInForce:e(oe),typedWaiver:e(le)})),Se=u(()=>t.health.catalog?.products_withdrawn_count??0),Ne=u(()=>e(T).find(H=>t.health.catalog!==null&&H.idLs(e(Se),e(Ne)?.occurred_at??""));let ye;qt(()=>{const H=t.health.catalog?.id??null;H!==ye&&(ye=H,lt())});const Re=new xr;qt(()=>{Re.observe(t.health)});async function lt(){const H=t.health.catalog_findings_id,ge=await r().load(()=>En(H===0?void 0:H));ge===null?v(g,"unread"):(v(T,ge.imports,!0),v(m,ge.findings,!0),v(g,"read"));const _e=await r().load(()=>ur());if(_e===null){v(U,"unread");return}v(j,_e,!0),v(U,"read")}async function it(H,ge){v(w,H,!0),r().actionError="",r().notice="",Re.forget();try{const _e=await r().protect(ge);return _e===null?null:(r().notice=_e.message,await r().refresh(),_e)}finally{v(w,"")}}async function gt(){const H=await it("reload",Cn);H!==null&&Re.begin(H)}function ht(H){return Aa(e(y).get(H),e(U),"Nom inconnu : le catalogue n’a pas pu être lu")}function dt(H){v(z,H,!0),v(R,"");const ge=e(x).find(_e=>_e.product_id===H)?.min_weight_g??null;v(K,ge===null?"":String(ge),!0)}async function mt(H){if(e(z)==="")return;const ge=e(z),_e=e(oe),ke=e(je);await it("offered",()=>Ma(ge,{offered:H,min_weight_g:_e,reason:ke}))!==null&&v(R,"")}async function yt(H){if(e(z)==="")return;const ge=e(z),_e=e(ve),ke=e(je);await it(H===null?"waiver-off":"waiver",()=>Ma(ge,{offered:_e,min_weight_g:H,reason:ke}))!==null&&v(R,"")}async function Ze(H){if(v(F,!1),H!=null){v(E,""),r().actionError="",r().notice="",v(w,"import");try{const ge=await r().protect(()=>vr(H));if(ge===null)return;v(E,`${H.name} : ${ne(ge.rows_read_count)} lignes lues, ${ne(ge.weighable_count)} pesables. `+(ge.reason===""?"Le fichier est déposé ; son résultat s’inscrira dans l’historique des imports.":ge.reason)),await r().refresh()}finally{v(w,"")}}}function me(H){H.preventDefault(),v(F,!1);const ge=H.dataTransfer?.files.item(0);if(e(B)){v(E,`${ge?.name??"Ce fichier"} n’a pas été déposé : un acte est déjà en cours sur cette page. Réessayez quand il aura répondu.`);return}Ze(ge)}function Le(H){const ge=H.files?.item(0);H.value="",Ze(ge)}var rt=Ll(),Ft=s(rt);bs(Ft,{get draft(){return t.draft},get station(){return t.health.station}});var Ot=n(Ft,2);ze(Ot,{title:"Dernier import",children:(H,ge)=>{var _e=ul(),ke=te(_e);{var Ke=xe=>{var Xe=il();l(xe,Xe)},Je=xe=>{yr(xe,{get record(){return t.health.catalog},get motives(){return t.health.catalog_motives}})};_(ke,xe=>{t.health.catalog===null?xe(Ke):xe(Je,-1)})}var et=n(ke,2),Ie=s(et),Oe=n(et,2);{var $e=xe=>{var Xe=ol(),vt=s(Xe);f(()=>o(vt,Re.sentence)),l(xe,Xe)};_(Oe,xe=>{Re.sentence!==""&&xe($e)})}var Ge=n(Oe,2),tt=s(Ge);{let xe=u(()=>e(w)==="reload");Pe(tt,{act:"reload",kind:"write",label:"Recharger le catalogue",protected:!0,get busy(){return e(xe)},get disabled(){return e(B)},onrun:()=>{gt()}})}var ut=n(tt,2);{let xe=u(()=>e(w)==="quarantine");Pe(ut,{act:"quarantine",kind:"destructive",label:"Oublier la quarantaine",protected:!0,get busy(){return e(xe)},get disabled(){return e(B)},onrun:()=>{it("quarantine",Pn)}})}f(()=>o(Ie,t.health.catalog_source===null?"Aucune source de catalogue publiée par ce poste.":"Source : "+t.health.catalog_source.label)),l(H,_e)},$$slots:{default:!0}});var Tt=n(Ot,2);ze(Tt,{title:"Ce que la grille montre",note:"Un réglage d’affichage : il ne change ni le fichier reçu, ni ce que le poste sait peser.",children:(H,ge)=>{var _e=gl(),ke=te(_e);{let Ee=u(()=>t.draft.flag("ui.show_by_unit_products"));Na(ke,{path:"ui.show_by_unit_products",label:"Afficher les produits vendus à l’unité",hint:"Décoché, leurs tuiles quittent la grille et la recherche ne les retrouve plus. Ce que le poste perd : une tuile vendue à l’unité imprime une étiquette sans jamais lire la balance, et c’est le seul geste que ce réglage retire.",get on(){return e(Ee)},onchange:Ue=>t.draft.set("ui.show_by_unit_products",Ue)})}var Ke=n(ke,2),Je=s(Ke),et=n(Ke,4),Ie=n(s(et));{var Oe=Ee=>{var Ue=cl();Ue.textContent="ui.grid_columns",l(Ee,Ue)};_(Ie,Ee=>{jt.showTechnicalNames&&Ee(Oe)})}var $e=n(et,2),Ge=s($e),tt=s(Ge),ut=n(Ge,2);Te(ut,16,()=>k,Ee=>Ee,(Ee,Ue)=>{var st=dl(),_t=s(st),P=n(_t);f((ee,be)=>{he(st,"data-columns",Ue),he(st,"data-on",ee),Zt(_t,Ue),ta(_t,e(A)===Ue),o(P,` ${be??""}`)},[()=>String(e(A)===Ue),()=>ne(Ue)]),Ce("change",_t,()=>t.draft.set(p,Ue)),l(Ee,st)});var xe=n($e,2),Xe=s(xe);Te(Xe,17,()=>e(ce),da,(Ee,Ue)=>{var st=vl(),_t=s(st);f(()=>o(_t,e(Ue))),l(Ee,st)});var vt=n(Xe,2);{var St=Ee=>{var Ue=pl(),st=s(Ue);f(()=>o(st,e(N))),l(Ee,Ue)};_(vt,Ee=>{e(N)!==""&&Ee(St)})}var nt=n(xe,4),pt=s(nt),xt=s(pt);{var Lt=Ee=>{var Ue=fl(),st=s(Ue);{let _t=u(()=>X("ui.show_grid_prices",e(D).show_grid_prices??!0));Fr(st,{get product(){return e(V)},get nameSizePx(){return ka},get primaryCode(){return e(L)},get showPrice(){return e(_t)},onpick:()=>{}})}sa(Ue,_t=>v(b,_t),()=>e(b)),l(Ee,Ue)};_(xt,Ee=>{e(V)!==null&&Ee(Lt)})}sa(pt,Ee=>v(M,Ee),()=>e(M));var It=n(pt,2);sa(It,Ee=>v(I,Ee),()=>e(I));var Dt=n(It,2);sa(Dt,Ee=>v(Y,Ee),()=>e(Y)),sa(nt,Ee=>v(ae,Ee),()=>e(ae)),f(Ee=>{o(Je,e(O)),he(Ge,"data-columns",Xt),he(Ge,"data-on",Ee),Zt(tt,Xt),ta(tt,e(A)===Xt)},[()=>String(e(A)===Xt)]),Ce("change",tt,()=>t.draft.set(p,Xt)),l(H,_e)},$$slots:{default:!0}});var At=n(Tt,2);ze(At,{title:"Déposer un catalogue",note:"Glissez le fichier CSV ici, ou choisissez-le. Il passe par le même chemin que le fichier du producteur.",children:(H,ge)=>{var _e=hl(),ke=te(_e);{var Ke=xe=>{var Xe=ml(),vt=s(Xe);f(()=>o(vt,e(E))),l(xe,Xe)};_(ke,xe=>{e(E)!==""&&xe(Ke)})}var Je=n(ke,2);let et;var Ie=s(Je),Oe=n(s(Ie)),$e=s(Oe),Ge=n(Ie,2),tt=s(Ge),ut=n(tt);f(()=>{et=Mt(Je,1,"drop svelte-3gk7u7",null,et,{dropping:e(F),working:e(w)==="import"}),o($e,`flv_${t.health.station??""}.csv`),o(tt,`${e(w)==="import"?"Import en cours…":"Choisir un fichier"} `),ut.disabled=e(B)}),Kt("dragover",Je,xe=>{xe.preventDefault(),!e(B)&&v(F,!0)}),Kt("dragleave",Je,()=>v(F,!1)),Kt("drop",Je,me),Ce("change",ut,xe=>Le(xe.currentTarget)),l(H,_e)},$$slots:{default:!0}});var Nt=n(At,2);xa(Nt,{title:"Anomalies à corriger dans Odoo",note:"Chaque ligne porte le nom du produit, son numéro dans le CSV, son motif et la valeur fautive.",list:"anomalies",get state(){return e(g)},get findings(){return e($)},singular:"anomalie",plural:"anomalies",none:"Aucune anomalie sur le dernier import.",remedy:"Corrigez celles-ci dans Odoo : l’import suivant ne signalera que ce qui reste."});var Ut=n(Nt,2);xa(Ut,{title:"Unités divergentes",note:"Le produit reste proposé : le code-barres fait foi, seul le libellé du prix est faux.",list:"mismatches",get state(){return e(g)},get findings(){return e(C)},singular:"unité divergente",plural:"unités divergentes",none:"Aucune unité divergente sur le dernier import."});var Bt=n(Ut,2);xa(Bt,{title:"Produits non pesables",note:"Un inventaire, pas une liste d’erreurs : ces produits portent déjà leur code-barres et n’ont aucune raison d’être pesés.",list:"not-weighable",get state(){return e(g)},get findings(){return e(J)},singular:"produit non pesable",plural:"produits non pesables",none:"Aucun produit non pesable sur le dernier import."});var $t=n(Bt,2);ze($t,{title:"Produits retirés depuis l’import précédent",note:"Un produit absent du nouveau fichier est marqué retiré à sa date, jamais supprimé.",children:(H,ge)=>{var _e=Rt(),ke=te(_e);{var Ke=Ie=>{var Oe=_l();l(Ie,Oe)},Je=Ie=>{var Oe=bl();l(Ie,Oe)},et=Ie=>{var Oe=wl(),$e=te(Oe),Ge=s($e);f(()=>o(Ge,e(Ve))),l(Ie,Oe)};_(ke,Ie=>{t.health.catalog===null?Ie(Ke):e(Se)===0?Ie(Je,1):Ie(et,-1)})}l(H,_e)},$$slots:{default:!0}});var Vt=n($t,2);ze(Vt,{title:"Décider d’un produit",note:"Retirer un produit et l’autoriser à peser moins sont deux décisions séparées : l’une n’efface pas l’autre.",children:(H,ge)=>{var _e=Sl(),ke=n(te(_e),2),Ke=n(ke,2);{var Je=Oe=>{var $e=xl(),Ge=te($e),tt=s(Ge),ut=n(Ge,2),xe=s(ut);Te(xe,21,()=>e(we),Xe=>Xe.id,(Xe,vt)=>{var St=yl(),nt=s(St),pt=s(nt),xt=n(nt,2),Lt=s(xt),It=n(xt,2),Dt=s(It);f(()=>{o(pt,e(vt).name),o(Lt,e(vt).id),o(Dt,`${e(vt).unit_price_text??""}${e(vt).price_suffix??""}`)}),Ce("click",nt,()=>dt(e(vt).id)),l(Xe,St)}),f(()=>o(tt,e(Fe))),l(Oe,$e)};_(Ke,Oe=>{e(W)!==""&&Oe(Je)})}var et=n(Ke,2);{var Ie=Oe=>{var $e=ql(),Ge=s($e),tt=n(s(Ge)),ut=s(tt),xe=n(tt),Xe=n(Ge,2),vt=s(Xe),St=n(Xe,4),nt=n(St,2);{var pt=de=>{var Ae=kl();l(de,Ae)};_(nt,de=>{e(je)===""&&de(pt)})}var xt=n(nt,2),Lt=n(s(xt),2),It=s(Lt);{var Dt=de=>{{let Ae=u(()=>e(w)==="offered"),De=u(()=>e(B)||!e(qe).canWithdraw);Pe(de,{act:"offered",kind:"destructive",label:"Ne plus proposer ce produit",protected:!0,get busy(){return e(Ae)},get disabled(){return e(De)},onrun:()=>{mt(!1)}})}},Ee=de=>{{let Ae=u(()=>e(w)==="offered"),De=u(()=>e(B)||!e(qe).canOfferAgain);Pe(de,{act:"offered",kind:"write",label:"Le proposer de nouveau",protected:!0,get busy(){return e(Ae)},get disabled(){return e(De)},onrun:()=>{mt(!0)}})}};_(It,de=>{e(ve)?de(Dt):de(Ee,-1)})}var Ue=n(xt,2),st=n(s(Ue),2),_t=n(st,2),P=s(_t);{let de=u(()=>e(w)==="waiver"),Ae=u(()=>e(B)||!e(qe).canSaveWaiver);Pe(P,{act:"waiver",kind:"write",label:"Enregistrer la dérogation",protected:!0,get busy(){return e(de)},get disabled(){return e(Ae)},onrun:()=>{yt(e(le))}})}var ee=n(P,2);{var be=de=>{{let Ae=u(()=>e(w)==="waiver-off"),De=u(()=>e(B)||!e(qe).canDropWaiver);Pe(de,{act:"waiver-off",kind:"write",label:"Retirer la dérogation",protected:!0,get busy(){return e(Ae)},get disabled(){return e(De)},onrun:()=>{yt(null)}})}};_(ee,de=>{e(oe)!==null&&de(be)})}f((de,Ae)=>{he($e,"data-decision",e(z)),o(ut,de),o(xe,` (${e(z)??""})`),o(vt,`En vigueur : ${Ae??""}`),Zt(st,e(K))},[()=>ht(e(z)),()=>e(se)===null?"aucune décision — ce produit suit les règles générales":_r(e(se))]),ia(St,()=>e(R),de=>v(R,de)),Ce("input",st,de=>v(K,de.currentTarget.value,!0)),l(Oe,$e)};_(et,Oe=>{e(z)!==""&&Oe(Ie)})}ia(ke,()=>e(W),Oe=>v(W,Oe)),l(H,_e)},$$slots:{default:!0}});var aa=n(Vt,2);Ts(aa,{get decisions(){return e(x)},nameOf:ht,onchoose:dt});var ra=n(aa,2);Ks(ra,{get imports(){return e(T)},get state(){return e(g)}}),l(a,rt),We()}ct(["change","click","input"]);function Cl(a){switch(a){case"ok":return"OK";case"warn":return"À SURVEILLER";case"fault":return"EN PANNE";case"off":return"SANS OBJET";default:return"INCONNU"}}var Pl=c('

          '),Tl=c('

          ');function zl(a,t){Me(t,!0);var r=Tl(),i=s(r),d=s(i),p=n(d,2),k=s(p),T=n(p,2),m=s(T),j=n(i,2),q=s(j),D=n(j,2);{var L=W=>{var z=Pl(),R=s(z);f(()=>o(R,t.light.remedy)),l(W,z)};_(D,W=>{t.light.remedy!==""&&W(L)})}f(W=>{he(r,"data-light",t.light.id),he(r,"data-level",t.light.level),he(d,"data-level",t.light.level),o(k,t.light.label),o(m,W),o(q,t.light.value)},[()=>Cl(t.light.level)]),l(a,r),We()}function jl(a){return[Rl(a),Ol(a),Nl(a),Il(a),Fl(a),Ul(a)]}function Rl(a){const t=a.state.scale,r=ua(t.median_ms);return a.scale_present?t.connected?t.too_slow?{id:"scale",label:"Balance",level:"warn",value:`une mesure toutes les ${r}, plus lent que la péremption`,remedy:"À cette cadence, un poids serait déclaré périmé avant l’arrivée de la mesure suivante. Vérifiez le câble et l’adaptateur USB, puis la cadence sur la page Matériel."}:{id:"scale",label:"Balance",level:"ok",value:Ml(t.provisional,`une mesure toutes les ${r}`),remedy:""}:{id:"scale",label:"Balance",level:"fault",value:"elle ne répond plus",remedy:"Vérifiez le câble et l’alimentation de la balance, puis touchez « Tester la balance ». En attendant, « Basculer en saisie manuelle » permet de continuer à servir."}:{id:"scale",label:"Balance",level:"off",value:"ce poste est déclaré sans balance",remedy:""}}function Ol(a){const t=a.state.printer;switch(t.health){case"faulted":return{id:"printer",label:"Imprimante",level:"fault",value:t.detail===""?"elle ne peut pas imprimer":t.detail,remedy:Al(a)};case"consumable":return{id:"printer",label:"Imprimante",level:"warn",value:"elle imprime, mais le rouleau arrive en fin de vie",remedy:"Changez le rouleau quand vous passez derrière le comptoir, puis touchez « J’ai changé le rouleau ». Le poste continue de servir en attendant."};case"ready":return{id:"printer",label:"Imprimante",level:"ok",value:"elle répond et n’a rien à signaler",remedy:""};default:return{id:"printer",label:"Imprimante",level:"unknown",value:"elle prend les étiquettes et ne dit rien en retour",remedy:"C’est la réponse normale d’une file Windows en RAW ou d’un fichier de périphérique, pas une panne. Pour savoir si elle imprime, touchez « Imprimer une étiquette de test »."}}}function Al(a){const t="Regardez le capot, le rouleau et le câble, puis touchez « Tester l’imprimante ».";return a.printing?.fallback_available===!0?t+" Le poste peut servir en attendant : « Imprimer sur l’imprimante du poste voisin ».":t}function Nl(a){const t=a.roll;return t===null?{id:"roll",label:"Rouleau",level:"unknown",value:"aucun compteur d’étiquettes sur ce poste",remedy:`Sans imprimante construite, il n’y a pas de rouleau à compter : vérifiez le « ${Ct("printer.type")} » et ses réglages sur la page Matériel.`}:t.known?t.level==="warn"?{id:"roll",label:"Rouleau",level:"warn",value:t.message,remedy:"Changez le rouleau, puis touchez « J’ai changé le rouleau »."}:{id:"roll",label:"Rouleau",level:"ok",value:t.message,remedy:""}:{id:"roll",label:"Rouleau",level:"unknown",value:t.message,remedy:"Touchez « J’ai changé le rouleau » en mettant un rouleau neuf : c’est le seul geste qui dise quelque chose de vrai du papier."}}function Il(a){const t=a.catalog_source?.label??"";if(a.state.catalog_count===0)return{id:"catalog",label:"Catalogue",level:"fault",value:"aucun produit dans la grille",remedy:Dl(t)};const r=a.catalog;return r!==null&&r.result!=="applied"&&r.result!=="unchanged"?{id:"catalog",label:"Catalogue",level:"warn",value:`dernier fichier refusé : ${r.reason===""?r.result:r.reason}`,remedy:"La grille tourne toujours sur le catalogue précédent. Corrigez le fichier dans Odoo, ou touchez « Oublier la quarantaine » puis redéposez-le."}:{id:"catalog",label:"Catalogue",level:"ok",value:`${ne(a.state.catalog_count)} produits dans la grille`,remedy:""}}function Dl(a){return"Déposez le fichier du catalogue là où le poste le guette, ou utilisez « Importer un catalogue » ci-contre pour le glisser depuis une clé USB."+(a===""?"":` Ce poste surveille : ${a}.`)}function Fl(a){const t=a.disk;if(t===null)return{id:"disk",label:"Disque",level:"unknown",value:"la place libre n’a pas pu être mesurée",remedy:"Téléchargez le fichier de diagnostic : il porte ce que le poste a pu lire du volume, et c’est la pièce qu’un support demandera."};const r=Sa(t.free_bytes),i=`seuil ${ne(t.alert_mb)} Mo`;return t.free_bytes===0?{id:"disk",label:"Disque",level:"fault",value:`plus un octet libre sur ${t.path}`,remedy:"Le journal ne peut plus rien écrire, alors que les étiquettes continuent de sortir. Faites de la place sur le disque, puis rechargez cette page."}:t.alert_mb>0&&t.free_bytes0?{id:"journal",label:"Journal",level:"fault",value:`${ne(t)} pesées imprimées mais non enregistrées`,remedy:"Les étiquettes sortent, les ventes sont bonnes, c’est la trace qui manque. Téléchargez le fichier de diagnostic et prévenez le support : ne redémarrez rien."}:a.counters.journal_rows_count<0?{id:"journal",label:"Journal",level:"unknown",value:"ce poste n’a pas de journal ouvert",remedy:"Le poste pèse et imprime quand même. Téléchargez le fichier de diagnostic : il dit pourquoi la base n’a pas pu être ouverte."}:{id:"journal",label:"Journal",level:"ok",value:`${ne(a.counters.journal_rows_count)} pesées enregistrées`,remedy:""}}function Ml(a,t){return a?t+" (cadence encore provisoire)":t}function Wl(a){return a===null||!a.known?"unknown":a.configured?"ok":"missing"}var Bl=c("Une mesure toutes les ",1),Vl=c('

          '),Gl=c("Source : ",1),Hl=c(`

          Aucun import enregistré sur ce poste : le catalogue n’est jamais arrivé, ou le - journal ne le porte pas.

          `),Jl=c('

          ',1),Kl=c('

          ',1),$l=c('

          Aucune décision locale : le catalogue est proposé tel qu’il arrive.

          '),Yl=c('

          '),Xl=c(' '),Ql=c('
        • '),Zl=c('
            ',1),ei=c(`INCONNU — la question n’a pas pu être posée à ce système. Ce - n’est pas « non configuré » : personne ne sait encore.`,1),ti=c('OK ',1),ai=c('NON CONFIGURÉ ',1),ri=c('

            '),ni=c('

            ',1),si=c('

            Rien à signaler depuis le démarrage du poste.

            '),li=c(' '),ii=c('
          • '),oi=c('
              '),ui=c(''),ci=c('

              '),di=c('
              Numéro de poste
              Nom
              Coopérative
              Version
              Empreinte de configuration
              ',1),vi=c('
              ');function pi(a,t){Me(t,!0);const r=u(()=>t.health.new_version),i=u(()=>jl(t.health)),d=u(()=>t.health.state.scale),p=u(()=>t.health.unattended_restart),k=u(()=>t.health.decisions),T=5,m=u(()=>e(k).slice(0,T));var j=vi(),q=s(j);Te(q,21,()=>e(i),F=>F.id,(F,E)=>{zl(F,{get light(){return e(E)}})});var D=n(q,2);ze(D,{title:"Cadence de la balance",children:(F,E)=>{var w=Vl(),g=s(w);{var U=C=>{var y=ot("Ce poste est déclaré sans balance : le poids est saisi à la main.");l(C,y)},B=C=>{var y=ot(`La balance ne répond pas : aucune cadence n’est mesurable tant qu’aucune trame - n’arrive.`);l(C,y)},$=C=>{var y=ot(`Aucun intervalle n’a encore été mesuré : la cadence apparaîtra dès les premières - trames.`);l(C,y)},J=C=>{var y=Bl(),S=n(te(y)),O=s(S),A=n(S),h=n(A);{var V=M=>{var b=ot("C’est encore une valeur d’attente : moins de huit intervalles ont été observés.");l(M,b)};_(h,M=>{e(d).provisional&&M(V)})}var X=n(h,2);{var ae=M=>{var b=ot("À cette cadence, un poids serait périmé avant l’arrivée de la mesure suivante.");l(M,b)};_(X,M=>{e(d).too_slow&&M(ae)})}f((M,b)=>{o(O,M),o(A,`, médiane - observée sur ${b??""} intervalles. `)},[()=>ua(e(d).median_ms),()=>ne(e(d).observations_count)]),l(C,y)};_(g,C=>{t.health.scale_present?e(d).connected?e(d).observations_count===0?C($,2):C(J,-1):C(B,1):C(U)})}l(F,w)},$$slots:{default:!0}});var L=n(D,2);ze(L,{title:"Catalogue",children:(F,E)=>{var w=Kl(),g=te(w),U=s(g);{var B=S=>{var O=ot("Aucune source de catalogue n’est publiée par ce poste.");l(S,O)},$=S=>{var O=Gl(),A=n(te(O)),h=s(A);f(()=>o(h,t.health.catalog_source.label)),l(S,O)};_(U,S=>{t.health.catalog_source===null?S(B):S($,-1)})}var J=n(g,2);{var C=S=>{var O=Hl();l(S,O)},y=S=>{var O=Jl(),A=te(O),h=s(A),V=n(A,2);yr(V,{get record(){return t.health.catalog},get motives(){return t.health.catalog_motives},get onshowrows(){return t.onshowrows}}),f((X,ae)=>o(h,`Dernier essai : ${X??""} — - ${ae??""}${t.health.catalog.file_name===""?"":" ("+t.health.catalog.file_name+")"}${t.health.catalog.reason===""?"":" : "+t.health.catalog.reason}`),[()=>Pt(t.health.catalog.occurred_at),()=>ha(t.health.catalog.result)]),l(S,O)};_(J,S=>{t.health.catalog===null?S(C):S(y,-1)})}l(F,w)},$$slots:{default:!0}});var W=n(L,2);ze(W,{title:"Décisions locales en vigueur",note:"Ce qu’un humain a décidé de ce catalogue, avec son motif et sa date.",children:(F,E)=>{var w=Rt(),g=te(w);{var U=$=>{var J=$l();l($,J)},B=$=>{var J=Zl(),C=te(J);{var y=O=>{var A=Yl(),h=s(A);f(V=>o(h,`${V??""} décisions en vigueur — les - 5 plus récentes ci-dessous. La liste entière est sur la page - Catalogue.`),[()=>ne(e(k).length)]),l(O,A)};_(C,O=>{e(k).length>T&&O(y)})}var S=n(C,2);Te(S,21,()=>e(m),O=>O.product_id,(O,A)=>{var h=Ql(),V=s(h),X=s(V),ae=n(V,2);{var M=fe=>{var pe=Xl(),ce=s(pe);f(N=>o(ce,`peut peser à partir de ${N??""} g`),[()=>ne(e(A).min_weight_g)]),l(fe,pe)};_(ae,fe=>{e(A).min_weight_g!==null&&fe(M)})}var b=n(ae,2),I=s(b),Y=n(b,2),ue=s(Y);f(fe=>{o(X,`${e(A).offered?"Dérogation de poids":"Produit retiré"} — ${e(A).product_id??""}`),o(I,e(A).reason),o(ue,`${fe??""}, ${e(A).decided_by??""}`)},[()=>va(e(A).decided_at)]),l(O,h)}),l($,J)};_(g,$=>{e(k).length===0?$(U):$(B,-1)})}l(F,w)},$$slots:{default:!0}});var z=n(W,2);ze(z,{title:"Redémarrage sans intervention",children:(F,E)=>{var w=ni(),g=te(w),U=s(g);{var B=S=>{var O=ei();l(S,O)},$=S=>{var O=ti(),A=n(te(O));f(()=>o(A,` — après une coupure de courant, ce poste revient seul sur - l’écran client. ${e(p).detail??""}`)),l(S,O)},J=S=>{var O=ai(),A=n(te(O));f(()=>o(A,` — ${e(p).detail??""}`)),l(S,O)};_(U,S=>{e(p)===null||!e(p).known?S(B):e(p).configured?S($,1):S(J,-1)})}var C=n(g,2);{var y=S=>{var O=ri(),A=s(O);f(()=>o(A,e(p).remedy)),l(S,O)};_(C,S=>{e(p)!==null&&e(p).known&&!e(p).configured&&e(p).remedy!==""&&S(y)})}f(S=>he(g,"data-verdict",S),[()=>Wl(e(p))]),l(F,w)},$$slots:{default:!0}});var R=n(z,2);ze(R,{title:"Dix derniers événements",children:(F,E)=>{var w=Rt(),g=te(w);{var U=$=>{var J=si();l($,J)},B=$=>{var J=oi();Te(J,21,()=>t.health.events,C=>C.id,(C,y)=>{var S=ii(),O=s(S),A=s(O),h=n(O,2),V=s(h),X=n(h,2);{var ae=I=>{var Y=li(),ue=s(Y);f(()=>o(ue,e(y).code)),l(I,Y)};_(X,I=>{e(y).code!==""&&jt.showTechnicalNames&&I(ae)})}var M=n(X,2),b=s(M);f((I,Y)=>{he(S,"data-level",e(y).level),o(A,I),o(V,Y),o(b,e(y).message)},[()=>ws(e(y).occurred_at),()=>gr(e(y).source)]),l(C,S)}),l($,J)};_(g,$=>{t.health.events.length===0?$(U):$(B,-1)})}l(F,w)},$$slots:{default:!0}});var K=n(R,2);ze(K,{title:"Ce poste",children:(F,E)=>{var w=di(),g=te(w),U=n(s(g),2),B=s(U),$=n(U,4),J=s($),C=n($,4),y=s(C),S=n(C,4),O=s(S),A=n(S,4),h=s(A),V=n(g,2);{var X=ae=>{var M=ci(),b=s(M),I=n(b);{var Y=ue=>{var fe=ui();Ce("click",fe,function(...pe){t.onshowupdate?.apply(this,pe)}),l(ue,fe)};_(I,ue=>{t.onshowupdate!==void 0&&ue(Y)})}f(()=>{he(M,"data-update-available",e(r)),o(b,`Version ${e(r)??""} disponible. `)}),l(ae,M)};_(V,ae=>{e(r)!==""&&ae(X)})}f(()=>{o(B,t.health.station),o(J,t.health.station_name===""?"non renseigné":t.health.station_name),o(y,t.health.coop),o(O,t.health.version),o(h,t.health.config_fingerprint)}),l(F,w)},$$slots:{default:!0}}),l(a,j),We()}ct(["click"]);var fi=c('

              ');function Ka(a,t){Me(t,!0);var r=fi(),i=s(r),d=s(i),p=n(i,2),k=n(s(p),1,!0);f(()=>{o(d,t.title),he(p,"data-standing",t.name),he(p,"data-level",t.standing.level),o(k,t.standing.word)}),l(a,r),We()}const gi={0:"NUL",2:"STX",3:"ETX",4:"EOT",5:"ENQ",6:"ACK",9:"TAB",10:"LF",13:"CR",21:"NAK",27:"ESC",127:"DEL"};function mi(a){return[...new TextEncoder().encode(a)].map(t=>t.toString(16).toUpperCase().padStart(2,"0")).join(" ")}function hi(a){let t="";for(const r of a){const i=r.codePointAt(0)??0;if(i>=32&&i!==127){t+=r;continue}t+=`⟨${gi[i]??i.toString(16).toUpperCase().padStart(2,"0")}⟩`}return t}function _i(a){const t=bi(a);return t===""?$a(a):`${$a(a)} ${t}`}function $a(a){return a.configRead?a.declaredWithoutScale?"Ce poste est déclaré sans balance : aucun port n’est écouté.":a.port===""?"Aucun port n’est indiqué : choisissez-en un dans la liste ci-dessus pour écouter les trames.":a.listed?a.portKnown?a.halt!==""?`L’écoute de ${a.port} est arrêtée.`:a.acting!==""?`L’écoute de ${a.port} est suspendue le temps de l’acte en cours.`:`Écoute de ${a.port}.`:`${a.port} n’est pas visible depuis ce poste : rien n’est écouté en continu.`:a.acting==="ports"?`Énumération des ports en cours : l’écoute de ${a.port} démarre dès qu’il est vu.`:`Les ports de ce poste n’ont pas été énumérés : « Lister les ports » dira si ${a.port} existe.`:"Lecture de la configuration en cours : le port à écouter n’est pas encore connu."}function bi(a){return a.framesShown===0?"Aucune trame reçue pour l’instant.":a.framesShown===1?`Une seule trame reçue — ${ne(a.framesKept)} au plus sont gardées.`:`Les ${ne(a.framesShown)} dernières trames — ${ne(a.framesKept)} au plus, la plus récente en bas.`}function wi(a){return!a.configRead||a.declaredWithoutScale||a.port===""?"":a.halt!==""?"Reprendre l’écoute":a.listed&&!a.portKnown?"Écouter ce port une fois":""}function yi(a){return a.acting!=="detect"?"Détecter automatiquement":a.listening?"Détection : le port se libère…":a.toScan===0?"Détection : énumération des ports…":`Détection : port ${ne(a.scanned)} sur ${ne(a.toScan)}…`}function xi(a,t){return a?t.connected?t.too_slow?{level:"warn",word:"Trop lente",detail:Ya(t)+" À cette cadence, un poids serait déclaré périmé avant l’arrivée de la mesure suivante."}:{level:"ok",word:"Connectée",detail:"Elle répond. "+Ya(t)}:{level:"fault",word:"Sans réponse",detail:"Elle ne répond plus. Vérifiez le câble et l’alimentation, puis « Tester la balance » sur la page Dépannage."}:{level:"off",word:"Sans balance",detail:"Ce poste est déclaré sans balance : le feu est éteint et le poids se saisit à la main."}}function Ya(a){return a.observations_count===0?"Aucun intervalle n’a encore été mesuré : la cadence sera connue dès les premières trames.":`Une mesure toutes les ${ua(a.median_ms)} sur ${ne(a.observations_count)} intervalles`+(a.provisional?", cadence encore provisoire.":".")}const ki={ready:{level:"ok",word:"Prête",detail:"Elle répond et n’a rien à signaler."},consumable:{level:"warn",word:"Rouleau en fin de vie",detail:"Elle imprime, mais le rouleau arrive en fin de vie."},faulted:{level:"fault",word:"En panne",detail:"Elle ne peut pas imprimer."},unknown:{level:"unknown",word:"Silencieuse",detail:"Elle prend les étiquettes et ne dit rien en retour : c’est la réponse normale d’une file Windows en RAW ou d’un fichier de périphérique, pas une panne."}};function qi(a){const t=ki[a.health]??{level:"unknown",word:"État inconnu",detail:"Le poste a répondu un état que cet écran ne sait pas nommer."};return{...t,detail:a.detail===""?t.detail:a.detail}}function Si(a){const t=a.observed_at===""?"Jamais observée depuis le démarrage":`Observée le ${Pt(a.observed_at)}`,r=a.pending_jobs_count;return`${t}, ${ne(r)} ${r>1?"travaux":"travail"} en attente.`}const Li=["queue","path","address"],Ei="queue",Ci={queue:"Choisissez-la dans la liste ci-dessus : une file mal orthographiée ne s’imprime pas.",path:"Le nœud d’impression de ce poste, /dev/usb/lp0 ou le lien que la règle udev lui donne.",address:"L’adresse de l’imprimante sur le réseau, 192.168.0.43 — le port 9100 est ajouté s’il manque."};function Pi(a,t){return t!==""&&!a.some(r=>r.id===t)}function Ti(a,t){return a.length===0?[]:[...Pi(a,t)?[{value:t,label:`${t} — inconnu de ce poste`}]:[],...a.map(r=>({value:r.id,label:r.label}))]}function zi(a,t){return a.find(r=>r.id===t)?.key??Ei}function ji(a,t,r){const i=[...new Set(a.filter(k=>k.key!==r).map(k=>Ri(t,k.key)))].filter(k=>k!==""),d=a.filter(k=>k.key!==r).length,p=`${ne(d)} ${d>1?"destinations ne sont pas proposées":"destination n’est pas proposée"}`;return i.length===0?`${p} : aucun transport de ce poste ne les lit.`:`${p} : choisissez « ${i.join(" » ou « ")} » pour les voir.`}function Ri(a,t){return a.find(r=>r.key===t)?.label??""}var Oi=c(''),Ai=c(`

              Lecture de la configuration en cours… tant qu’elle n’est pas arrivée, cette page ne - déclare rien de ce poste.

              `),Xa=c('
            • '),Ni=c('
                '),Ii=c('

                ',1),Di=c('
              • '),Fi=c('

                  ',1),Ui=c('

                  '),Mi=c('
                • '),Wi=c('
                    '),Bi=c('

                    '),Vi=c('

                      ',1),Gi=c('

                      '),Hi=c('

                      L’état et la cadence viennent de ce que le poste observe vraiment, jamais d’un réglage.

                      Réglages série de la balance

                      Réglages de l’imprimante
                      ');function Ji(a,t){Me(t,!0);const r=Be(t,"admin",7),i=20,d=3,p=250,k=50,T=d*1e3+p+1e3,m=12;let j=G(ft([])),q=G(ft([])),D=G(ft([])),L=G(ft([])),W=G(""),z=G(!1),R=G(""),K=G(0),F=G(0),E=G(!1),w=G(null),g=G(!1),U=G(!1),B=!1;const $=u(()=>t.health.state.scale),J=u(()=>t.health.state.printer),C=u(()=>t.draft.config!==null),y=u(()=>e(C)&&!t.draft.flag("scale.present")),S=u(()=>t.draft.text("scale.options.port")),O=u(()=>e(w)!==null&&e(w).port===e(S)?e(w).reason:""),A=u(()=>e(W)===e(S)?e(L):[]),h=u(()=>t.health.printer_transports),V=u(()=>t.draft.text("printer.options.transport")),X=u(()=>Ti(e(h),e(V))),ae=u(()=>zi(e(h),e(V))),M=u(()=>"printer.options."+e(ae)),b=u(()=>e(q).filter(P=>P.key===e(ae))),I=u(()=>e(q).length-e(b).length),Y=u(()=>xi(t.health.scale_present,e($))),ue=u(()=>qi(e(J))),fe=u(()=>({configRead:e(C),declaredWithoutScale:e(y),port:e(S),listed:e(z),portKnown:ie(e(S)),halt:e(O),acting:e(R),framesShown:e(A).length,framesKept:i})),pe=u(()=>_i(e(fe))),ce=u(()=>wi(e(fe))),N=u(()=>yi({acting:e(R),listening:e(E),scanned:e(K),toScan:e(F)})),Q=u(()=>Ye(t.draft,"scale.type")!==""||Ye(t.draft,"scale.options.port")!==""),re=u(()=>Ye(t.draft,"printer.type")!==""||Ye(t.draft,"printer.options.transport")!==""||Li.some(P=>Ye(t.draft,"printer.options."+P)!==""));qt(()=>{e(Q)&&v(g,!0)}),qt(()=>{e(re)&&v(U,!0)}),$r(()=>{B=!0}),Ta(()=>{oe("ports",le)}),qt(()=>{e(E)||!Z(e(S))||we(e(S))});function Z(P){return!B&&e(C)&&!e(y)&&P!==""&&P===e(S)&&ie(P)&&e(O)===""&&e(R)===""}function ie(P){return e(j).some(ee=>ee.name===P)}async function we(P){v(E,!0);try{for(;Z(P);){try{x(P,await Ua(P,d))}catch(ee){v(w,{port:P,reason:Re(ee)},!0);return}await se()}}finally{v(E,!1)}}async function Fe(){const P=e(S),ee=await r().protect(()=>Ua(P,d));ee!==null&&(x(P,ee),v(w,null))}function x(P,ee){if(B||ee.length===0)return;const be=P===e(W)?e(L):[];v(W,P,!0),v(L,[...be,...ee].slice(-i),!0)}function se(P=p){return new Promise(ee=>setTimeout(ee,P))}async function ve(){const P=Date.now()+T;for(;e(E)&&Date.now()pr(P));ee!==null&&(r().notice=ee.message,await r().refresh())}async function Ne(){v(K,0),v(F,0),e(z)||await le();const P=e(j);if(P.length===0){v(D,[],!0);return}await r().protect(()=>Ve(P))}async function Ve(P){v(D,[],!0),v(F,P.length,!0);for(const[ee,be]of P.entries()){v(K,ee+1);try{const de=await xn(be.name);v(D,[...e(D),{port:be.name,message:de.message,refused:!1}],!0)}catch(de){if(fa(de))throw de;v(D,[...e(D),{port:be.name,message:Re(de),refused:!0}],!0)}}}function ye(P,ee){const be=Qt(t.draft,P);return be.length>0?be:ee.slice(0,m)}function Re(P){return P instanceof zt?P.message:P instanceof Error?"Le poste n’a pas répondu : "+P.message:"Le poste n’a pas répondu."}const lt=[{what:"label",name:"étiquette"},{what:"alignment",name:"alignement"},{what:"ruler",name:"réglette"}],it=u(()=>lt.filter(P=>t.health.printer_self_tests.includes(P.what)));var gt=Hi(),ht=s(gt),dt=s(ht);Ka(dt,{title:"Balance",name:"scale",get standing(){return e(Y)}});var mt=n(dt,2),yt=s(mt),Ze=n(mt,4);{var me=P=>{var ee=Oi(),be=s(ee);f(()=>ta(be,e(y))),Ce("change",be,de=>t.draft.set("scale.present",!de.currentTarget.checked)),l(P,ee)},Le=P=>{var ee=Ai();l(P,ee)};_(Ze,P=>{e(C)?P(me):P(Le,-1)})}var rt=n(Ze,2),Ft=s(rt);{let P=u(()=>e(R)==="ports"),ee=u(()=>e(R)!=="");Pe(Ft,{label:"Lister les ports",get busy(){return e(P)},get disabled(){return e(ee)},onrun:()=>{oe("ports",le)}})}var Ot=n(Ft,2);{let P=u(()=>e(R)!=="");Pe(Ot,{act:"detect",get label(){return e(N)},protected:!0,get disabled(){return e(P)},onrun:()=>{oe("detect",Ne)}})}var Tt=n(rt,2);{var At=P=>{var ee=Ii(),be=te(ee),de=s(be),Ae=n(be,2);{var De=Qe=>{var kt=Ni();Te(kt,21,()=>e(j).slice(0,m),Et=>Et.name,(Et,bt)=>{var Gt=Xa(),Yt=s(Gt),_a=s(Yt),Er=n(Yt,2),Cr=s(Er);f(()=>{o(_a,e(bt).name),o(Cr,`${(e(bt).description===""?"aucune description USB":e(bt).description)??""} - ${e(bt).vid===""?"":` — VID ${e(bt).vid} PID ${e(bt).pid}`}`)}),Ce("click",Yt,()=>t.draft.set("scale.options.port",e(bt).name)),l(Et,Gt)}),l(Qe,kt)};_(Ae,Qe=>{e(j).length>0&&Qe(De)})}f(Qe=>o(de,Qe),[()=>e(j).length===0?"Aucun port série n’est visible depuis ce poste.":ya("port détecté","ports détectés",e(j).length,m)]),l(P,ee)};_(Tt,P=>{e(z)&&P(At)})}var Nt=n(Tt,2);{var Ut=P=>{var ee=Fi(),be=te(ee),de=s(be),Ae=n(be,2);Te(Ae,21,()=>e(D).slice(0,m),De=>De.port,(De,Qe)=>{var kt=Di();let Et;var bt=s(kt),Gt=s(bt),Yt=n(bt,2),_a=s(Yt);f(()=>{Et=Mt(kt,1,"svelte-1p4i0xm",null,Et,{refused:e(Qe).refused}),o(Gt,e(Qe).port),o(_a,e(Qe).message)}),l(De,kt)}),f(De=>o(de,De),[()=>ya("port interrogé","ports interrogés",e(D).length,m)]),l(P,ee)};_(Nt,P=>{e(D).length>0&&P(Ut)})}var Bt=n(Nt,2),$t=n(s(Bt),2),Vt=s($t);{let P=u(()=>t.draft.text("scale.type")),ee=u(()=>Ye(t.draft,"scale.type")),be=u(()=>Qt(t.draft,"scale.type")),de=u(()=>!e(C));at(Vt,{label:"Protocole",path:"scale.type",get value(){return e(P)},hint:"Les valeurs acceptées apparaissent ici si l’enregistrement est refusé.",get fault(){return e(ee)},get allowed(){return e(be)},get disabled(){return e(de)},onchange:Ae=>t.draft.set("scale.type",Ae)})}var aa=n(Vt,2);{let P=u(()=>t.draft.text("scale.options.port")),ee=u(()=>Ye(t.draft,"scale.options.port")),be=u(()=>ye("scale.options.port",e(j).map(Ae=>Ae.name))),de=u(()=>!e(C));at(aa,{label:"Port série",path:"scale.options.port",get value(){return e(P)},hint:"Choisissez-le dans la liste détectée ci-dessus plutôt que de le taper : l’écoute permanente ne suit que des ports détectés.",get fault(){return e(ee)},get allowed(){return e(be)},get disabled(){return e(de)},onchange:Ae=>t.draft.set("scale.options.port",Ae)})}var ra=n(Bt,2),H=s(ra),ge=s(H),_e=n(H,2);{var ke=P=>{var ee=Ui(),be=s(ee),de=n(be);{var Ae=De=>{{let Qe=u(()=>e(R)==="listen"),kt=u(()=>e(R)!=="");Pe(De,{act:"listen",get label(){return e(ce)},protected:!0,get busy(){return e(Qe)},get disabled(){return e(kt)},onrun:()=>{oe("listen",Fe)}})}};_(de,De=>{e(ce)!==""&&De(Ae)})}f(()=>o(be,`${e(O)??""} `)),l(P,ee)};_(_e,P=>{(e(O)!==""||e(ce)!=="")&&P(ke)})}var Ke=n(_e,2);{var Je=P=>{var ee=Wi();Te(ee,21,()=>e(A),da,(be,de)=>{var Ae=Mi(),De=s(Ae),Qe=s(De),kt=n(De,2),Et=s(kt);f((bt,Gt)=>{o(Qe,bt),o(Et,Gt)},[()=>mi(e(de)),()=>hi(e(de))]),l(be,Ae)}),l(P,ee)};_(Ke,P=>{e(A).length>0&&P(Je)})}var et=n(ht,2),Ie=s(et);Ka(Ie,{title:"Imprimante",name:"printer",get standing(){return e(ue)}});var Oe=n(Ie,2),$e=s(Oe),Ge=n(Oe,2),tt=s(Ge),ut=n(Ge,2),xe=s(ut);{let P=u(()=>e(R)==="printers"),ee=u(()=>e(R)!=="");Pe(xe,{label:"Lister les files",get busy(){return e(P)},get disabled(){return e(ee)},onrun:()=>{oe("printers",je)}})}var Xe=n(xe,2);{let P=u(()=>e(R)==="discover"),ee=u(()=>e(R)!=="");Pe(Xe,{label:"Rechercher l’imprimante",protected:!0,get busy(){return e(P)},get disabled(){return e(ee)},onrun:()=>{oe("discover",qe)}})}var vt=n(Xe,2);Te(vt,17,()=>e(it),P=>P.what,(P,ee)=>{{let be=u(()=>`Auto-test : ${e(ee).name}${e(R)===e(ee).what?" — en cours…":""}`),de=u(()=>e(R)!=="");Pe(P,{get act(){return e(ee).what},get label(){return e(be)},protected:!0,get disabled(){return e(de)},onrun:()=>{oe(e(ee).what,()=>Se(e(ee).what))}})}});var St=n(ut,2);{var nt=P=>{var ee=Bi(),be=s(ee);f(()=>o(be,e(it).length===0?"Le driver d’impression en service n’imprime aucun auto-test.":"Les autres auto-tests ne sont pas proposés : le driver d’impression en service ne les imprime pas.")),l(P,ee)};_(St,P=>{e(it).length{var ee=Vi(),be=te(ee),de=s(be),Ae=n(be,2);Te(Ae,21,()=>e(b).slice(0,m),De=>De.name,(De,Qe)=>{var kt=Xa(),Et=s(kt),bt=s(Et),Gt=n(Et,2),Yt=s(Gt);f(()=>{o(bt,e(Qe).name),o(Yt,`${e(Qe).detail??""}${e(Qe).default?" — file par défaut du système":""}`)}),Ce("click",Et,()=>t.draft.set(e(M),e(Qe).name)),l(De,kt)}),f(De=>o(de,De),[()=>ya("destination","destinations",e(b).length,m)]),l(P,ee)};_(pt,P=>{e(b).length>0&&P(xt)})}var Lt=n(pt,2);{var It=P=>{var ee=Gi(),be=s(ee);f(de=>o(be,de),[()=>ji(e(q),e(h),e(ae))]),l(P,ee)};_(Lt,P=>{e(I)>0&&P(It)})}var Dt=n(Lt,2),Ee=n(s(Dt),2),Ue=s(Ee);{let P=u(()=>t.draft.text("printer.type")),ee=u(()=>Ye(t.draft,"printer.type")),be=u(()=>Qt(t.draft,"printer.type")),de=u(()=>!e(C));at(Ue,{label:"Driver",path:"printer.type",get value(){return e(P)},hint:"Gardez le driver raster : c’est celui que les postes en service utilisent.",get fault(){return e(ee)},get allowed(){return e(be)},get disabled(){return e(de)},onchange:Ae=>t.draft.set("printer.type",Ae)})}var st=n(Ue,2);{let P=u(()=>Ye(t.draft,"printer.options.transport")),ee=u(()=>Qt(t.draft,"printer.options.transport")),be=u(()=>!e(C));at(st,{label:"Transport",path:"printer.options.transport",get value(){return e(V)},hint:"Local par défaut : une file Windows ou un nœud d’impression de ce poste.",get fault(){return e(P)},get allowed(){return e(ee)},get choices(){return e(X)},get disabled(){return e(be)},onchange:de=>t.draft.set("printer.options.transport",de)})}var _t=n(st,2);{let P=u(()=>Ct(e(M))),ee=u(()=>t.draft.text(e(M))),be=u(()=>Ci[e(ae)]??""),de=u(()=>Ye(t.draft,e(M))),Ae=u(()=>ye(e(M),e(b).map(Qe=>Qe.name))),De=u(()=>!e(C));at(_t,{get label(){return e(P)},get path(){return e(M)},get value(){return e(ee)},get hint(){return e(be)},get fault(){return e(de)},get allowed(){return e(Ae)},get disabled(){return e(De)},onchange:Qe=>t.draft.set(e(M),Qe)})}f(P=>{o(yt,e(Y).detail),o(ge,e(pe)),o($e,e(ue).detail),o(tt,P)},[()=>Si(e(J))]),Fa("open","toggle",Bt,P=>v(g,P),()=>e(g)),Fa("open","toggle",Dt,P=>v(U,P),()=>e(U)),l(a,gt),We()}ct(["change","click"]);const Qa={sent:"envoyée à l’imprimante",rejected:"refusée",failed:"en échec",reprint:"réimpression"},Za=[{value:"",label:"toutes"},{value:"sent",label:"envoyées à l’imprimante"},{value:"rejected",label:"refusées"},{value:"failed",label:"en échec"},{value:"reprint",label:"réimpressions"}],Ki={scale:"balance",manual:"saisie manuelle",replay:"trame rejouée"},$i={stable:"stable",unstable:"instable",unknown:"non déclarée par la balance",not_applicable:"sans objet — saisie manuelle"},Yi={by_weight:"au poids",by_unit:"à l’unité"},Xi={debug:"mise au point",info:"information",warn:"avertissement",error:"erreur",critical:"critique"};function na(a,t,r){return a[t]??r}var Qi=c(' '),Zi=c(' '),eo=c('
                    • '),to=c('
                        ');function ao(a,t){Me(t,!0);var r=to(),i=s(r);Te(i,21,()=>t.lines,d=>d.id,(d,p)=>{var k=eo(),T=s(k),m=s(T),j=n(T,2),q=s(j),D=n(j,2),L=s(D),W=n(D,2);{var z=w=>{var g=Qi(),U=s(g);f(()=>o(U,e(p).code)),l(w,g)};_(W,w=>{e(p).code!==""&&jt.showTechnicalNames&&w(z)})}var R=n(W,2),K=s(R),F=n(R,2);{var E=w=>{var g=Zi(),U=s(g);f(()=>o(U,e(p).detail)),l(w,g)};_(F,w=>{e(p).detail!==""&&w(E)})}f((w,g,U)=>{he(k,"data-level",e(p).level),o(m,w),o(q,g),o(L,U),o(K,e(p).message)},[()=>Pt(e(p).occurred_at),()=>na(Xi,e(p).level,"niveau inconnu"),()=>gr(e(p).source)]),l(d,k)}),l(a,r),We()}var ro=c(""),no=c('L’export sera proposé quand le journal aura répondu.'),so=c('L’export n’est pas proposé : ce poste n’a pas répondu à la lecture du journal.'),lo=c('Exporter en CSV'),io=c('

                        '),oo=c('

                        '),uo=c(`

                        Aucune trame brute n’a été enregistrée pour cette pesée : il n’y a - rien à rejouer.

                        `),co=c(`

                        La trame repart dans le décodeur du poste EN SERVICE : le poids - affiché au client change, et rien ne le remet comme il était. C’est - ce qui fait d’un refus inexpliqué un test permanent, sans - déplacement au magasin et sans balance.

                        `,1),vo=c('

                        Produit
                        Référence
                        Vente
                        Brut / tare / net
                        Stabilité
                        Origine du poids
                        Résultat
                        Trame brute
                        '),po=c(' ',1),fo=c('

                        QuandProduitNetCode-barresRésultatDuréeDétail
                        ',1),go=c('
                        ',1),mo=c('

                        '),ho=c('

                        ',1),_o=c('
                        ');function bo(a,t){Me(t,!0);const r=Be(t,"admin",7),i=200,d=5e3,p=50,k=500,T=7;let m=G(ft([])),j=G(ft([])),q=G(""),D=G(null),L=G(!1),W=G(!1),z=G("loading"),R=G("loading"),K=G(""),F=G("");const E=u(()=>({limit:String(i),...e(q)===""?{}:{result:e(q)}})),w=u(()=>({...e(E),limit:String(d)})),g=u(()=>e(m).find(h=>h.id===e(D))??null),U=u(()=>Za.find(h=>h.value===e(q))?.label??""),B=u(()=>y(e(m).length,i,"pesée","pesées",`L’export CSV en emporte jusqu’à ${ne(d)}.`)),$=u(()=>y(e(j).length,p,"ligne","lignes",`Le fichier de diagnostic emporte les ${ne(k)} dernières.`));J();async function J(){v(D,null),v(W,!0),v(z,"loading"),v(R,"loading"),v(m,[],!0),v(j,[],!0),v(K,""),v(F,"");try{const h=await r().load(()=>qn(e(E)));v(z,h===null?"unread":"read",!0),v(K,h===null?r().actionError:"",!0),v(m,h??[],!0);const V=await r().load(()=>Ln({limit:String(p)}));v(R,V===null?"unread":"read",!0),v(F,V===null?r().actionError:"",!0),v(j,V??[],!0)}finally{v(W,!1),r().actionError=""}}async function C(h){r().notice="",r().actionError="",v(L,!0);try{const V=await r().protect(()=>Tn(h));V!==null&&(r().notice=V.message)}finally{v(L,!1)}}function y(h,V,X,ae,M){const b=h>1?ae:X;return h{var X=go(),ae=te(X),M=n(s(ae),2);Te(M,21,()=>Za,Z=>Z.value,(Z,ie)=>{var we=ro(),Fe=s(we),x={};f(()=>{o(Fe,e(ie).label),x!==(x=e(ie).value)&&(we.value=(we.__value=e(ie).value)??"")}),l(Z,we)});var b=n(M,2);Pe(b,{label:"Rafraîchir",get busy(){return e(W)},onrun:()=>{J()}});var I=n(b,2);{var Y=Z=>{var ie=no();l(Z,ie)},ue=Z=>{var ie=so();l(Z,ie)},fe=Z=>{var ie=lo();f(we=>he(ie,"href",we),[()=>Sn(e(w))]),l(Z,ie)};_(I,Z=>{e(z)==="loading"?Z(Y):e(z)==="unread"?Z(ue,1):Z(fe,-1)})}var pe=n(ae,2);{var ce=Z=>{var ie=io(),we=s(ie);f(Fe=>o(we,`L’export emporte le même filtre que le tableau, mais pas son plafond : il descend - jusqu’à ${Fe??""} pesées, en point-virgule et en UTF-8 — il s’ouvre - tel quel dans le tableur d’un Windows français. Il ne demande aucun mot de passe : la - lecture du journal n’en demande pas non plus, et le fichier de diagnostic emporte déjà - les deux cents dernières pesées.`),[()=>ne(d)]),l(Z,ie)};_(pe,Z=>{e(z)==="read"&&Z(ce)})}var N=n(pe,2);{var Q=Z=>{var ie=oo(),we=s(ie);{var Fe=oe=>{var le=ot("Lecture du journal…");l(oe,le)},x=oe=>{var le=ot();f(()=>o(le,`Le journal n’a pas pu être lu : ${e(K)??""} Ce n’est pas « aucune pesée ».`)),l(oe,le)},se=oe=>{var le=ot("Le journal ne contient aucune pesée.");l(oe,le)},ve=oe=>{var le=ot();f(()=>o(le,`Aucune pesée ne correspond au filtre « ${e(U)??""} ».`)),l(oe,le)};_(we,oe=>{e(z)==="loading"?oe(Fe):e(z)==="unread"?oe(x,1):e(q)===""?oe(se,2):oe(ve,-1)})}l(Z,ie)},re=Z=>{var ie=fo(),we=te(ie),Fe=s(we),x=n(we,2),se=s(x),ve=n(s(se));Te(ve,21,()=>e(m),oe=>oe.id,(oe,le)=>{var je=po(),qe=te(je);let Se;var Ne=s(qe),Ve=s(Ne),ye=n(Ne),Re=s(ye),lt=n(ye),it=s(lt),gt=n(lt),ht=s(gt),dt=n(gt),mt=s(dt),yt=n(dt),Ze=s(yt),me=n(yt),Le=s(me),rt=s(Le),Ft=n(qe,2);{var Ot=Tt=>{var At=vo(),Nt=s(At);he(Nt,"colspan",T);var Ut=s(Nt),Bt=s(Ut),$t=n(Ut,2),Vt=n(s($t),2),aa=s(Vt),ra=n(Vt,4),H=s(ra),ge=n(ra,4),_e=s(ge),ke=n(ge,4),Ke=s(ke),Je=n(ke,4),et=s(Je),Ie=n(Je,4),Oe=s(Ie),$e=n(Ie,4),Ge=s($e),tt=n($e,4),ut=s(tt),xe=s(ut),Xe=n($t,2);{var vt=nt=>{var pt=uo();l(nt,pt)},St=nt=>{var pt=co(),xt=te(pt),Lt=s(xt);Pe(Lt,{kind:"destructive",label:"Rejouer cette trame",protected:!0,get busy(){return e(L)},onrun:()=>{C(e(g).frame)}}),l(nt,pt)};_(Xe,nt=>{e(g).frame===""?nt(vt):nt(St,-1)})}f((nt,pt,xt,Lt,It,Dt,Ee,Ue,st)=>{he(At,"data-detail",e(le).id),o(Bt,`Pesée ${e(g).id??""}`),o(aa,`${e(g).product_name??""} (${e(g).product_id??""})`),o(H,e(g).reference),o(_e,`${nt??""} — - ${pt??""} - ${e(g).quantity>1?"unités":"unité"}`),o(Ke,`${xt??""} g / ${Lt??""} g / - ${It??""} g`),o(et,`${Dt??""} — cadence - médiane ${Ee??""}`),o(Oe,Ue),o(Ge,`${st??""}${e(g).detail===""?"":` — ${e(g).detail}`}`),o(xe,e(g).frame===""?"aucune trame enregistrée":e(g).frame)},[()=>na(Yi,e(g).mode,"mode de vente inconnu"),()=>ne(e(g).quantity),()=>ne(e(g).gross_g),()=>ne(e(g).tare_g),()=>ne(e(g).net_g),()=>na($i,e(g).stability,"stabilité inconnue"),()=>ua(e(g).rate_ms),()=>na(Ki,e(g).source,"origine inconnue"),()=>na(Qa,e(g).result,"résultat inconnu")]),l(Tt,At)};_(Ft,Tt=>{e(D)===e(le).id&&e(g)!==null&&Tt(Ot)})}f((Tt,At,Nt,Ut)=>{Se=Mt(qe,1,"svelte-86o8oz",null,Se,{open:e(D)===e(le).id}),o(Ve,Tt),o(Re,e(le).product_name),o(it,`${At??""} g`),o(ht,e(le).barcode),o(mt,Nt),o(Ze,Ut),he(Le,"aria-expanded",e(D)===e(le).id),o(rt,e(D)===e(le).id?"fermer":"détail")},[()=>Pt(e(le).occurred_at),()=>ne(e(le).net_g),()=>na(Qa,e(le).result,"résultat inconnu"),()=>ua(e(le).duration_ms)]),Ce("click",Le,()=>v(D,e(D)===e(le).id?null:e(le).id,!0)),l(oe,je)}),f(()=>o(Fe,e(B))),l(Z,ie)};_(N,Z=>{e(m).length===0?Z(Q):Z(re,-1)})}Ce("change",M,()=>{J()}),Kr(M,()=>e(q),Z=>v(q,Z)),l(h,X)},$$slots:{default:!0}});var A=n(O,2);ze(A,{title:"Journal technique",children:(h,V)=>{var X=Rt(),ae=te(X);{var M=I=>{var Y=mo(),ue=s(Y);{var fe=N=>{var Q=ot("Lecture du journal technique…");l(N,Q)},pe=N=>{var Q=ot();f(()=>o(Q,`Le journal technique n’a pas pu être lu : ${e(F)??""} Ce n’est pas « aucune - ligne ».`)),l(N,Q)},ce=N=>{var Q=ot("Aucune ligne technique.");l(N,Q)};_(ue,N=>{e(R)==="loading"?N(fe):e(R)==="unread"?N(pe,1):N(ce,-1)})}l(I,Y)},b=I=>{var Y=ho(),ue=te(Y),fe=s(ue),pe=n(ue,2);ao(pe,{get lines(){return e(j)}}),f(()=>o(fe,e($))),l(I,Y)};_(ae,I=>{e(j).length===0?I(M):I(b,-1)})}l(h,X)},$$slots:{default:!0}}),l(a,S),We()}ct(["change","click"]);const er="L’aperçu n’a pas pu être rendu par le poste.",wo="Le poste a rendu l’aperçu, mais le navigateur ne l’a pas affiché.";async function yo(a){try{const t=await fetch(a,{headers:{accept:"application/json"}});if(t.ok)return wo;const r=JSON.parse(await t.text());return typeof r.message=="string"&&r.message!==""?r.message:er}catch{return er}}function tr(a,t){let r=a;for(const i of t.split(".")){if(r===null||typeof r!="object")return 0;r=r[i]}return typeof r=="number"?r:0}function kr(a){const t=Number(a);return a.trim()===""||Number.isNaN(t)?null:t}function xo(a){const t=a.trim().replace(",",".");if(!/^\d{1,3}(\.\d)?$/u.test(t))return null;const r=Number(t);return r>100?null:r}function qr(a,t){!(a instanceof HTMLInputElement)||a.value===t||(a.value=t)}var ko=c('

                        '),qo=c('

                        '),So=c('

                        '),Lo=c('

                        '),Eo=c('

                        Lecture de la configuration en cours… les flèches attendent qu’elle soit arrivée.

                        '),Co=c(`
                        Aperçu de l’étiquette telle qu’elle serait imprimée

                        Décalage horizontal

                        Décalage vertical

                        Le décalage ne descend pas sous zéro : le poste refuse un décalage négatif quel que - soit le gabarit. Le maximum, lui, dépend de la géométrie du gabarit ; il est annoncé - ici si l’enregistrement le dépasse.

                        Le symbole code-barres est volontairement tronqué : un symbole conforme n’entre pas - sur 40 × 25 mm avec les cinq champs texte. Ce n’est pas un défaut de rendu et il n’y - a rien à corriger.

                        Le détail chiffré du symbole — largeur de module, modules rendus, modules attendus — - n’est pas servi par ce poste. Cet écran n’affiche pas un chiffre qu’il aurait deviné.

                        `,1),Po=c(`

                        Chaque appui sort une étiquette pour de bon : le mot de passe est demandé au moment - d’imprimer, et l’impression repart d’elle-même une fois la session ouverte.

                        `,1),To=c('
                        ');function zo(a,t){Me(t,!0);const r=Be(t,"admin",7),i="printer.template",d="printer.options.offset_x",p="printer.options.offset_y",k=0,T=[{what:"alignment",label:"Imprimer la mire d’alignement"},{what:"ruler",label:"Imprimer la réglette"}];let m=G(1),j=G(!0),q=G(!1),D=G(""),L=G(null),W=G(""),z=null,R=!1;const K=u(()=>t.draft.text(i)),F=u(()=>kn(e(K),e(j),e(q),e(m))),E=u(()=>t.draft.config!==null),w=u(()=>t.draft.number(d)),g=u(()=>t.draft.number(p)),U=u(()=>Ye(t.draft,d)),B=u(()=>Ye(t.draft,p)),$=u(A),J=u(()=>r().busy||e(W)!=="");qt(()=>{if(t.draft.config===null)return;if(t.draft.dirty){z===null&&C();return}const I={x:t.draft.number(d),y:t.draft.number(p)};if(z!==null&&z.x===I.x&&z.y===I.y)return;const Y=z===null;z=I,v(L,I,!0),Y||S()});async function C(){if(!R){R=!0;try{const I=await za(),Y={x:tr(I.config,d),y:tr(I.config,p)};z=Y,v(L,Y,!0)}catch{}finally{R=!1}}}function y(I,Y){const ue=t.draft.number(I)+Y;ue=2?"s":""}`}function V(I,Y){const ue=kr(Y);ue!==null&&t.draft.set(I,ue)}async function X(I){if(!e(J)){v(W,I,!0),r().actionError="",r().notice="";try{const Y=await r().protect(()=>pr(I));if(Y===null)return;r().notice=Y.message,await r().refresh()}finally{v(W,"")}}}var ae=To(),M=s(ae);ze(M,{title:"Aperçu de l’étiquette",note:"Le même moteur que l’impression (A2) : le décalage se voit parce qu’il est cuit dans le bitmap. L’image porte le gabarit en cours d’édition, mais le décalage ENREGISTRÉ — jamais celui que les flèches sont en train de régler.",children:(I,Y)=>{var ue=Co(),fe=te(ue);{var pe=me=>{var Le=ko(),rt=s(Le);f(()=>o(rt,e($))),l(me,Le)};_(fe,me=>{e($)!==""&&me(pe)})}var ce=n(fe,2),N=s(ce),Q=s(N),re=n(Q,2);{var Z=me=>{var Le=qo(),rt=s(Le);f(()=>o(rt,e(D))),l(me,Le)};_(re,me=>{e(D)!==""&&me(Z)})}var ie=n(N,2),we=s(ie),Fe=n(s(we),2),x=s(Fe),se=n(we,2),ve=s(se);{let me=u(()=>!e(E)||e(w)<=k);Pe(ve,{label:"← 1 dot",get disabled(){return e(me)},onrun:()=>y(d,-1)})}var oe=n(ve,2);{let me=u(()=>!e(E));Pe(oe,{label:"1 dot →",get disabled(){return e(me)},onrun:()=>y(d,1)})}var le=n(se,2);{var je=me=>{var Le=So(),rt=s(Le);f(()=>o(rt,e(U))),l(me,Le)};_(le,me=>{e(U)!==""&&me(je)})}var qe=n(le,2),Se=n(s(qe),2),Ne=s(Se),Ve=n(qe,2),ye=s(Ve);{let me=u(()=>!e(E)||e(g)<=k);Pe(ye,{label:"↑ 1 dot",get disabled(){return e(me)},onrun:()=>y(p,-1)})}var Re=n(ye,2);{let me=u(()=>!e(E));Pe(Re,{label:"1 dot ↓",get disabled(){return e(me)},onrun:()=>y(p,1)})}var lt=n(Ve,2);{var it=me=>{var Le=Lo(),rt=s(Le);f(()=>o(rt,e(B))),l(me,Le)};_(lt,me=>{e(B)!==""&&me(it)})}var gt=n(lt,4),ht=s(gt),dt=n(gt,2),mt=s(dt),yt=n(dt,2);{var Ze=me=>{var Le=Eo();l(me,Le)};_(yt,me=>{e(E)||me(Ze)})}f((me,Le)=>{he(Q,"src",e(F)),o(x,me),o(Ne,Le)},[()=>h(e(w)),()=>h(e(g))]),Kt("load",Q,()=>v(D,"")),Kt("error",Q,()=>{O(e(F))}),Ce("change",ht,()=>S()),Da(ht,()=>e(j),me=>v(j,me)),Ce("change",mt,()=>S()),Da(mt,()=>e(q),me=>v(q,me)),l(I,ue)},$$slots:{default:!0}});var b=n(M,2);ze(b,{title:"Gabarit et impression",children:(I,Y)=>{var ue=Po(),fe=te(ue);{let re=u(()=>Ye(t.draft,i)),Z=u(()=>!e(E));at(fe,{label:"Gabarit",path:i,get value(){return e(K)},hint:"Le gabarit reproduit à l’identique s’appelle weighing_identical (A1).",get fault(){return e(re)},get disabled(){return e(Z)},onchange:ie=>{t.draft.set(i,ie),S()}})}var pe=n(fe,2);{let re=u(()=>t.draft.text("printer.options.darkness")),Z=u(()=>Ye(t.draft,"printer.options.darkness")),ie=u(()=>!e(E));at(pe,{label:"Noircissement",path:"printer.options.darkness",kind:"number",get value(){return e(re)},hint:"Trop bas, l’étiquette pâlit au soleil ; trop haut, elle bave et le scanner refuse.",get fault(){return e(Z)},get disabled(){return e(ie)},onchange:we=>V("printer.options.darkness",we)})}var ce=n(pe,2);{let re=u(()=>t.draft.text("printer.options.speed")),Z=u(()=>Ye(t.draft,"printer.options.speed")),ie=u(()=>!e(E));at(ce,{label:"Vitesse",path:"printer.options.speed",kind:"number",get value(){return e(re)},get fault(){return e(Z)},get disabled(){return e(ie)},onchange:we=>V("printer.options.speed",we)})}var N=n(ce,2);{let re=u(()=>t.draft.text("printer.options.copies")),Z=u(()=>Ye(t.draft,"printer.options.copies")),ie=u(()=>!e(E));at(N,{label:"Exemplaires",path:"printer.options.copies",kind:"number",get value(){return e(re)},hint:"Un client repart avec une étiquette : deux exemplaires se justifient, pas se devinent.",get fault(){return e(Z)},get disabled(){return e(ie)},onchange:we=>V("printer.options.copies",we)})}var Q=n(N,2);Te(Q,21,()=>T,re=>re.what,(re,Z)=>{{let ie=u(()=>e(W)===e(Z).what);Pe(re,{get act(){return e(Z).what},get label(){return e(Z).label},protected:!0,get busy(){return e(ie)},get disabled(){return e(J)},onrun:()=>{X(e(Z).what)}})}}),l(I,ue)},$$slots:{default:!0}}),l(a,ae),We()}ct(["change"]);const Sr=[{rank:1,code:"OVERLOAD",label:"Surcharge",when:"La balance annonce elle-même OL, ou le poids brut dépasse la capacité.",severity:"Bloquant",blocking:!0,message:"La balance est en surcharge. Retirez votre article.",thresholds:[],switchPath:"",switchLabel:"",note:`Seuil : la capacité, réglée au garde-fou 9 sous « ${Ct("limits.max_weight_g")} ».`},{rank:2,code:"MEASUREMENT_EXPIRED",label:"Poids périmé",when:"La mesure est plus vieille que la péremption, dans les deux modes de stabilité.",severity:"Bloquant",blocking:!0,message:"Poids indisponible. Patientez ou appelez un bénévole.",thresholds:[],switchPath:"",switchLabel:"",note:"Aucun seuil à régler : le poste calcule lui-même à partir du rythme de la balance."},{rank:3,code:"BASKET_MISSING",label:"Panier absent",when:"Le poids brut tombe dans la fenêtre négative du panier : il a été soulevé.",severity:"Bloquant",blocking:!0,message:"Le panier n'est pas sur la balance. Reposez-le.",thresholds:[{path:"limits.basket_min_g",label:"Bas de la fenêtre du panier",hint:"En grammes, NÉGATIF : c’est le poids du panier que la balance a perdu."},{path:"limits.basket_max_g",label:"Haut de la fenêtre du panier",hint:"Négatif lui aussi, et plus proche de zéro que le bas."}],switchPath:"limits.basket_check_enabled",switchLabel:"Ce poste travaille avec un panier taré",note:"La règle s’active ou non, en bloc : il n’y a pas de demi-mesure à régler."},{rank:4,code:"SCALE_EMPTY",label:"Plateau vide",when:"Le poids brut ne sort pas de la bande « il n’y a rien sur le plateau ».",severity:"Bloquant — un filet, hors parcours nominal",blocking:!0,message:"Posez votre produit.",thresholds:[{path:"limits.empty_max_g",label:"Plateau considéré vide",hint:"En dessous, le poste considère qu’il n’y a rien sur le plateau."}],switchPath:"",switchLabel:"",note:"Toucher une tuile sur un plateau vide ARME la sélection au lieu d’être refusé : la règle reste évaluée pour la saisie manuelle et les chemins dérivés."},{rank:5,code:"TARE_REQUIRED",label:"Remise à zéro nécessaire",when:"Le brut est sous la bande du plateau vide, et hors de la fenêtre du panier.",severity:"Bloquant",blocking:!0,message:"La balance doit être remise à zéro.",thresholds:[],switchPath:"",switchLabel:"",note:`Seuil : la valeur négative de celui du garde-fou 4, « ${Ct("limits.empty_max_g")} ».`},{rank:6,code:"WEIGHT_UNSTABLE",label:"Pesée instable",when:"La trame déclare la mesure instable.",severity:"Information par défaut (A3)",blocking:!1,message:"Pesée en cours…",thresholds:[],switchPath:"",switchLabel:"",note:"La sévérité suit l’exigence de stabilité : elle passe à Bloquant quand celle-ci est réglée sur « blocking ». L’impression n’est jamais bloquée par défaut."},{rank:7,code:"TARE_INVALID",label:"Emballage incohérent",when:"Une tare a été saisie, et elle atteint la pesée ou dépasse le maximum.",severity:"Bloquant",blocking:!0,message:"Le poids de l'emballage est supérieur ou égal à la pesée.",thresholds:[{path:"limits.max_tare_g",label:"Tare maximum",hint:"Une tare plus lourde que le maximum est une faute de frappe."}],switchPath:"",switchLabel:"",note:""},{rank:8,code:"WEIGHT_TOO_LOW",label:"Poids trop faible",when:"Vente au poids : le NET est strictement positif et ne dépasse pas le plancher.",severity:"Bloquant",blocking:!0,message:"La balance doit être retarée, ou l'emballage est trop lourd.",thresholds:[{path:"limits.min_weight_g",label:"Poids minimum",hint:"Une dérogation par produit existe, dans l’onglet Catalogue."}],switchPath:"",switchLabel:"",note:""},{rank:9,code:"WEIGHT_TOO_HIGH",label:"Poids trop élevé",when:"Le NET dépasse la capacité — strictement, pour que la capacité reste atteignable.",severity:"Bloquant",blocking:!0,message:"{{.Weight}} kg, ça paraît un peu lourd !",thresholds:[{path:"limits.max_weight_g",label:"Poids maximum",hint:"C’est la capacité du champ NNDDD du code-barres, pas un seuil de vraisemblance."}],switchPath:"",switchLabel:"",note:""},{rank:10,code:"UNITS_OUT_OF_RANGE",label:"Nombre d’unités hors plage",when:"Vente à l’unité : la quantité sort de la plage.",severity:"Bloquant",blocking:!0,message:"{{.Quantity}} unités, ça paraît un peu beaucoup !",thresholds:[{path:"limits.min_units",label:"Unités minimum",hint:""},{path:"limits.max_units",label:"Unités maximum",hint:""}],switchPath:"",switchLabel:"",note:""},{rank:11,code:"AMOUNT_OUT_OF_CAPACITY",label:"Montant hors capacité du code-barres",when:"La charge utile encode un PRIX, et il dépasse ce que le champ peut porter.",severity:"Bloquant",blocking:!0,message:"Prix trop élevé pour le code-barres.",thresholds:[{path:"limits.max_amount_cents",label:"Montant maximum",hint:"En centimes. Aucun préfixe du plan livré n’encode un prix : la règle est éprouvée sans qu’aucun produit puisse l’atteindre."}],switchPath:"",switchLabel:"",note:""},{rank:12,code:"ZERO_PRICE",label:"Prix nul",when:"Le montant du tarif imprimé en grand vaut zéro.",severity:"Bloquant",blocking:!0,message:"Prix nul. Appelez un bénévole.",thresholds:[],switchPath:"",switchLabel:"",note:"Aucun seuil : un produit à 0 € est une anomalie sans nuance."},{rank:13,code:"LIGHT_PRODUCT_ALLOWED",label:"Produit léger autorisé",when:"Le garde-fou 8 n’a pas déclenché grâce à la dérogation du produit.",severity:"Information",blocking:!1,message:"",thresholds:[],switchPath:"",switchLabel:"",note:"Aucun seuil général : c’est la dérogation par produit, listée plus bas et posée depuis l’onglet Catalogue. Rien ne s’affiche au client ; l’id du produit est journalisé."},{rank:14,code:"PRODUCT_WITHDRAWN",label:"Produit retiré",when:"Quelqu’un a décidé de ne plus proposer ce produit.",severity:"Bloquant",blocking:!0,message:"Ce produit n'est pas disponible.",thresholds:[],switchPath:"",switchLabel:"",note:"Aucun seuil : c’est une décision humaine, prise depuis l’onglet Catalogue. Aucune règle d’import ne peut la déduire."}],jo=["{{.Weight}}","{{.Quantity}}"];function Ro(a){return Sr.find(t=>t.code===a)?.label??"Garde-fou inconnu de cet écran"}function Oo(a){return a==="blocking"?"Bloquant":a==="info"?"Information":"Sévérité inconnue de cet écran"}var Ao=c('

                        Rien ne s’affiche au client : c’est une information.

                        '),No=c('

                        '),Io=c('
                        '),Do=c('

                        '),Fo=c('
                      • '),Uo=c(`

                          Un seuil vidé garde la valeur du fichier : il n’écrit pas zéro, et la case la retrouve - dès qu’on quitte le champ. Pour changer un seuil, on tape l’autre valeur.

                          `,1);function Mo(a,t){Me(t,!0);function r(T,m){const j=kr(m);j!==null&&t.draft.set(T,j)}var i=Uo(),d=te(i);Te(d,21,()=>Sr,T=>T.code,(T,m)=>{var j=Fo(),q=s(j),D=s(q),L=s(D),W=n(D,2),z=s(W),R=n(W,2),K=s(R),F=n(R,2),E=s(F),w=n(q,2),g=s(w),U=n(w,2);{var B=A=>{var h=Ao();l(A,h)},$=A=>{var h=No(),V=s(h);f(()=>o(V,`« ${e(m).message??""} »`)),l(A,h)};_(U,A=>{e(m).message===""?A(B):A($,-1)})}var J=n(U,2);{var C=A=>{{let h=u(()=>t.draft.flag(e(m).switchPath));Na(A,{get path(){return e(m).switchPath},get label(){return e(m).switchLabel},get on(){return e(h)},onchange:V=>t.draft.set(e(m).switchPath,V)})}};_(J,A=>{e(m).switchPath!==""&&A(C)})}var y=n(J,2);Te(y,17,()=>e(m).thresholds,A=>A.path,(A,h)=>{var V=Io(),X=s(V);{let ae=u(()=>t.draft.text(e(h).path));at(X,{get label(){return e(h).label},get path(){return e(h).path},kind:"number",get value(){return e(ae)},get hint(){return e(h).hint},onchange:M=>r(e(h).path,M)})}Ce("focusout",V,ae=>qr(ae.target,t.draft.text(e(h).path))),l(A,V)});var S=n(y,2);{var O=A=>{var h=Do(),V=s(h);f(()=>o(V,e(m).note)),l(A,h)};_(S,A=>{e(m).note!==""&&A(O)})}f(A=>{he(j,"data-code",e(m).code),o(L,e(m).rank),o(z,e(m).label),o(K,e(m).code),he(F,"data-blocking",A),o(E,e(m).severity),o(g,e(m).when)},[()=>String(e(m).blocking)]),l(T,j)});var p=n(d,4),k=s(p);f(T=>o(k,`Les marqueurs ${T??""} sont remplacés par les valeurs de la pesée au - moment où le message s’affiche.`),[()=>jo.join(" et ")]),l(a,i),We()}ct(["focusout"]);function Wo(a){const t=a.value("pricing.tiers");return Array.isArray(t)?t.map(r=>{const i=r??{},d=i.discount_percent;return{code:String(i.code??""),label:String(i.label??""),abbrev:String(i.abbrev??""),written:d===void 0?null:String(d),discount:Bo(d)?d:null,rank:Number(i.rank??0)}}):[]}function Bo(a){return typeof a!="number"||!Number.isFinite(a)||a<0||a>100?!1:Math.abs(a*10-Math.round(a*10))<1e-9}function ar(a){return a===null?"":String(a).replace(".",",")}function Vo(a){const t=1e3-Math.round(a*10);return`${String(Math.trunc(t/100))},${String(t%100).padStart(2,"0")}`}var Go=c('

                          Aucun tarif déclaré dans la configuration lue.

                          '),Ho=c('Prix du catalogue Odoo — pas de remise'),rr=c(' '),Jo=c(' % ',1),Ko=c(' '),$o=c(`

                          CodeLibelléAbrégéRemiseOrdre

                          Un champ vidé garde la valeur du fichier : il n’écrit pas zéro, et la case la - retrouve dès qu’on quitte le champ. Une remise effacée serait le plein tarif pour - tous les adhérents.

                          `,1);function Yo(a,t){Me(t,!0);const r=u(()=>Wo(t.draft)),i=u(()=>String(t.draft.value("pricing.reference_code")??""));function d(q){return q<=1?`${String(q)} tarif déclaré`:`${String(q)} tarifs déclarés`}function p(q,D){const L=xo(D);L!==null&&t.draft.set(q,L)}var k=Rt(),T=te(k);{var m=q=>{var D=Go();l(q,D)},j=q=>{var D=$o(),L=te(D),W=s(L),z=n(L,2),R=s(z),K=n(s(R));Te(K,21,()=>e(r),da,(F,E,w)=>{var g=Ko(),U=s(g),B=s(U),$=n(U),J=s($);he(J,"aria-label",`Libellé du tarif ${w+1}`);var C=n($),y=s(C);he(y,"aria-label",`Abrégé du tarif ${w+1}`);var S=n(C),O=s(S);{var A=b=>{var I=Ho();l(b,I)},h=b=>{var I=rr(),Y=s(I);f(()=>o(Y,`${e(E).written??""} — le tarif de référence est le prix du catalogue : il - ne peut pas porter de remise, et l’enregistrement la refusera.`)),l(b,I)},V=b=>{var I=rr(),Y=s(I);f(()=>o(Y,`${e(E).written??""} — une remise s’écrit au dixième de point ; celle-ci se - change dans le fichier de configuration.`)),l(b,I)},X=b=>{var I=Jo(),Y=te(I);he(Y,"aria-label",`Remise du tarif ${w+1}`);var ue=n(Y,2),fe=s(ue);f((pe,ce)=>{Zt(Y,pe),o(fe,`un produit à 10,00 €/kg s’affiche ${ce??""} €/kg`)},[()=>ar(e(E).discount??0),()=>Vo(e(E).discount??0)]),Ce("input",Y,pe=>p(`pricing.tiers.${String(w)}.discount_percent`,pe.currentTarget.value)),Ce("focusout",Y,pe=>qr(pe.currentTarget,ar(e(E).discount??0))),l(b,I)};_(O,b=>{e(E).code===e(i)&&e(E).written===null?b(A):e(E).code===e(i)?b(h,1):e(E).written!==null&&e(E).discount===null?b(V,2):b(X,-1)})}var ae=n(S),M=s(ae);f(()=>{o(B,e(E).code),Zt(J,e(E).label),Zt(y,e(E).abbrev),o(M,e(E).rank)}),Ce("input",J,b=>t.draft.set(`pricing.tiers.${String(w)}.label`,b.currentTarget.value)),Ce("input",y,b=>t.draft.set(`pricing.tiers.${String(w)}.abbrev`,b.currentTarget.value)),l(F,g)}),f(F=>o(W,`${F??""}.`),[()=>d(e(r).length)]),l(q,D)};_(T,q=>{e(r).length===0?q(m):q(j,-1)})}l(a,k),We()}ct(["input","focusout"]);var Xo=c('

                          Aucune dérogation : la limite générale s’applique à tous les produits.

                          '),Qo=c(`

                          Les noms de produits n’ont pas pu être lus : le catalogue en service n’a pas - répondu. Les identifiants Odoo restent affichés.

                          `),Zo=c(`Produit retiré : le garde-fou 14 refuse le produit avant que cette - dérogation ait un sens.`),eu=c('
                        1. '),tu=c('

                            ',1);function au(a,t){Me(t,!0);function r(L){return Aa(t.names[L],t.namesState,"Nom non lu")}const i=20,d=u(()=>t.waivers.filter(L=>!L.offered)),p=u(()=>t.waivers.slice(0,i)),k=u(()=>`${ne(e(p).length)} ${e(p).length>1?"lignes affichées":"ligne affichée"} sur ${ne(t.waivers.length)} `+(e(d).length===0?`${t.waivers.length>1?"dérogations en vigueur":"dérogation en vigueur"}.`:`${t.waivers.length>1?"dérogations enregistrées":"dérogation enregistrée"}, dont ${ne(e(d).length)} sur un produit retiré, sans effet : le garde-fou 14 refuse le produit avant que le 8 ait un sens.`)+(t.waivers.length>e(p).length?" Les autres se lisent produit par produit depuis l’onglet Catalogue.":""));function T(L){return`plancher ${ne(L)} g : refusé à ${ne(L)} g et en dessous`}var m=Rt(),j=te(m);{var q=L=>{var W=Xo();l(L,W)},D=L=>{var W=tu(),z=te(W),R=s(z),K=n(z,2);{var F=w=>{var g=Qo();l(w,g)};_(K,w=>{t.namesState==="unread"&&w(F)})}var E=n(K,2);Te(E,21,()=>e(p),w=>w.product_id,(w,g)=>{var U=eu(),B=s(U),$=s(B),J=n(B,2),C=s(J),y=n(J,2),S=s(y),O=n(y,2),A=s(O),h=n(O,2),V=s(h),X=n(h,2);{var ae=M=>{var b=Zo();l(M,b)};_(X,M=>{e(g).offered||M(ae)})}f((M,b,I,Y)=>{he(U,"data-withdrawn",M),o($,b),o(C,e(g).product_id),o(S,I),o(A,e(g).reason),o(V,`${Y??""}, ${e(g).decided_by??""}`)},[()=>String(!e(g).offered),()=>r(e(g).product_id),()=>T(e(g).min_weight_g??0),()=>va(e(g).decided_at)]),l(w,U)}),f(()=>o(R,e(k))),l(L,W)};_(j,L=>{t.waivers.length===0?L(q):L(D,-1)})}l(a,m),We()}var ru=c(" ",1),nu=c(`

                            Les deux arrondis sont distincts parce qu’ils tombent à des endroits différents du - calcul : arrondir le prix au kilo puis le multiplier, ou multiplier puis arrondir, ne - donne pas le même centime sur une étiquette. Le poste applique l’arrondi commercial, - et l’écart se voit au centime près sur la même pesée.

                            `,1),su=c(`

                            Aucun verdict en ce moment : ces lignes portent sur la pesée EN COURS, et le poste - est au repos. Elles apparaissent le temps d’un cycle, quand un client pose son sac - et touche une tuile.

                            `),lu=c(' '),iu=c(' ',1),ou=c('
                          • '),uu=c('

                              ',1),cu=c(`

                              L’ordre compte : le premier verdict bloquant décide de ce que le client lit. Les - garde-fous 1 à 7 portent sur l’état de la balance, c’est-à-dire le poids brut ; les - garde-fous 8 à 14 portent sur la vente, c’est-à-dire le net. Le code en gris est - celui du journal, celui qu’on lit au téléphone.

                              Ce que les garde-fous disent de la pesée en cours

                              `,1),du=c(`

                              Le plan de numérotation n’est PAS ici, et c’est tout l’intérêt : préfixes, largeur de - la référence, largeur de la charge utile, décimales et mode de vente sont une - constante du binaire, indexée par préfixe et vérifiée au démarrage. - Un champ qui change le SENS du code lu par la caisse n’est pas un réglage, c’est un - contrat externe : il change avec une version du binaire, relue et testée, jamais - depuis l’écran d’un poste.

                              `,1),vu=c('
                              ');function pu(a,t){Me(t,!0);const r=u(()=>t.health.state.diagnostics),i=u(()=>t.health.decisions.filter(R=>R.min_weight_g!==null));let d=G(ft({})),p=G("loading");k();async function k(){try{const R=await ur();v(d,Object.fromEntries(R.products.map(K=>[K.id,K.name])),!0),v(p,"read")}catch{v(p,"unread")}}function T(R){return Aa(e(d)[R],e(p),"Nom non lu")}const m=u(()=>`${ne(e(r).length)} ${e(r).length>1?"verdicts":"verdict"} sur les quatorze garde-fous.`);var j=vu(),q=s(j);ze(q,{title:"Grille de tarifs",note:"Un second tarif n’est pas une case à cocher : c’est une ligne de plus dans cette grille.",children:(R,K)=>{var F=ru(),E=te(F);Yo(E,{get draft(){return t.draft}});var w=n(E,2);{let U=u(()=>t.draft.text("pricing.primary_code"));at(w,{label:"Tarif imprimé en grand",path:"pricing.primary_code",get value(){return e(U)},hint:"Le prix que le client lit sur l’étiquette (A7).",onchange:B=>t.draft.set("pricing.primary_code",B)})}var g=n(w,2);{let U=u(()=>t.draft.text("pricing.reference_code"));at(g,{label:"Tarif qui serait encodé si le code-barres portait un prix",path:"pricing.reference_code",get value(){return e(U)},hint:"Le plan livré ne porte pas de prix, mais un poids ou un nombre d’unités : la caisse retrouve le prix par la référence, dans Odoo, et ne doit jamais sous-facturer.",onchange:B=>t.draft.set("pricing.reference_code",B)})}l(R,F)},$$slots:{default:!0}});var D=n(q,2);ze(D,{title:"Les deux arrondis",children:(R,K)=>{var F=nu(),E=te(F);{let g=u(()=>t.draft.text("pricing.unit_price_rounding"));at(E,{label:"Arrondi du prix unitaire dérivé",path:"pricing.unit_price_rounding",get value(){return e(g)},hint:"Il s’applique au prix au kilo calculé par la remise.",onchange:U=>t.draft.set("pricing.unit_price_rounding",U)})}var w=n(E,2);{let g=u(()=>t.draft.text("pricing.amount_rounding"));at(w,{label:"Arrondi du montant",path:"pricing.amount_rounding",get value(){return e(g)},hint:"Il s’applique au montant de l’étiquette.",onchange:U=>t.draft.set("pricing.amount_rounding",U)})}l(R,F)},$$slots:{default:!0}});var L=n(D,2);ze(L,{title:"Les quatorze garde-fous, dans l’ordre d’évaluation",note:"Le seuil se modifie ici. La sévérité ne se règle pas : elle dit si le poste refuse ou avertit, et cela ne dépend pas du magasin. Le message affiché au client n’est pas encore modifiable depuis cet écran.",children:(R,K)=>{var F=cu(),E=n(te(F),2);Mo(E,{get draft(){return t.draft}});var w=n(E,4);{var g=B=>{var $=su();l(B,$)},U=B=>{var $=uu(),J=te($),C=s(J),y=n(J,2);Te(y,21,()=>e(r),S=>S.code,(S,O)=>{var A=ou(),h=s(A),V=s(h),X=n(h,2),ae=s(X),M=n(X,2),b=s(M),I=n(M,2);{var Y=pe=>{var ce=lu(),N=s(ce);f(()=>o(N,`« ${e(O).message??""} »`)),l(pe,ce)};_(I,pe=>{e(O).message!==""&&pe(Y)})}var ue=n(I,2);{var fe=pe=>{var ce=iu(),N=te(ce),Q=s(N),re=n(N,2),Z=s(re);f(ie=>{o(Q,ie),o(Z,e(O).product_id)},[()=>T(e(O).product_id)]),l(pe,ce)};_(ue,pe=>{e(O).product_id!==""&&pe(fe)})}f((pe,ce,N)=>{he(A,"data-blocking",pe),o(V,ce),o(ae,e(O).code),o(b,N)},[()=>String(e(O).blocking),()=>Ro(e(O).code),()=>Oo(e(O).severity)]),l(S,A)}),f(S=>{he(J,"data-verdicts",S),o(C,e(m))},[()=>String(e(r).length)]),l(B,$)};_(w,B=>{e(r).length===0?B(g):B(U,-1)})}l(R,F)},$$slots:{default:!0}});var W=n(L,2);ze(W,{title:"Code-barres",note:"Un seul réglage, et c’est voulu : le reste du plan de numérotation n’est pas de la configuration.",children:(R,K)=>{var F=du(),E=te(F);{let w=u(()=>t.draft.flag("barcode.verify_reference_check_digit"));Na(E,{path:"barcode.verify_reference_check_digit",label:"Refuser une référence dont la clé de contrôle est fausse",hint:"Décoché, le poste recalcule une clé juste sur une référence fausse, en silence — et la caisse encaisse un autre article.",get on(){return e(w)},onchange:g=>t.draft.set("barcode.verify_reference_check_digit",g)})}l(R,F)},$$slots:{default:!0}});var z=n(W,2);ze(z,{title:"Dérogations de poids minimum",note:"En lecture ici ; elles se modifient depuis l’onglet Catalogue, là où se trouve le produit.",children:(R,K)=>{au(R,{get waivers(){return e(i)},get names(){return e(d)},get namesState(){return e(p)}})},$$slots:{default:!0}}),l(a,j),We()}var fu=c(" "),gu=c(' '),mu=c('
                              ChampEn serviceDans le fichier
                              ');function hu(a,t){Me(t,!0);var r=mu(),i=s(r),d=n(s(i));Te(d,21,()=>t.rows,p=>p.path,(p,k)=>{const T=u(()=>Ct(e(k).path));var m=gu(),j=s(m),q=s(j),D=n(q);{var L=F=>{var E=fu(),w=s(E);f(()=>o(w,e(k).path)),l(F,E)};_(D,F=>{jt.showTechnicalNames&&e(T)!==e(k).path&&F(L)})}var W=n(j),z=s(W),R=n(W),K=s(R);f(()=>{he(m,"data-path",e(k).path),o(q,`${e(T)??""} `),o(z,e(k).before),o(K,e(k).after)}),l(p,m)}),l(a,r),We()}var _u=c(" "),bu=c(' '),wu=c("
                            • "),yu=c(`

                                Recopier reste possible : les valeurs entrent dans le brouillon, où elles se - corrigent champ par champ avant l’enregistrement.

                                `);function xu(a,t){Me(t,!0);const r=20,i=u(()=>t.faults.slice(0,r)),d=u(()=>ca(e(i).length,t.faults.length,"contrôle refuse une clé","contrôles refusent une clé"));var p=yu(),k=s(p),T=s(k),m=n(k,2);Te(m,21,()=>e(i),da,(j,q)=>{var D=wu(),L=s(D),W=s(L),z=n(L,2);{var R=w=>{var g=_u(),U=s(g);f(()=>o(U,e(q).field)),l(w,g)};_(z,w=>{jt.showTechnicalNames&&w(R)})}var K=n(z),F=n(K);{var E=w=>{var g=bu(),U=s(g);f(B=>o(U,`Valeurs acceptées : ${B??""}.`),[()=>e(q).allowed.join(", ")]),l(w,g)};_(F,w=>{e(q).allowed!==void 0&&e(q).allowed.length>0&&w(E)})}f(w=>{o(W,w),o(K,` ${e(q).message??""} `)},[()=>Ct(e(q).field)]),l(j,D)}),f(()=>o(T,`Ce fichier serait refusé en l’état : ${e(d)??""}`)),l(a,p),We()}const ku=2e3,qu=300*1e3;async function Su(){const a=Date.now()+qu;for(;Date.now()setTimeout(t,ku));try{if((await fetch("/healthz",{cache:"no-store"})).ok)return!0}catch{}}return!1}var Lu=c('

                                ',1),Eu=c(`

                                Touchez de nouveau pour confirmer. Rien ne défait un redémarrage une fois - l’ordinateur parti.

                                `),Cu=c(" ",1),Pu=c('

                                '),Tu=c('
                              • '),zu=c('
                                  '),ju=c(`

                                  Trois gestes de reprise, du plus doux au plus brutal. Le premier ne coupe rien.

                                  Relire le fichier de configuration
                                  Met en service config.json tel qu’il est sur le disque, sans arrêter le - poste. À utiliser après une modification faite à la main dans le fichier.
                                  Redémarrer le poste
                                  Arrête l’application et la relance. La pesée est interrompue quelques secondes ; - l’écran client revient tout seul.
                                  Redémarrer l’ordinateur
                                  Redémarre la machine entière. Comptez une minute avant que l’écran revienne.
                                  `,1);function Ru(a,t){Me(t,!0);let r=G(""),i=G(""),d=G(ft([])),p=G(!1),k=G(0),T=G(0),m=G(!1);const j=u(()=>e(k)>0);qt(()=>{if(!e(j))return;const z=setInterval(()=>{v(T,Math.max(0,Math.round((e(k)-Date.now())/1e3)),!0)},1e3);return()=>clearInterval(z)});async function q(){v(r,"reload-config"),v(i,""),v(d,[],!0);try{const z=await t.admin.protect(async()=>{try{return await vn()}catch(R){if(fa(R))throw R;return t.admin.report(R),v(d,t.admin.lastFaults,!0),null}});if(z===null)return;v(i,`Le fichier est en service. Empreinte ${z.config_fingerprint}.`)}finally{v(r,"")}}async function D(){v(r,"restart"),v(i,""),v(d,[],!0);try{const z=await t.admin.protect(()=>pn());if(z===null)return;v(i,z.message,!0),v(p,!0),v(i,await Su()?"Le poste est revenu.":"Le poste n’a pas répondu dans les cinq minutes. Allez le voir.",!0)}finally{v(r,""),v(p,!1)}}async function L(){if(!e(m)){v(m,!0);return}v(r,"reboot"),v(i,""),v(d,[],!0);try{const z=await t.admin.protect(()=>fn());if(z===null)return;v(k,Date.parse(z.at),!0),v(T,z.seconds_left,!0)}finally{v(r,""),v(m,!1)}}async function W(){v(r,"cancel-reboot");try{const z=await t.admin.protect(()=>gn());if(z===null)return;v(k,0),v(i,z.message,!0)}finally{v(r,"")}}ze(a,{title:"Maintenance",children:(z,R)=>{var K=ju(),F=n(te(K),2),E=n(s(F),2),w=n(s(E),3);{let h=u(()=>e(r)==="reload-config");Pe(w,{kind:"write",act:"reload-config",label:"Relire le fichier",protected:!0,get busy(){return e(h)},onrun:q})}var g=n(E,4),U=n(s(g));{let h=u(()=>e(r)==="restart"||e(p));Pe(U,{kind:"write",act:"restart",label:"Redémarrer le poste",protected:!0,get busy(){return e(h)},onrun:D})}var B=n(g,4),$=n(s(B));{var J=h=>{var V=Lu(),X=te(V),ae=s(X),M=n(X,2);{let b=u(()=>e(r)==="cancel-reboot");Pe(M,{kind:"write",act:"cancel-reboot",label:"Annuler",get busy(){return e(b)},onrun:W})}f(()=>o(ae,`L’ordinateur redémarre dans ${e(T)??""} seconde${e(T)>1?"s":""}.`)),l(h,V)},C=h=>{var V=Cu(),X=te(V);{var ae=b=>{var I=Eu();l(b,I)};_(X,b=>{e(m)&&b(ae)})}var M=n(X,2);{let b=u(()=>e(m)?"Confirmer le redémarrage":"Redémarrer l’ordinateur"),I=u(()=>e(r)==="reboot");Pe(M,{kind:"destructive",act:"reboot",get label(){return e(b)},protected:!0,get busy(){return e(I)},onrun:L})}l(h,V)};_($,h=>{e(j)?h(J):h(C,-1)})}var y=n(F,2);{var S=h=>{var V=Pu(),X=s(V);f(()=>o(X,e(i))),l(h,V)};_(y,h=>{e(i)!==""&&h(S)})}var O=n(y,2);{var A=h=>{var V=zu();Te(V,21,()=>e(d),X=>X.field,(X,ae)=>{var M=Tu(),b=s(M),I=s(b),Y=n(b);f(()=>{o(I,e(ae).field),o(Y,` — ${e(ae).message??""}`)}),l(X,M)}),l(h,V)};_(O,h=>{e(d).length>0&&h(A)})}l(z,K)},$$slots:{default:!0}}),We()}function Ou(a,t){const r=[];for(const i of[...La(a),...La(t)]){if(r.some(k=>k.path===i))continue;const d=nr(ga(a,i)),p=nr(ga(t,i));d!==p&&r.push({path:i,before:d,after:p})}return r}function ga(a,t){let r=a;for(const i of t.split(".")){if(r===null||typeof r!="object")return;r=r[i]}return r}function La(a,t=""){if(a===null||typeof a!="object"||Array.isArray(a))return t===""?[]:[t];const r=[];for(const[i,d]of Object.entries(a))r.push(...La(d,t===""?i:`${t}.${i}`));return r}function nr(a){return a===void 0?"—":a===null?"vide":typeof a=="boolean"?a?"oui":"non":typeof a=="object"?JSON.stringify(a):String(a)}const Au=new Set(["modified_at","catalog.options.password"]),Nu=[{path:"station.name",name:"le nom du poste"},{path:"network.listen",name:"l’adresse d’écoute"},{path:"scale.options.port",name:"le port de la balance"},{path:"printer.options.queue",name:"la file d’impression"},{path:"printer.options.path",name:"le nœud d’impression"},{path:"printer.options.address",name:"l’adresse de l’imprimante"},{path:"catalog.options.url",name:"l’adresse du partage"},{path:"catalog.options.username",name:"le compte du partage"},{path:"catalog.images.path",name:"le chemin des images"}];function Iu(a,t){return a===null||t===null?[]:Ou(a,t).filter(r=>!Au.has(r.path))}function Du(a,t){if(a===null||t===null)return[];const r=a,i=t;return Nu.filter(d=>sr(i,d.path)&&!sr(r,d.path))}function sr(a,t){const r=ga(a,t);return r==null||r===""?!0:typeof r!="object"||Array.isArray(r)?!1:Object.keys(r).length===0}function Fu(a){return a.length<2?a.join(""):`${a.slice(0,-1).join(", ")} et ${a[a.length-1]??""}`}async function Uu(a,t){const i=await fetch(`/admin/api/config/export?hardware=${t?"1":"0"}`,{headers:{accept:"application/json"}});if(!i.ok)throw new zt(i.status,Lr(await i.text(),"L’export"));return{name:Wu(a,t),blob:await i.blob()}}async function Mu(a){const t=await fetch("/admin/api/config/import",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(a)}),r=await t.text();if(!t.ok)throw new zt(t.status,Lr(r,"L’import"));return JSON.parse(r)}function Wu(a,t){const r=t?"":"-sans-materiel";return`config-poste${String(a)}${r}-${Bu()}.json`}function Bu(){const a=new Date,t=String(a.getMonth()+1).padStart(2,"0"),r=String(a.getDate()).padStart(2,"0");return`${String(a.getFullYear())}-${t}-${r}`}function Vu(a,t){const r=URL.createObjectURL(t),i=document.createElement("a");i.href=r,i.download=a,document.body.appendChild(i),i.click(),i.remove(),setTimeout(()=>{URL.revokeObjectURL(r)},0)}function Lr(a,t){try{const r=JSON.parse(a);if(typeof r?.message=="string"&&r.message!=="")return r.message}catch{}return`${t} a été refusé par le poste.`}var Gu=c('
                                  Empreinte de la configuration en service
                                  Version du binaire
                                  Répertoire de données
                                  Espace disque
                                  ',1),Hu=c('

                                  '),Ju=c(`

                                  Ce fichier n’a été comparé à rien : la configuration en service n’a pas pu être - lue. Ni « identique », ni « n champs changent » — ce que ce fichier changerait - sur ce poste reste inconnu.

                                  `),Ku=c(`

                                  Ce fichier décrit la même configuration que celle en service : il n’y a rien à - recopier. C’est ce qu’on veut lire à la fin d’un clonage. Deux champs ne sont pas - comparés : la date du dernier enregistrement, que chaque poste écrit lui-même, et - le mot de passe du catalogue, qu’aucun des deux ne porte en clair.

                                  `),$u=c('

                                  '),Yu=c(`

                                  Recopier n’applique rien : les valeurs entrent dans le brouillon, et c’est - « Enregistrer » qui les met en service.

                                  `,1),Xu=c('

                                  ',1),Qu=c(`

                                  L’export emporte encore l’empreinte du mot de passe : c’est la seule lecture que le - poste garde derrière la clé. L’import, lui, est lu PAR LE POSTE, qui écarte les deux - secrets et le numéro de poste avant de dire ce qui changerait.

                                  `,1),Zu=c('

                                  Lecture des versions enregistrées…

                                  '),ec=c(`

                                  Les versions enregistrées n’ont pas pu être lues : cette liste ne dit donc rien de - ce que ce poste garde. Ce n’est pas « aucune version ».

                                  `),tc=c('

                                  Aucune version enregistrée : ce poste n’a jamais été reconfiguré.

                                  '),ac=c('
                                • '),rc=c(`

                                    Remettre une version en service remplace la configuration du poste sur-le-champ, et - ce qui n’a pas été enregistré est perdu : c’est le seul geste de cette page qui - change le poste tout de suite, et le seul qui garde ses 72 px.

                                    `,1),nc=c('
                                    ');function sc(a,t){Me(t,!0);const r=Be(t,"admin",7),i=40,d=5;let p=G(ft([])),k=G("reading"),T=G(null),m=G(""),j=G(null),q=G(""),D=G(ft([])),L=G(""),W="";const z=u(()=>r().busy||e(L)!==""),R=u(()=>Iu(e(T),e(j))),K=u(()=>e(T)!==null&&e(j)!==null),F=u(()=>Du(e(T),e(j))),E=u(()=>e(R).slice(0,i)),w=u(()=>e(p).slice(0,d)),g=u(()=>ca(e(E).length,e(R).length,"champ qui change","champs qui changent")),U=u(()=>ca(e(w).length,e(p).length,"version enregistrée","versions enregistrées")),B=u(()=>e(R).length>1?`Recopier ces ${ne(e(R).length)} champs dans le brouillon`:"Recopier ce champ dans le brouillon");$(),qt(()=>{const b=t.health.config_fingerprint;b!==W&&(W=b,J())});async function $(){const b=await r().load(hn);if(b===null){v(k,"unreadable");return}v(p,b,!0),v(k,"read")}async function J(){try{v(T,(await za()).config,!0),v(m,"")}catch(b){v(T,null),v(m,b instanceof Error?b.message:"Le poste n’a pas répondu.",!0)}}async function C(b){v(L,b?"export-all":"export-clone",!0),r().notice="",r().actionError="";try{const I=await r().protect(()=>Uu(t.health.station,b));if(I===null)return;Vu(I.name,I.blob),r().notice=`${I.name} est remis au navigateur : c’est lui qui l’enregistre, voyez ses téléchargements.`}finally{v(L,"")}}async function y(b){if(b==null)return;r().notice="",r().actionError="";let I;try{I=JSON.parse(await b.text())}catch{r().actionError=`${b.name} n’est pas un fichier JSON lisible.`;return}v(L,"import");try{const Y=await r().protect(()=>Mu(I));if(Y===null)return;v(j,Y.config,!0),v(q,b.name,!0),v(D,Y.faults,!0),await J(),r().notice=S(b.name)}finally{v(L,"")}}function S(b){return e(K)?e(R).length===0?`${b} décrit la même configuration que celle en service.`:`${b} est lu. Rien n’est appliqué : relisez le tableau.`:`${b} est lu, mais la configuration en service ne l’est pas : rien ne peut être comparé.`}async function O(){if(e(j)===null)return;const b=e(j),I=[...e(R)];for(const Y of I)t.draft.set(Y.path,ga(b,Y.path));r().actionError="",r().notice="Le fichier est recopié dans le brouillon. Rien n’est appliqué avant « Enregistrer ».",await J()}async function A(b){v(L,`restore-${String(b)}`),r().notice="",r().actionError="";try{if(await r().protect(()=>_n(b))===null)return;await t.draft.load(),await $(),await J(),await r().refresh(),r().notice=`La version ${String(b)} est remise en service.`}finally{v(L,"")}}var h=nc(),V=s(h);ze(V,{title:"Identité du poste",children:(b,I)=>{var Y=Gu(),ue=te(Y);{let oe=u(()=>t.draft.text("station.number")),le=u(()=>Ye(t.draft,"station.number")),je=u(()=>Qt(t.draft,"station.number"));at(ue,{label:"Numéro du poste",path:"station.number",kind:"number",get value(){return e(oe)},get fault(){return e(le)},get allowed(){return e(je)},hint:"C’est de lui que dérive le nom du fichier de catalogue attendu, flv_.csv.",onchange:qe=>t.draft.set("station.number",Number(qe))})}var fe=n(ue,2);{let oe=u(()=>t.draft.text("station.name")),le=u(()=>Ye(t.draft,"station.name")),je=u(()=>Qt(t.draft,"station.name"));at(fe,{label:"Nom du poste",path:"station.name",get value(){return e(oe)},get fault(){return e(le)},get allowed(){return e(je)},hint:"Ce que lit un bénévole : « Poste 2 — fruits ».",onchange:qe=>t.draft.set("station.name",qe)})}var pe=n(fe,2);{let oe=u(()=>t.draft.text("station.coop")),le=u(()=>Ye(t.draft,"station.coop")),je=u(()=>Qt(t.draft,"station.coop"));at(pe,{label:"Coopérative",path:"station.coop",get value(){return e(oe)},get fault(){return e(le)},get allowed(){return e(je)},onchange:qe=>t.draft.set("station.coop",qe)})}var ce=n(pe,2),N=n(s(ce),2),Q=s(N),re=n(N,4),Z=s(re),ie=n(re,4),we=s(ie),Fe=n(ie,4),x=s(Fe);{var se=oe=>{var le=ot("non mesuré");l(oe,le)},ve=oe=>{var le=ot();f((je,qe,Se)=>o(le,`${je??""} libres sur - ${qe??""} — seuil d’alerte - ${Se??""} Mo`),[()=>Sa(t.health.disk.free_bytes),()=>Sa(t.health.disk.total_bytes),()=>ne(t.health.disk.alert_mb)]),l(oe,le)};_(x,oe=>{t.health.disk===null?oe(se):oe(ve,-1)})}f(()=>{o(Q,t.health.config_fingerprint),o(Z,t.health.version),o(we,t.health.disk===null?"non publié par ce poste":t.health.disk.path)}),l(b,Y)},$$slots:{default:!0}});var X=n(V,2);ze(X,{title:"Exporter, importer",note:"Pour installer un autre poste : ce fichier emporte les tarifs, les garde-fous, l’étiquette, les catégories, et les réglages du matériel que les quatre postes partagent — le décalage d’étiquette, le noircissement, la vitesse, le débit de la balance. Reste ici ce qui désigne ce poste-ci ou ce magasin — le mot de passe, le code de secours, le numéro et le nom du poste, le port de la balance, la file d’impression, l’adresse du partage et son compte, le chemin des images et le réseau.",children:(b,I)=>{var Y=Qu(),ue=te(Y),fe=s(ue);{let x=u(()=>e(L)==="export-all");Pe(fe,{label:"Exporter tout",protected:!0,get busy(){return e(x)},get disabled(){return e(z)},onrun:()=>{C(!0)}})}var pe=n(fe,2);{let x=u(()=>e(L)==="export-clone");Pe(pe,{label:"Exporter sans le matériel",protected:!0,get busy(){return e(x)},get disabled(){return e(z)},onrun:()=>{C(!1)}})}var ce=n(pe,2);let N;var Q=s(ce),re=n(Q,3),Z=n(ue,4);{var ie=x=>{var se=Hu(),ve=s(se);f(()=>o(ve,`La configuration en service n’a pas pu être lue : ${e(m)??""} La colonne - « En service » ne peut donc rien affirmer.`)),l(x,se)};_(Z,x=>{e(m)!==""&&x(ie)})}var we=n(Z,2);{var Fe=x=>{var se=Xu(),ve=te(se),oe=s(ve),le=n(ve,2);{var je=ye=>{xu(ye,{get faults(){return e(D)}})};_(le,ye=>{e(D).length>0&&ye(je)})}var qe=n(le,2);{var Se=ye=>{var Re=Ju();l(ye,Re)},Ne=ye=>{var Re=Ku();l(ye,Re)},Ve=ye=>{var Re=Yu(),lt=te(Re),it=s(lt),gt=n(it);{var ht=Le=>{var rt=ot("Les autres sont dans le fichier, et « Recopier » les prend tous.");l(Le,rt)};_(gt,Le=>{e(R).length>e(E).length&&Le(ht)})}var dt=n(lt,2);hu(dt,{get rows(){return e(E)}});var mt=n(dt,2);{var yt=Le=>{var rt=$u(),Ft=s(rt);f(Ot=>o(Ft,`Ce fichier ne porte pas ${Ot??""} : - l’export sans le matériel les retire, et l’import ne remet que le numéro du - poste. Les lignes correspondantes sont VIDES ci-dessus, et - « Recopier » recopie ce vide dans le brouillon.`),[()=>Fu(e(F).map(Ot=>Ot.name))]),l(Le,rt)};_(mt,Le=>{e(F).length>0&&Le(yt)})}var Ze=n(mt,2),me=s(Ze);Pe(me,{kind:"write",get label(){return e(B)},get disabled(){return e(z)},onrun:()=>{O()}}),f(()=>o(it,`${e(g)??""} `)),l(ye,Re)};_(qe,ye=>{e(K)?e(R).length===0?ye(Ne,1):ye(Ve,-1):ye(Se)})}f(()=>o(oe,`Fichier lu : ${e(q)??""}`)),l(x,se)};_(we,x=>{e(j)!==null&&x(Fe)})}f(()=>{N=Mt(ce,1,"choose svelte-18wbxwu",null,N,{working:e(L)==="import",off:e(z)}),o(Q,`${e(L)==="import"?"Lecture du fichier…":"Importer un fichier"} `),re.disabled=e(z)}),Ce("change",re,x=>{y(x.currentTarget.files?.item(0))}),l(b,Y)},$$slots:{default:!0}});var ae=n(X,2);ze(ae,{title:"Cinq versions restaurables",note:"Chaque enregistrement fait tourner les versions : la plus récente est la 1.",children:(b,I)=>{var Y=Rt(),ue=te(Y);{var fe=Q=>{var re=Zu();l(Q,re)},pe=Q=>{var re=ec();l(Q,re)},ce=Q=>{var re=tc();l(Q,re)},N=Q=>{var re=rc(),Z=te(re),ie=s(Z),we=n(Z,2),Fe=s(we);Te(Fe,21,()=>e(w),x=>x.version,(x,se)=>{var ve=ac(),oe=s(ve),le=s(oe),je=n(oe,2),qe=s(je),Se=n(je,2),Ne=s(Se),Ve=n(Se,2);{let ye=u(()=>e(L)===`restore-${String(e(se).version)}`);Pe(Ve,{kind:"destructive",label:"Remettre cette version en service",protected:!0,get busy(){return e(ye)},get disabled(){return e(z)},onrun:()=>{A(e(se).version)}})}f((ye,Re)=>{o(le,`version ${ye??""}`),o(qe,Re),o(Ne,e(se).config_fingerprint)},[()=>ne(e(se).version),()=>Pt(e(se).modified_at)]),l(x,ve)}),f(()=>o(ie,`${e(U)??""} Le poste n’en garde jamais plus de cinq.`)),l(Q,re)};_(ue,Q=>{e(k)==="reading"?Q(fe):e(k)==="unreadable"?Q(pe,1):e(p).length===0?Q(ce,2):Q(N,-1)})}l(b,Y)},$$slots:{default:!0}});var M=n(ae,2);Ru(M,{get admin(){return r()}}),l(a,h),We()}ct(["change"]);var lc=c('clé'),ic=c(' '),oc=c('');function Ht(a,t){const r=Be(t,"kind",3,"read"),i=Be(t,"hint",3,""),d=Be(t,"disabled",3,!1),p=Be(t,"engaged",3,!1),k=Be(t,"busy",3,!1),T=Be(t,"protected",3,!1);var m=oc();let j;var q=s(m),D=s(q),L=n(D);{var W=K=>{var F=lc();l(K,F)};_(L,K=>{T()&&K(W)})}var z=n(q,2);{var R=K=>{var F=ic(),E=s(F);f(()=>o(E,k()?"En cours…":i())),l(K,F)};_(z,K=>{i()!==""&&K(R)})}f(()=>{j=Mt(m,1,`big touch-target ${r()??""}`,"svelte-15nlt6i",j,{engaged:p(),busy:k()}),he(m,"data-kind",r()),m.disabled=d()||k(),o(D,`${t.label??""} `)}),Ce("click",m,function(...K){t.onrun?.apply(this,K)}),l(a,m)}ct(["click"]);const uc="ERR-SCL-09";var cc=c('

                                    '),dc=c('

                                    '),vc=c('

                                    '),pc=c('

                                    Déposez ici le fichier clé

                                    '),fc=c('

                                    ',1),gc=c('');function mc(a,t){Me(t,!0);const r=Be(t,"admin",7);let i=G("");const d=u(()=>t.health.state.degraded?.code===uc),p=u(()=>t.health.printing),k=u(()=>e(p)?.on_fallback===!0);let T=G(!1),m=G("");const j=u(()=>r().busy||e(m)!==""),q=new xr;qt(()=>{q.observe(t.health)});const D=u(()=>t.health.catalog_source===null?"Ce poste ne publie pas la source de son catalogue.":"Catalogue surveillé : "+t.health.catalog_source.label),L=u(()=>t.health.catalog!==null&&(t.health.catalog.result==="rejected"||t.health.catalog.result==="failed")?t.health.catalog:null);function W(N){v(i,""),q.forget(),v(m,N,!0)}async function z(N,Q){W(N);try{await r().run(Q)}finally{v(m,"")}}async function R(){W("reload");try{const N=await r().run(Zr);N!==null&&q.begin(N)}finally{v(m,"")}}async function K(N,Q){W(N);try{const re=await r().load(Q);re!==null&&v(i,re.message,!0)}finally{v(m,"")}}async function F(N,Q){W(N);try{const re=await r().protect(Q);re!==null&&(r().notice=re.message,await r().refresh())}finally{v(m,"")}}async function E(N){if(v(T,!1),N!=null){W("import");try{const Q=await r().protect(()=>vr(N));if(Q===null)return;v(i,Q.result==="rejected"||Q.result==="failed"?`${N.name} : REFUSÉ${Q.reason===""?"":" — "+Q.reason}. Le catalogue en service n’a pas changé.`:`${N.name} : ${String(Q.rows_read_count)} lignes lues, ${String(Q.weighable_count)} pesables. La veille l’appliquera dans la seconde.`,!0),await r().refresh()}finally{v(m,"")}}}var w=gc(),g=s(w);{var U=N=>{var Q=cc(),re=s(Q);f(()=>o(re,e(i))),l(N,Q)};_(g,N=>{e(i)!==""&&N(U)})}var B=n(g,2);{var $=N=>{var Q=dc(),re=s(Q);f(()=>o(re,q.sentence)),l(N,Q)};_(B,N=>{q.sentence!==""&&N($)})}var J=n(B,2);{var C=N=>{var Q=vc(),re=s(Q);f(()=>o(re,e(p).banner)),l(N,Q)};_(J,N=>{e(p)!==null&&e(p).banner!==""&&N(C)})}var y=n(J,2),S=s(y),O=n(y,2),A=s(O);{let N=u(()=>e(m)==="scale");Ht(A,{label:"Tester la balance",hint:"Ce que le poste a déjà observé — le port n’est pas rouvert.",get busy(){return e(N)},get disabled(){return e(j)},onrun:()=>{K("scale",rn)}})}var h=n(A,2);{let N=u(()=>e(m)==="printer");Ht(h,{label:"Tester l’imprimante",hint:"Ce que le superviseur a vu il y a moins d’une seconde.",get busy(){return e(N)},get disabled(){return e(j)},onrun:()=>{K("printer",nn)}})}var V=n(h,2);{let N=u(()=>e(m)==="label");Ht(V,{label:"Imprimer une étiquette de test",hint:"Une étiquette de démonstration sort de l’imprimante du poste.",get busy(){return e(N)},get disabled(){return e(j)},onrun:()=>{z("label",sn)}})}var X=n(V,2);{let N=u(()=>e(m)==="reprint");Ht(X,{label:"Réimprimer la dernière",hint:"La dernière étiquette imprimée sort une seconde fois.",get busy(){return e(N)},get disabled(){return e(j)},onrun:()=>{z("reprint",Qr)}})}var ae=n(X,2);{let N=u(()=>e(m)==="reload");Ht(ae,{label:"Recharger le catalogue",kind:"write",hint:"La veille refait tout de suite le contrôle qu’elle fait toutes les cinq secondes.",get busy(){return e(N)},get disabled(){return e(j)},onrun:()=>{R()}})}var M=n(ae,2);{let N=u(()=>e(d)?"Revenir à la balance":"Basculer en saisie manuelle"),Q=u(()=>e(d)?"Le poids sera de nouveau lu sur la balance.":"Le poids se tape à la main : le poste continue de servir sans balance."),re=u(()=>e(m)==="manual");Ht(M,{get label(){return e(N)},kind:"destructive",get hint(){return e(Q)},get engaged(){return e(d)},protected:!0,get busy(){return e(re)},get disabled(){return e(j)},onrun:()=>{F("manual",()=>en(!e(d)))}})}var b=n(M,2);{let N=u(()=>e(m)==="roll");Ht(b,{label:"J’ai changé le rouleau",kind:"write",hint:"Le compteur d’étiquettes repart à zéro. C’est le seul geste qui dise quelque chose de vrai du papier.",get busy(){return e(N)},get disabled(){return e(j)},onrun:()=>{z("roll",tn)}})}var I=n(b,2);{var Y=N=>{{let Q=u(()=>e(k)?"Revenir à l’imprimante du poste":"Imprimer sur l’imprimante du poste voisin"),re=u(()=>e(k)?"Les étiquettes repartiront sur l’imprimante de ce poste.":"Les étiquettes sortiront sur l’imprimante voisine, pour cette session seulement."),Z=u(()=>e(m)==="fallback");Ht(N,{get label(){return e(Q)},kind:"write",get hint(){return e(re)},get engaged(){return e(k)},get busy(){return e(Z)},get disabled(){return e(j)},onrun:()=>{z("fallback",()=>an(!e(k)))}})}};_(I,N=>{e(p)!==null&&e(p).fallback_available&&N(Y)})}var ue=n(I,2),fe=n(O,2);ze(fe,{title:"Importer un catalogue",note:"Glissez le fichier CSV ici, ou choisissez-le. Il passe par le même chemin que le fichier du producteur.",children:(N,Q)=>{var re=pc();let Z;var ie=s(re),we=n(s(ie)),Fe=s(we),x=n(ie,2),se=n(s(x));f(()=>{Z=Mt(re,1,"drop svelte-1yd0nzg",null,Z,{dropping:e(T)}),o(Fe,`flv_${t.health.station??""}.csv`)}),Kt("dragover",re,ve=>{ve.preventDefault(),v(T,!0)}),Kt("dragleave",re,()=>v(T,!1)),Kt("drop",re,ve=>{ve.preventDefault(),E(ve.dataTransfer?.files.item(0))}),Ce("change",se,ve=>{E(ve.currentTarget.files?.item(0))}),l(N,re)},$$slots:{default:!0}});var pe=n(fe,2);{var ce=N=>{ze(N,{title:"Le dernier fichier n’a pas pris service",children:(Q,re)=>{var Z=fc(),ie=te(Z),we=s(ie),Fe=n(ie,2),x=s(Fe);f((se,ve)=>{o(we,`${se??""} — - ${(e(L).reason===""?"aucun motif enregistré.":e(L).reason)??""} - Le catalogue en service n’a pas changé.`),o(x,`Dernier essai : ${ve??""}.`)},[()=>ha(e(L).result),()=>Pt(e(L).occurred_at)]),l(Q,Z)},$$slots:{default:!0}})};_(pe,N=>{e(L)!==null&&N(ce)})}f(()=>{o(S,e(D)),he(ue,"href",ln)}),l(a,w),We()}ct(["change"]);const hc={succeeded:"La dernière mise à jour a réussi.","rolled-back":"La dernière mise à jour a échoué. La version précédente a été remise et le poste fonctionne.","rolled-back-unhealthy":"La dernière mise à jour a échoué et le poste n’a pas redémarré. Appelez le support.","not-started":"La dernière mise à jour n’a pas démarré : rien n’a été remplacé. Vous pouvez réessayer."},_c=2e3,bc=300*1e3;var wc=c('

                                    '),lr=c('

                                    '),yc=c("
                                    Dernière vérification
                                    ",1),xc=c(`

                                    La mise à jour depuis cet écran n’existe que sur les postes Windows. Sur les - autres, elle se fait à la main — voir la notice d’installation.

                                    `),kc=c('Voir les nouveautés'),qc=c('

                                    Version disponible : .

                                    '),Sc=c('

                                    Ce poste est à jour.

                                    '),Lc=c("
                                    Version installée
                                    Dépôt suivi
                                    ",1),Ec=c(`

                                    Le poste va s’arrêter environ une minute. L’écran client s’éteindra puis reviendra - tout seul. Si la nouvelle version ne démarre pas, la précédente sera remise - automatiquement — mais les données enregistrées, elles, ne reviendront pas en - arrière.

                                    `),Cc=c('

                                    Mise à jour en cours. Le poste redémarre, ne le débranchez pas.

                                    '),Pc=c('
                                    ',1),Tc=c("
                                    Terminée le
                                    ",1),zc=c("
                                    Raison
                                    ",1),jc=c('

                                    Depuis
                                    Vers
                                    ',1),Rc=c(" ",1);function Oc(a,t){Me(t,!0);let r=G(null),i=G(""),d=G(""),p=G(!1),k=G("");const T=u(()=>e(r)?.outcome??null),m=u(()=>e(r)?.supported===!0&&e(r).available),j=u(()=>e(r)?.latest??"");qt(()=>{q()});async function q(){try{v(r,await Rn(),!0),v(i,"")}catch(g){v(i,g instanceof Error?g.message:"Lecture impossible.",!0)}}async function D(){v(d,"check");try{const g=await t.admin.protect(()=>On());g!==null&&(v(r,g,!0),v(i,""))}finally{v(d,"")}}async function L(g){v(d,"apply"),v(k,"");try{if(await t.admin.protect(()=>An(g))===null)return;v(p,!0),v(k,await W()?"":"Le poste n’a pas répondu dans les cinq minutes. Allez le voir.",!0),await q()}finally{v(d,""),v(p,!1)}}async function W(){const g=Date.now()+bc;for(;Date.now()setTimeout(U,_c));try{if((await fetch("/healthz",{cache:"no-store"})).ok)return!0}catch{}}return!1}var z=Rc(),R=te(z);ze(R,{title:"Version de ce poste",children:(g,U)=>{var B=Rt(),$=te(B);{var J=y=>{var S=wc(),O=s(S);f(()=>o(O,e(i)===""?"Lecture…":e(i))),l(y,S)},C=y=>{var S=Lc(),O=te(S);{var A=ce=>{var N=lr(),Q=s(N);f(()=>o(Q,e(i))),l(ce,N)};_(O,ce=>{e(i)!==""&&ce(A)})}var h=n(O,2),V=n(s(h),2),X=s(V),ae=n(V,4),M=s(ae),b=n(ae,2);{var I=ce=>{var N=yc(),Q=n(te(N),2),re=s(Q);f(Z=>o(re,Z),[()=>Pt(e(r).checked_at)]),l(ce,N)};_(b,ce=>{e(r).checked_at!==""&&ce(I)})}var Y=n(h,2);{var ue=ce=>{var N=xc();l(ce,N)},fe=ce=>{var N=qc(),Q=n(s(N)),re=s(Q),Z=n(Q,2);{var ie=x=>{var se=ot();f(ve=>o(se,`, publiée le ${ve??""}`),[()=>Pt(e(r).published_at)]),l(x,se)};_(Z,x=>{e(r).published_at!==""&&x(ie)})}var we=n(Z,2);{var Fe=x=>{var se=kc();f(()=>he(se,"href",e(r).html_url)),l(x,se)};_(we,x=>{e(r).html_url!==""&&x(Fe)})}f(()=>o(re,e(r).latest)),l(ce,N)},pe=ce=>{var N=Sc();l(ce,N)};_(Y,ce=>{e(r).supported?e(r).available?ce(fe,1):ce(pe,-1):ce(ue)})}f(()=>{o(X,e(r).running),o(M,e(r).repository)}),l(y,S)};_($,y=>{e(r)===null?y(J):y(C,-1)})}l(g,B)},$$slots:{default:!0}});var K=n(R,2);{var F=g=>{ze(g,{title:"Installer",children:(U,B)=>{var $=Pc(),J=te($),C=s(J);{let M=u(()=>e(d)==="check");Pe(C,{kind:"read",label:"Vérifier maintenant",protected:!0,act:"check",get busy(){return e(M)},get disabled(){return e(p)},onrun:()=>{D()}})}var y=n(C,2);{var S=M=>{{let b=u(()=>`Installer la version ${e(j)}`),I=u(()=>e(d)==="apply");Pe(M,{kind:"destructive",get label(){return e(b)},protected:!0,act:"apply",get busy(){return e(I)},onrun:()=>{L(e(j))}})}};_(y,M=>{e(m)&&M(S)})}var O=n(J,2);{var A=M=>{var b=Ec();l(M,b)};_(O,M=>{e(m)&&M(A)})}var h=n(O,2);{var V=M=>{var b=Cc();l(M,b)};_(h,M=>{e(p)&&M(V)})}var X=n(h,2);{var ae=M=>{var b=lr(),I=s(b);f(()=>o(I,e(k))),l(M,b)};_(X,M=>{e(k)!==""&&M(ae)})}l(U,$)},$$slots:{default:!0}})};_(K,g=>{e(r)!==null&&e(r).supported&&g(F)})}var E=n(K,2);{var w=g=>{ze(g,{title:"Dernière tentative",children:(U,B)=>{var $=jc(),J=te($),C=s(J),y=n(J,2),S=n(s(y),2),O=s(S),A=n(S,4),h=s(A),V=n(A,2);{var X=b=>{var I=Tc(),Y=n(te(I),2),ue=s(Y);f(fe=>o(ue,fe),[()=>Pt(e(T).finished_at)]),l(b,I)};_(V,b=>{e(T).finished_at!==""&&b(X)})}var ae=n(V,2);{var M=b=>{var I=zc(),Y=n(te(I),2),ue=s(Y);f(()=>o(ue,e(T).reason)),l(b,I)};_(ae,b=>{e(T).reason!==""&&b(M)})}f(()=>{he(J,"data-outcome",e(T).status),o(C,hc[e(T).status]),o(O,e(T).from===""?"—":e(T).from),o(h,e(T).to===""?"—":e(T).to)}),l(U,$)},$$slots:{default:!0}})};_(E,g=>{e(T)!==null&&g(w)})}l(a,z),We()}var Ac=c(''),Nc=c('

                                    ',1),Ic=c('

                                    ',1),Dc=c(''),Fc=c(''),Uc=c(''),Mc=c(''),Wc=c(' '),Bc=c(''),Vc=c('

                                    Lecture de l’état du poste…

                                    '),Gc=c('

                                    '),Hc=c(" "),Jc=c("
                                  • "),Kc=c('
                                      '),$c=c('
                                      '),Yc=c('

                                      ');function Xc(a,t){Me(t,!0);const r=new Kn,i=new Nn(r),d=[{title:"Au quotidien",pages:[{id:"dashboard",label:"Tableau de bord"},{id:"troubleshooting",label:"Dépannage"}]},{title:"Réglages",pages:[{id:"hardware",label:"Matériel"},{id:"label",label:"Étiquette"},{id:"rules",label:"Règles"},{id:"catalog",label:"Catalogue"},{id:"journal",label:"Journal"},{id:"station",label:"Poste"},{id:"update",label:"Mise à jour"}]}],p=u(()=>d.flatMap(x=>x.pages).find(x=>x.id===r.page)?.label??""),k=u(()=>r.health===null||r.health.station_name===""?`Poste ${String(r.health?.station??"")}`:r.health.station_name),T=u(()=>(i.pending?.changed_blocks??[]).map(x=>Un(x)).join(", ")),m=u(()=>(i.pending?.changed_blocks??[]).join(", "));Ta(()=>(r.start(),()=>r.stop()));async function j(x){r.open(x),Wa(x)&&i.config===null&&await i.load()}async function q(){await r.protect(()=>i.save())===!0&&(r.notice="La configuration est enregistrée et appliquée.")}var D=Yc(),L=s(D),W=n(s(L),2);Te(W,17,()=>d,x=>x.title,(x,se)=>{var ve=Nc(),oe=te(ve),le=s(oe),je=n(oe,2);Te(je,17,()=>e(se).pages,qe=>qe.id,(qe,Se)=>{var Ne=Ac();let Ve;var ye=s(Ne);f(()=>{Ve=Mt(Ne,1,"entry svelte-9b2mjq",null,Ve,{current:r.page===e(Se).id}),he(Ne,"aria-current",r.page===e(Se).id?"page":void 0),o(ye,e(Se).label)}),Ce("click",Ne,()=>{j(e(Se).id)}),l(qe,Ne)}),f(()=>o(le,e(se).title)),l(x,ve)});var z=n(W,2),R=s(z);{var K=x=>{var se=Ic(),ve=te(se),oe=s(ve),le=n(ve,2),je=s(le),qe=n(le,2),Se=s(qe);f(()=>{o(oe,e(k)),o(je,`${r.health.coop??""} · version ${r.health.version??""}`),o(Se,`configuration ${r.health.config_fingerprint??""}`)}),l(x,se)};_(R,x=>{r.health!==null&&x(K)})}var F=n(R,2),E=s(F),w=n(F,2);{var g=x=>{var se=Dc();Ce("click",se,function(...ve){t.onclose?.apply(this,ve)}),l(x,se)};_(w,x=>{t.onclose!==void 0&&x(g)})}var U=n(L,2),B=s(U);{var $=x=>{var se=Fc(),ve=s(se);f(()=>o(ve,r.notice)),l(x,se)};_(B,x=>{r.notice!==""&&x($)})}var J=n(B,2);{var C=x=>{var se=Uc(),ve=s(se);f(()=>o(ve,r.linkError)),l(x,se)};_(J,x=>{r.linkError!==""&&x(C)})}var y=n(J,2);{var S=x=>{var se=Mc(),ve=s(se);f(()=>o(ve,r.actionError)),l(x,se)};_(y,x=>{r.actionError!==""&&x(S)})}var O=n(y,2);{var A=x=>{var se=Bc(),ve=s(se),oe=s(ve),le=n(oe);{var je=Ne=>{var Ve=Wc(),ye=s(Ve);f(()=>o(ye,e(m))),l(Ne,Ve)};_(le,Ne=>{jt.showTechnicalNames&&Ne(je)})}var qe=n(le),Se=n(ve,2);Pe(Se,{kind:"write",label:"Tout fonctionne : confirmer",onrun:()=>{r.protect(()=>i.confirm())}}),f(()=>{o(oe,`Configuration appliquée mais NON CONFIRMÉE. Ce qui a changé : ${e(T)??""}. `),o(qe,` Le poste reviendra tout seul à la version précédente dans - ${i.pending.seconds_left??""} secondes si personne ne confirme.`)}),l(x,se)};_(O,x=>{i.pending!==null&&x(A)})}var h=n(O,2),V=s(h),X=s(V),ae=n(V,2);{var M=x=>{var se=Vc();l(x,se)},b=x=>{pi(x,{get health(){return r.health},onshowrows:()=>{j("catalog")},onshowupdate:()=>{j("update")}})},I=x=>{mc(x,{get admin(){return r},get health(){return r.health}})},Y=x=>{Ji(x,{get admin(){return r},get draft(){return i},get health(){return r.health}})},ue=x=>{zo(x,{get admin(){return r},get draft(){return i}})},fe=x=>{pu(x,{get draft(){return i},get health(){return r.health}})},pe=x=>{El(x,{get admin(){return r},get draft(){return i},get health(){return r.health}})},ce=x=>{bo(x,{get admin(){return r}})},N=x=>{sc(x,{get admin(){return r},get draft(){return i},get health(){return r.health}})},Q=x=>{Oc(x,{get admin(){return r}})};_(ae,x=>{r.health===null?x(M):r.page==="dashboard"?x(b,1):r.page==="troubleshooting"?x(I,2):r.page==="hardware"?x(Y,3):r.page==="label"?x(ue,4):r.page==="rules"?x(fe,5):r.page==="catalog"?x(pe,6):r.page==="journal"?x(ce,7):r.page==="station"?x(N,8):r.page==="update"&&x(Q,9)})}var re=n(h,2);{var Z=x=>{var se=$c(),ve=s(se);{var oe=Se=>{var Ne=Gc(),Ve=s(Ne),ye=n(Ve);Te(ye,16,()=>i.retired,Re=>Re,(Re,lt)=>{{let it=u(()=>`retirer ${lt}`);Pe(Re,{kind:"write",get label(){return e(it)},onrun:()=>i.dropRetired(lt)})}}),f(Re=>o(Ve,`Ce fichier porte des réglages que cette version du poste ne connaît plus : - ${Re??""}. `),[()=>i.retired.map(Re=>Ct(Re)).join(", ")]),l(Se,Ne)};_(ve,Se=>{i.retired.length>0&&Se(oe)})}var le=n(ve,2);{var je=Se=>{var Ne=Kc();Te(Ne,21,()=>i.faults,Ve=>Ve.field,(Ve,ye)=>{var Re=Jc(),lt=s(Re),it=s(lt),gt=n(lt,2);{var ht=Ze=>{var me=Hc(),Le=s(me);f(()=>o(Le,e(ye).field)),l(Ze,me)};_(gt,Ze=>{jt.showTechnicalNames&&Ze(ht)})}var dt=n(gt),mt=n(dt);{var yt=Ze=>{var me=ot();f(Le=>o(me,`— valeurs acceptées : ${Le??""}`),[()=>e(ye).allowed.join(", ")]),l(Ze,me)};_(mt,Ze=>{e(ye).allowed!==void 0&&e(ye).allowed.length>0&&Ze(yt)})}f(Ze=>{o(it,Ze),o(dt,` ${e(ye).message??""} `)},[()=>Ct(e(ye).field)]),l(Ve,Re)}),l(Se,Ne)};_(le,Se=>{i.faults.length>0&&Se(je)})}var qe=n(le,2);{let Se=u(()=>i.dirty?"Enregistrer la configuration":"Aucune modification à enregistrer"),Ne=u(()=>!i.dirty||r.busy);Pe(qe,{kind:"write",get label(){return e(Se)},get disabled(){return e(Ne)},onrun:()=>{q()}})}l(x,se)},ie=u(()=>Wa(r.page)&&i.config!==null);_(re,x=>{e(ie)&&x(Z)})}var we=n(U,2);{var Fe=x=>{ts(x,{get admin(){return r}})};_(we,x=>{r.pending!==null&&x(Fe)})}f(()=>{ta(E,jt.showTechnicalNames),o(X,e(p))}),Ce("change",E,()=>jt.toggleTechnicalNames()),l(a,D),We()}ct(["click","change"]);let oa=null;function ed(a){if(oa!==null||a.querySelector("[data-admin]")!==null)return;const t=document.createElement("div");a.appendChild(t),oa={component:Gr(Xc,{target:t,props:{onclose:Qc}}),host:t}}function Qc(){if(oa===null)return;const{component:a,host:t}=oa;oa=null,Hr(a),t.remove()}export{Qc as closeAdmin,ed as mountAdmin}; diff --git a/internal/web/dist/assets/mount-zw3SIkYn.js b/internal/web/dist/assets/mount-zw3SIkYn.js new file mode 100644 index 0000000..64b38b9 --- /dev/null +++ b/internal/web/dist/assets/mount-zw3SIkYn.js @@ -0,0 +1,102 @@ +import{Y as Tr,Z as zr,_ as jr,$ as Ea,a0 as Vt,e as Rr,a1 as or,a2 as Or,a3 as Ar,a4 as Nr,u as ma,a5 as Ca,a6 as Ir,a7 as ur,x as wt,A as B,h as e,z as v,H as vt,d as ot,a as Be,s as n,i as _,t as f,l as Nt,j as be,m as o,b as Ce,c as l,o as s,g as c,p as Ue,I as Dr,f as Me,n as u,q as ae,a8 as Ia,r as ze,G as Gt,a9 as ea,aa as Tt,B as da,ab as lt,ac as Xt,ad as ka,C as Fr,J as cr,y as sa,T as Ur,ae as Ht,F as Mr,E as Wr,D as Br,O as Vr,K as Gr,X as Hr,af as Jr}from"./app-BaRSXv80.js";function dr(a){throw new Error("https://svelte.dev/e/lifecycle_outside_component")}function Kr(a,t,...r){var i=new jr(a);Tr(()=>{const d=t()??null;i.ensure(d,d&&(p=>d(p,...r)))},zr)}function Pa(a,t,r=!1){if(a.multiple){if(t==null)return;if(!Or(t))return Ar();for(var i of a.options)i.selected=t.includes(la(i));return}for(i of a.options){var d=la(i);if(Nr(d,t)){i.selected=!0;return}}(!r||t!==void 0)&&(a.selectedIndex=-1)}function vr(a){var t=new MutationObserver(()=>{"__value"in a&&Pa(a,a.__value)});t.observe(a,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["value"]}),or(()=>{t.disconnect()})}function $r(a,t,r=t){var i=new WeakSet,d=!0;Ea(a,"change",p=>{var y=p?"[selected]":":checked",z;if(a.multiple)z=[].map.call(a.querySelectorAll(y),la);else{var m=a.querySelector(y)??a.querySelector("option:not([disabled])");z=m&&la(m)}r(z),a.__value=z,Vt!==null&&i.add(Vt)}),Rr(()=>{var p=t();if(a===document.activeElement){var y=Vt;if(i.has(y))return}if(Pa(a,p,d),d&&p===void 0){var z=a.querySelector(":checked");z!==null&&(p=la(z),r(p))}a.__value=p,d=!1}),vr(a)}function la(a){return"__value"in a?a.__value:a.value}function ia(a,t,r=t){var i=new WeakSet;Ea(a,"input",async d=>{var p=d?a.defaultValue:a.value;if(p=ba(a)?wa(p):p,r(p),Vt!==null&&i.add(Vt),await Ir(),p!==(p=t())){var y=a.selectionStart,z=a.selectionEnd,m=a.value.length;if(a.value=p??"",z!==null){var j=a.value.length;y===z&&z===m&&j>m?(a.selectionStart=j,a.selectionEnd=j):(a.selectionStart=y,a.selectionEnd=Math.min(z,j))}}}),ma(t)==null&&a.value&&(r(ba(a)?wa(a.value):a.value),Vt!==null&&i.add(Vt)),Ca(()=>{var d=t();if(a===document.activeElement){var p=Vt;if(i.has(p))return}ba(a)&&d===wa(a.value)||a.type==="date"&&!d&&!a.value||d!==a.value&&(a.value=d??"")})}function Da(a,t,r=t){Ea(a,"change",i=>{var d=i?a.defaultChecked:a.checked;r(d)}),ma(t)==null&&r(a.checked),Ca(()=>{var i=t();a.checked=!!i})}function ba(a){var t=a.type;return t==="number"||t==="range"}function wa(a){return a===""?null:+a}function Fa(a,t,r,i,d){var p=()=>{i(r[a])};r.addEventListener(t,p),d?Ca(()=>{r[a]=d()}):p(),(r===document.body||r===window||r===document)&&or(()=>{r.removeEventListener(t,p)})}function Ta(a){ur===null&&dr(),wt(()=>{const t=ma(a);if(typeof t=="function")return t})}function Yr(a){ur===null&&dr(),Ta(()=>()=>ma(a))}const Xr="ERR-CFG-02";class Pt extends Error{constructor(t,r,i="",d=[]){super(r),this.status=t,this.code=i,this.faults=d,this.name="AdminError"}status;code;faults;get needsPassword(){return this.status===401}get needsFirstPassword(){return this.status===409&&this.code===Xr}get needsCredentials(){return this.needsPassword||this.needsFirstPassword}}function fa(a){return a instanceof Pt&&a.needsCredentials}function Qr(){return It("/admin/api/health")}function Zr(){return He("/admin/api/troubleshooting/reprint",{})}function en(){return He("/admin/api/troubleshooting/reload-catalog",{})}function tn(a){return He("/admin/api/troubleshooting/manual-entry",{on:a})}function an(){return He("/admin/api/troubleshooting/roll-changed",{})}function rn(a){return He("/admin/api/troubleshooting/fallback-printer",{on:a})}function nn(){return He("/admin/api/troubleshooting/test-scale",{})}function sn(){return He("/admin/api/troubleshooting/test-printer",{})}function ln(){return He("/admin/api/troubleshooting/test-label",{})}async function pr(a){const t=new FormData;t.append("file",a,a.name);const r=await fetch("/admin/api/catalog/import",{method:"POST",body:t});return Ra(r,"POST /admin/api/catalog/import")}const on="/admin/api/diagnostic.zip";function un(a){return He("/admin/api/session",{password:a})}async function cn(){await fetch("/admin/api/session",{method:"DELETE"})}function dn(a,t){return He("/admin/api/session/recovery",{code:a,password:t})}function za(){return It("/admin/api/config")}function vn(a){return ja("PUT","/admin/api/config",a)}function pn(){return He("/admin/api/config/reload",{})}function fn(){return He("/admin/api/restart",{})}function gn(){return He("/admin/api/reboot",{})}function mn(){return ja("DELETE","/admin/api/reboot",{})}function hn(){return He("/admin/api/config/confirm",{})}async function _n(){return(await It("/admin/api/config/versions")).versions}function bn(a){return He("/admin/api/config/restore",{version:a})}async function wn(){return(await It("/admin/api/ports")).ports}async function yn(){return(await It("/admin/api/printers")).printers}async function xn(){return(await He("/admin/api/printers/discover",{})).printers}function kn(a){return He("/admin/api/scale/detect",{port:a})}async function Ua(a,t){return(await He("/admin/api/scale/capture",{port:a,seconds:t})).frames}function fr(a){return He("/admin/api/printer/test?what="+a,{})}function qn(a,t,r,i){const d=new URLSearchParams({template:a,demo:t?"1":"0",dual:r?"1":"0"});return d.set("t",String(i)),"/admin/api/label/preview.png?"+d.toString()}async function Sn(a){const t=new URLSearchParams(a);return(await It("/admin/api/journal?"+t)).weighings}function Ln(a){return"/admin/api/journal/export.csv?"+new URLSearchParams(a).toString()}async function En(a){const t=new URLSearchParams(a);return(await It("/admin/api/technical?"+t)).entries}function Cn(a){const t=a===void 0?"":"?id="+String(a);return It("/admin/api/imports"+t)}function Pn(){return He("/admin/api/catalog/reload",{})}function Tn(){return He("/admin/api/catalog/forget-quarantine",{})}function Ma(a,t){return He("/admin/api/products/"+encodeURIComponent(a)+"/decision",t)}function zn(a){return He("/admin/api/replay",{frame:a})}async function It(a){const t=await fetch(a,{headers:{accept:"application/json"}});return Ra(t,"GET "+a)}function He(a,t){return ja("POST",a,t)}async function ja(a,t,r){const i=await fetch(t,{method:a,headers:{"content-type":"application/json"},body:JSON.stringify(r)});return Ra(i,a+" "+t)}async function Ra(a,t){const r=await a.text();if(!a.ok){const i=gr(r);throw new Pt(a.status,Rn(r,t,a.status)+jn(a),i?.code??"",i?.faults??[])}if(r!=="")return JSON.parse(r)}function jn(a){if(a.status!==429)return"";const t=Number(a.headers.get("Retry-After")??"");if(!Number.isFinite(t)||t<=0)return"";const r=Math.ceil(t/60);return r>1?` Réessayez dans ${String(r)} minutes.`:" Réessayez dans une minute."}function Rn(a,t,r){const i=gr(a);return i?.message!==void 0&&i.message!==""?i.message:`${t} a répondu ${String(r)}.`}function gr(a){try{return JSON.parse(a)}catch{return null}}function On(){return It("/admin/api/update")}function An(){return He("/admin/api/update/check",{})}function Nn(a){return He("/admin/api/update/apply",{version:a})}class In{constructor(t){this.admin=t}admin;#t=B(null);get config(){return e(this.#t)}set config(t){v(this.#t,t,!0)}#r=B("");get fingerprint(){return e(this.#r)}set fingerprint(t){v(this.#r,t,!0)}#a=B(vt([]));get retired(){return e(this.#a)}set retired(t){v(this.#a,t,!0)}#e=B(null);get pending(){return e(this.#e)}set pending(t){v(this.#e,t,!0)}#n=B(vt([]));get faults(){return e(this.#n)}set faults(t){v(this.#n,t,!0)}#l=B(!1);get dirty(){return e(this.#l)}set dirty(t){v(this.#l,t,!0)}async load(){const t=await this.admin.load(()=>za());t!==null&&(this.config=t.config,this.fingerprint=t.config_fingerprint,this.retired=t.retired_keys??[],this.pending=t.pending_confirmation,this.faults=[],this.dirty=!1)}value(t){let r=this.config;for(const i of t.split(".")){if(r===null||typeof r!="object")return;r=r[i]}return r}text(t){const r=this.value(t);return r==null?"":String(r)}number(t){const r=this.value(t);return typeof r=="number"?r:Number(r??0)}flag(t){return this.value(t)===!0}set(t,r){if(this.config===null)return;const i=t.split("."),d=i.pop();if(d===void 0)return;let p=this.config;for(const y of i){const z=p[y];(z===null||typeof z!="object")&&(p[y]={}),p=p[y]}p[d]=r,this.dirty=!0}unset(t){const r=t.split("."),i=r.pop();if(i===void 0||this.config===null)return!1;let d=this.config;for(const p of r){if(d===null||typeof d!="object")return!1;d=d[p]}return d===null||typeof d!="object"?!1:(delete d[i],this.dirty=!0,!0)}async save(){if(this.config===null)return!1;this.faults=[];const t=this.config;let r;try{r=await vn(t)}catch(i){if(fa(i))throw i;return this.admin.report(i),this.faults=Dn(this.admin),!1}return this.config=r.config,this.fingerprint=r.config_fingerprint,this.retired=r.retired_keys??[],this.pending=r.pending_confirmation,this.dirty=!1,!0}async confirm(){try{await hn()}catch(t){if(fa(t))throw t;this.admin.report(t);return}this.admin.notice="La configuration est confirmée.",this.pending=null,await this.admin.refresh()}dropRetired(t){if(t.includes("[")){this.admin.actionError=`« ${t} » est logée dans un tableau : cet écran ne sait pas la retirer de là. Modifiez le fichier de configuration lui-même.`;return}this.unset(t)&&(this.retired=this.retired.filter(r=>r!==t))}}function Dn(a){return a.lastFaults}const Fn={"station.number":"Numéro du poste","station.name":"Nom du poste","station.coop":"Nom de la coopérative","network.listen":"Adresse d’écoute","network.admin_on_lan":"Administration accessible depuis le réseau","ui.language":"Langue","ui.sound":"Son","ui.idle_timeout_s":"Retour à l’accueil après (secondes)","ui.reprint_window_s":"Réimpression possible pendant (secondes)","ui.show_grid_prices":"Afficher les prix sur les tuiles","ui.show_by_unit_products":"Afficher les produits vendus à l’unité","ui.grid_columns":"Colonnes de la grille","ui.min_products_for_chip":"Produits minimum pour afficher une catégorie","scale.type":"Protocole de la balance","scale.present":"Ce poste a une balance","scale.manual_entry_allowed":"Saisie manuelle autorisée","scale.degrade_after_s":"Passer en mode dégradé après (secondes)","scale.options.port":"Port série","scale.options.baud":"Vitesse (bauds)","scale.options.bits":"Bits de données","scale.options.parity":"Parité","scale.options.stop":"Bits d’arrêt","scale.options.backoff_min_ms":"Attente minimale avant réessai (ms)","scale.options.backoff_max_ms":"Attente maximale avant réessai (ms)","printer.type":"Pilote d’impression","printer.template":"Gabarit d’étiquette","printer.options.transport":"Transport","printer.options.queue":"File d’impression","printer.options.path":"Fichier de périphérique","printer.options.address":"Adresse réseau","printer.options.darkness":"Noircissement","printer.options.speed":"Vitesse d’impression","printer.options.offset_x":"Décalage horizontal (dots)","printer.options.offset_y":"Décalage vertical (dots)","printer.options.invert_bits":"Inverser les points","printer.options.copies":"Exemplaires","printer.options.roll_capacity":"Étiquettes par rouleau","pricing.amount_rounding":"Arrondi du montant","pricing.unit_price_rounding":"Arrondi du prix au kilo","pricing.primary_code":"Tarif principal","pricing.reference_code":"Tarif de référence","barcode.verify_reference_check_digit":"Vérifier la clé de contrôle","limits.empty_max_g":"Plateau considéré vide en dessous de (g)","limits.basket_check_enabled":"Vérifier la présence du panier","limits.basket_min_g":"Poids du panier, borne basse (g)","limits.basket_max_g":"Poids du panier, borne haute (g)","limits.min_weight_g":"Poids minimum accepté","limits.max_weight_g":"Poids maximum accepté","limits.max_tare_g":"Tare maximale","limits.min_units":"Unités minimum","limits.max_units":"Unités maximum","limits.max_amount_cents":"Montant maximum (centimes)","stability.mode":"Exigence de stabilité","stability.min_duration_ms":"Durée de stabilité (ms)","stability.tolerance_g":"Tolérance de stabilité (g)","stability.timeout_ms":"Délai d’attente de stabilité (ms)","stability.on_timeout":"Au bout du délai","stability.min_latch_rate":"Taux d’accroche minimal","stability.latch_rate_window_ms":"Fenêtre de mesure du taux (ms)","stability.expiry_floor_ms":"Péremption, plancher (ms)","stability.expiry_ceiling_ms":"Péremption, plafond (ms)","stability.expiry_factor":"Péremption, facteur","catalog.type":"Où le poste va chercher le catalogue","catalog.options.directory":"Répertoire surveillé","catalog.options.url":"Adresse du serveur","catalog.options.username":"Compte","catalog.options.password":"Mot de passe","catalog.options.separator":"Séparateur du CSV","catalog.options.poll_interval_s":"Vérifier toutes les (secondes)","catalog.options.stable_polls":"Vérifications avant lecture","catalog.options.max_file_size_mb":"Taille maximale du fichier (Mo)","catalog.options.max_image_size_kb":"Taille maximale d’une image (Ko)","catalog.options.min_readable_ratio":"Part minimale de lignes lisibles","catalog.options.max_weighable_drop":"Baisse maximale des produits pesables","catalog.options.max_archives":"Archives conservées","catalog.options.archive_days":"Archives conservées (jours)","catalog.options.failures_before_reject":"Échecs avant mise en quarantaine","catalog.images.source":"Origine des photos","catalog.images.path":"Répertoire des photos","catalog.fallback_category":"Rayon par défaut","journal.max_rows":"Pesées conservées","journal.max_days":"Pesées conservées (jours)","journal.max_technical":"Événements techniques conservés","admin.session_minutes":"Durée d’une session (minutes)","admin.attempts_per_minute":"Tentatives par minute","maintenance.weekly_integrity_check":"Contrôle d’intégrité hebdomadaire","maintenance.disk_alert_mb":"Alerte disque en dessous de (Mo)"};function qt(a){return Fn[a]??a}const Un={station:"l’identité du poste",network:"le réseau",ui:"l’écran client",scale:"la balance",printer:"l’imprimante",pricing:"les tarifs",barcode:"le code-barres",limits:"les garde-fous",stability:"la stabilité",catalog:"le catalogue",journal:"le journal",maintenance:"la maintenance"};function Mn(a){return Un[a]??a}const Wn={scale:"balance",printer:"imprimante",catalog:"catalogue",ui:"écran",config:"configuration",http:"réseau",system:"système"};function mr(a){return Wn[a]??"origine inconnue"}const qa="openscale.admin.preferences",hr="technical";class Bn{#t=B(vt(Vn()));get showTechnicalNames(){return e(this.#t)}set showTechnicalNames(t){v(this.#t,t,!0)}toggleTechnicalNames(){this.showTechnicalNames=!this.showTechnicalNames,Gn(this.showTechnicalNames)}}function Vn(){try{return globalThis.localStorage?.getItem(qa)===hr}catch{return!1}}function Gn(a){try{a?globalThis.localStorage?.setItem(qa,hr):globalThis.localStorage?.removeItem(qa)}catch{}}const St=new Bn,Hn=3e3,Jn=["hardware","label","rules","catalog","journal","station","update"];function Wa(a){return Jn.includes(a)}function Kn(a){return a instanceof Pt&&a.needsCredentials}class $n{constructor(t=Hn){this.periodMs=t}periodMs;#t=B(null);get health(){return e(this.#t)}set health(t){v(this.#t,t,!0)}#r=B("dashboard");get page(){return e(this.#r)}set page(t){v(this.#r,t,!0)}#a=B(!1);get expert(){return e(this.#a)}set expert(t){v(this.#a,t,!0)}#e=B("");get linkError(){return e(this.#e)}set linkError(t){v(this.#e,t,!0)}#n=B("");get actionError(){return e(this.#n)}set actionError(t){v(this.#n,t,!0)}#l=B(!1);get needsFirstPassword(){return e(this.#l)}set needsFirstPassword(t){v(this.#l,t,!0)}#d=B("");get notice(){return e(this.#d)}set notice(t){v(this.#d,t,!0)}#v=B(!1);get busy(){return e(this.#v)}set busy(t){v(this.#v,t,!0)}#p=B(vt([]));get lastFaults(){return e(this.#p)}set lastFaults(t){v(this.#p,t,!0)}#f=B(null);get pending(){return e(this.#f)}set pending(t){v(this.#f,t,!0)}#i=null;#o=null;start(){this.refresh(),this.#i=setInterval(()=>{this.refresh()},this.periodMs)}stop(){this.#i!==null&&clearInterval(this.#i),this.#i=null}async refresh(){try{this.health=await Qr(),this.linkError=""}catch(t){this.linkError=Ba(t)}}#u(){this.busy=!0,this.notice="",this.actionError="",this.needsFirstPassword=!1}#s(t){this.actionError=Ba(t),this.needsFirstPassword=t instanceof Pt&&t.needsFirstPassword,this.#m(t)}async run(t){this.#u();let r=null;try{r=await t(),this.notice=r.message}catch(i){this.#s(i)}finally{this.busy=!1}return await this.refresh(),r}async login(t){this.#u();try{return await un(t),this.expert=!0,this.notice="Session d’administration ouverte.",!0}catch(r){return this.expert=!1,this.#s(r),!1}finally{this.busy=!1}}async recover(t,r){this.#u();try{const i=await dn(t,r);return this.expert=!0,this.notice=i.warning??"Le mot de passe est remplacé et la session est ouverte.",!0}catch(i){return this.#s(i),!1}finally{this.busy=!1}}async protect(t){try{return await t()}catch(r){if(!Kn(r))return this.#s(r),null;if(!await this.#g(r))return null;try{return await t()}catch(i){return this.#s(i),null}}}#g(t){return this.pending={kind:t.needsFirstPassword?"first-password":"password",message:t.message},new Promise(r=>{this.#o=r})}async answerPassword(t){await this.login(t)&&this.#c(!0)}async answerRecovery(t,r){await this.recover(t,r)&&this.#c(!0)}cancelPassword(){this.#c(!1)}#c(t){this.pending=null,this.notice="";const r=this.#o;this.#o=null,r?.(t)}async logout(){await cn(),this.expert=!1,this.page="dashboard",this.notice="Session d’administration fermée."}open(t){this.actionError="",this.notice="",this.needsFirstPassword=!1,this.page=t}async load(t){try{const r=await t();return this.actionError="",this.lastFaults=[],r}catch(r){return this.lastFaults=r instanceof Pt?r.faults:[],this.#s(r),null}}report(t){this.lastFaults=t instanceof Pt?t.faults:[],this.#s(t)}#m(t){t instanceof Pt&&t.needsPassword&&(this.expert=!1)}}function Ba(a){return a instanceof Pt?a.message:a instanceof Error?"Le poste n’a pas répondu : "+a.message:"Le poste n’a pas répondu."}var Yn=c('clé'),Xn=c('');function Te(a,t){const r=Be(t,"kind",3,"read"),i=Be(t,"busy",3,!1),d=Be(t,"disabled",3,!1),p=Be(t,"protected",3,!1),y=Be(t,"act",3,void 0);var z=Xn();let m;var j=s(z),S=n(j);{var A=L=>{var W=Yn();l(L,W)};_(S,L=>{p()&&L(A)})}f(()=>{m=Nt(z,1,`act ${r()??""}`,"svelte-5tpq0o",m,{"touch-target":r()==="destructive",busy:i()}),be(z,"data-kind",r()),be(z,"data-act",y()),z.disabled=d()||i(),o(j,`${(i()?"En cours…":t.label)??""} `)}),Ce("click",z,function(...L){t.onrun?.apply(this,L)}),l(a,z)}ot(["click"]);var Qn=c(`

                                      Le code de secours a été tiré à l’installation de ce poste et imprimé + sur sa fiche, rangée dans le classeur du magasin. Il n’existe nulle part ailleurs.

                                      `,1),Zn=c(`

                                      Oublié ? Le code de secours de la fiche d’installation en repose un. + Un responsable peut aussi le faire en ligne de commande, avec openscale config password.

                                      `,1),es=c('

                                      '),ts=c('');function as(a,t){Ue(t,!0);const r=u(()=>t.admin.pending?.kind==="first-password");let i=B(""),d=B("");const p=8,y=u(()=>e(r)?e(d).trim().length>0&&e(i).length>=p:e(i).length>0);async function z(){!e(y)||t.admin.busy||(e(r)?await t.admin.answerRecovery(e(d),e(i)):await t.admin.answerPassword(e(i)),v(i,""),v(d,""))}var m=ts(),j=s(m),S=s(j),A=s(S),L=s(A);Dr(L,{name:"settings",size:"1.5rem"});var W=n(A,2),O=s(W),C=n(S,2),K=s(C),U=n(C,2);{var E=T=>{var w=Qn(),k=n(ae(w),2),R=n(s(k),2);Ia(R);var F=n(k,2),h=s(F);h.textContent="Nouveau mot de passe — 8 caractères au moins";var G=n(h,2);Ce("keydown",R,Y=>Y.key==="Enter"&&void z()),ia(R,()=>e(d),Y=>v(d,Y)),Ce("keydown",G,Y=>Y.key==="Enter"&&void z()),ia(G,()=>e(i),Y=>v(i,Y)),l(T,w)},x=T=>{var w=Zn(),k=ae(w),R=n(s(k),2);Ia(R),Ce("keydown",R,F=>F.key==="Enter"&&void z()),ia(R,()=>e(i),F=>v(i,F)),l(T,w)};_(U,T=>{e(r)?T(E):T(x,-1)})}var g=n(U,2);{var D=T=>{var w=es(),k=s(w);f(()=>o(k,t.admin.actionError)),l(T,w)};_(g,T=>{t.admin.actionError!==""&&T(D)})}var H=n(g,2),V=s(H);Te(V,{label:"Annuler",onrun:()=>t.admin.cancelPassword()});var $=n(V,2);{let T=u(()=>e(r)?"Poser ce mot de passe":"Continuer"),w=u(()=>!e(y)||t.admin.busy);Te($,{kind:"write",get label(){return e(T)},get disabled(){return e(w)},onrun:()=>{z()}})}f(()=>{o(O,e(r)?"Ce poste n’a pas encore de mot de passe":"Mot de passe d’administration"),o(K,t.admin.pending?.message??"")}),l(a,m),Me()}ot(["keydown"]);var rs=c(' '),ns=c(""),ss=c(''),ls=c(''),is=c(""),os=c(""),us=c('

                                      '),cs=c(" "),ds=c('

                                      '),vs=c('
                                      ');function at(a,t){Ue(t,!0);const r=Be(t,"hint",3,""),i=Be(t,"kind",3,"text"),d=Be(t,"disabled",3,!1),p=Be(t,"fault",3,""),y=Be(t,"allowed",19,()=>[]),z=Be(t,"choices",19,()=>[]),m=u(()=>`field-${t.path.replace(/\./gu,"-")}`);var j=vs();let S;var A=s(j),L=s(A),W=s(L),O=n(L,2);{var C=T=>{var w=rs(),k=s(w);f(()=>o(k,t.path)),l(T,w)};_(O,T=>{St.showTechnicalNames&&T(C)})}var K=n(A,2);{var U=T=>{var w=ss();ze(w,21,z,R=>R.value,(R,F)=>{var h=ns(),G=s(h),Y={};f(()=>{o(G,e(F).label),Y!==(Y=e(F).value)&&(h.value=(h.__value=e(F).value)??"")}),l(R,h)});var k;vr(w),f(()=>{be(w,"id",e(m)),w.disabled=d(),k!==(k=t.value)&&(w.value=(w.__value=t.value)??"",Pa(w,t.value))}),Ce("change",w,R=>t.onchange(R.currentTarget.value)),l(T,w)},E=T=>{var w=ls();f(()=>{be(w,"id",e(m)),be(w,"type",i()),Gt(w,t.value),w.disabled=d(),be(w,"list",y().length>0?e(m)+"-allowed":void 0)}),Ce("input",w,k=>t.onchange(k.currentTarget.value)),l(T,w)};_(K,T=>{z().length>0?T(U):T(E,-1)})}var x=n(K,2);{var g=T=>{var w=os();ze(w,20,y,k=>k,(k,R)=>{var F=is(),h={};f(()=>{h!==(h=R)&&(F.value=(F.__value=R)??"")}),l(k,F)}),f(()=>be(w,"id",e(m)+"-allowed")),l(T,w)};_(x,T=>{y().length>0&&T(g)})}var D=n(x,2);{var H=T=>{var w=us(),k=s(w);f(()=>o(k,r())),l(T,w)};_(D,T=>{r()!==""&&T(H)})}var V=n(D,2);{var $=T=>{var w=ds(),k=s(w),R=n(k);{var F=h=>{var G=cs(),Y=s(G);f(ne=>o(Y,`Valeurs acceptées : ${ne??""}.`),[()=>y().join(", ")]),l(h,G)};_(R,h=>{y().length>0&&h(F)})}f(()=>o(k,`${p()??""} `)),l(T,w)};_(V,T=>{p()!==""&&T($)})}f(()=>{S=Nt(j,1,"field svelte-1oilv5s",null,S,{refused:p()!==""}),be(A,"for",e(m)),o(W,t.label)}),l(a,j),Me()}ot(["change","input"]);var ps=c('

                                      '),fs=c('

                                      ');function Oe(a,t){const r=Be(t,"note",3,"");var i=fs(),d=s(i),p=s(d),y=n(d,2);{var z=j=>{var S=ps(),A=s(S);f(()=>o(A,r())),l(j,S)};_(y,j=>{r()!==""&&j(z)})}var m=n(y,2);Kr(m,()=>t.children),f(()=>o(p,t.title)),l(a,i)}function $e(a,t){return a.faults.find(r=>r.field===t)?.message??""}function Qt(a,t){return a.faults.find(r=>r.field===t)?.allowed??[]}var gs=c('

                                      Lecture des réglages du poste…

                                      '),ms=c(`

                                      Le poste y cherche le fichier , et le supprime + une fois lu : c’est ce qui dit au producteur que la livraison est prise.

                                      `,1),hs=c(`

                                      Sur un serveur WebDAV, le dépôt d’un fichier CSV depuis cet écran n’est plus + possible : le poste n’a plus de répertoire local où l’écrire. C’est le seul recours + du jour de la mise en service.

                                      `,1),_s=c(`

                                      Ce poste ne déclare aucune source : choisissez-en une ci-dessus, sinon il n’ira + chercher aucun catalogue.

                                      `),bs=c('
                                      ',1);function ws(a,t){Ue(t,!0);const r={local_drop:["catalog.options.directory"],webdav:["catalog.options.url","catalog.options.username","catalog.options.password"]},i=u(()=>t.draft.text("catalog.type"));function d(p){t.draft.set("catalog.type",p);for(const[y,z]of Object.entries(r))if(y!==p)for(const m of z)t.draft.unset(m)}Oe(a,{title:"Où le poste va chercher le catalogue",children:(p,y)=>{var z=bs(),m=ae(z),j=s(m),S=s(j),A=n(j,2),L=s(A),W=n(m,2);{var O=E=>{var x=gs();l(E,x)},C=E=>{var x=ms(),g=ae(x);{let $=u(()=>qt("catalog.options.directory")),T=u(()=>t.draft.text("catalog.options.directory")),w=u(()=>$e(t.draft,"catalog.options.directory"));at(g,{get label(){return e($)},path:"catalog.options.directory",get value(){return e(T)},hint:"Laissez vide pour le répertoire du poste, celui que le service crée lui-même. Un répertoire nommé ici doit exister : le poste ne le crée pas.",get fault(){return e(w)},onchange:k=>t.draft.set("catalog.options.directory",k)})}var D=n(g,2),H=n(s(D)),V=s(H);f(()=>o(V,`flv_${t.station??""}.csv`)),l(E,x)},K=E=>{var x=hs(),g=ae(x);{let V=u(()=>qt("catalog.options.url")),$=u(()=>t.draft.text("catalog.options.url")),T=u(()=>$e(t.draft,"catalog.options.url"));at(g,{get label(){return e(V)},path:"catalog.options.url",get value(){return e($)},get fault(){return e(T)},onchange:w=>t.draft.set("catalog.options.url",w)})}var D=n(g,2);{let V=u(()=>qt("catalog.options.username")),$=u(()=>t.draft.text("catalog.options.username")),T=u(()=>$e(t.draft,"catalog.options.username"));at(D,{get label(){return e(V)},path:"catalog.options.username",get value(){return e($)},get fault(){return e(T)},onchange:w=>t.draft.set("catalog.options.username",w)})}var H=n(D,2);{let V=u(()=>qt("catalog.options.password")),$=u(()=>$e(t.draft,"catalog.options.password"));at(H,{get label(){return e(V)},path:"catalog.options.password",kind:"password",value:"",hint:"Laissez vide : le mot de passe actuel est conservé.",get fault(){return e($)},onchange:T=>t.draft.set("catalog.options.password",T)})}l(E,x)},U=E=>{var x=_s();l(E,x)};_(W,E=>{t.draft.config===null?E(O):e(i)==="local_drop"?E(C,1):e(i)==="webdav"?E(K,2):E(U,-1)})}f(()=>{ea(S,e(i)==="local_drop"),ea(L,e(i)==="webdav")}),Ce("change",S,()=>d("local_drop")),Ce("change",L,()=>d("webdav")),l(p,z)},$$slots:{default:!0}}),Me()}ot(["change"]);const Va=1e3;function va(a){const t=Oa(a);return t===null?"":[Zt(t.getDate()),Zt(t.getMonth()+1),String(t.getFullYear())].join("/")}function Lt(a){const t=Oa(a);return t===null?"":`${va(a)} à ${Zt(t.getHours())}:${Zt(t.getMinutes())}`}function ys(a){const t=Oa(a);return t===null?"":[Zt(t.getHours()),Zt(t.getMinutes()),Zt(t.getSeconds())].join(":")}function Sa(a){const t=a/1e6;return t<1e3?`${Math.round(t)} Mo`:`${_r(t/1e3)} Go`}function se(a){const t=String(Math.abs(Math.trunc(a))),r=[];for(let i=t.length;i>0;i-=3)r.unshift(t.slice(Math.max(0,i-3),i));return(a<0?"-":"")+r.join(" ")}function ua(a){return a0}function qs(a){const t=a.motive!=="";return{canWithdraw:t||!pa(!1,a.waiverInForce),canOfferAgain:t||!pa(!0,a.waiverInForce),canSaveWaiver:ks(a.typedWaiver)&&(t||!pa(a.offeredInForce,a.typedWaiver)),canDropWaiver:t||!pa(a.offeredInForce,null)}}function br(a){const t=[];return a.offered||t.push("retiré de la grille"),a.min_weight_g!==null&&t.push(`peut peser à partir de ${se(a.min_weight_g)} g`),t.length===0?"aucune restriction":t.join(" · ")}function ca(a,t,r,i){const d=t>1?i:r;return a>=t?`${se(t)} ${d}.`:`${se(a)} lignes affichées sur ${se(t)} ${d}.`}function Ss(a,t){return t>a?`${se(a)} produits affichés sur ${se(t)} trouvés — précisez votre recherche.`:`${se(t)} ${t>1?"produits trouvés":"produit trouvé"}.`}function Ls(a){return`${se(a)} ${a>1?"imports affichés":"import affiché"} : le poste n’en publie jamais plus de vingt.`}function ya(a,t,r,i){const d=`${se(r)} ${r>1?t:a}`;return r<=i?d+".":`${d} — seules les ${se(i)} premières lignes sont affichées.`}function Es(a,t){const r=`${se(a)} ${a>1?"produits retirés":"produit retiré"}`;return t===""?r+".":`${r} depuis l’import du ${Lt(t)}.`}var Cs=c('

                                      Aucune décision locale : la grille est celle du fichier.

                                      '),Ps=c('
                                    • '),Ts=c('

                                        ',1);function zs(a,t){Ue(t,!0);const r=20,i=u(()=>t.decisions.slice(0,r));Oe(a,{title:"Décisions en vigueur",note:"Ce qu’un humain a décidé, produit par produit. C’est ici que se reprend un produit retiré de la grille.",children:(d,p)=>{var y=Tt(),z=ae(y);{var m=S=>{var A=Cs();l(S,A)},j=S=>{var A=Ts(),L=ae(A),W=s(L),O=n(L,2),C=s(O);ze(C,21,()=>e(i),K=>K.product_id,(K,U)=>{var E=Ps(),x=s(E),g=n(x,2),D=s(g),H=n(g,2),V=s(H),$=n(H,2),T=s($),w=n($,2),k=s(w),R=n(w,2),F=s(R);f((h,G,Y)=>{o(D,h),o(V,e(U).product_id),o(T,G),o(k,e(U).reason),o(F,Y)},[()=>t.nameOf(e(U).product_id),()=>br(e(U)),()=>va(e(U).decided_at)]),Ce("click",x,()=>t.onchoose(e(U).product_id)),l(K,E)}),f(K=>o(W,K),[()=>ca(e(i).length,t.decisions.length,"décision en vigueur","décisions en vigueur")]),l(S,A)};_(z,S=>{t.decisions.length===0?S(m):S(j,-1)})}l(d,y)},$$slots:{default:!0}}),Me()}ot(["click"]);function js(a){return a==="loading"?"Lecture des signalements du dernier import…":"Les signalements du dernier import n’ont pas pu être lus : cet écran ne sait pas ce qu’ils disent."}function Rs(a){return a==="loading"?"Lecture de l’historique des imports…":"L’historique des imports n’a pas pu être lu : cet écran ne sait pas ce qu’il contient."}function Aa(a,t,r){return a!==void 0?a:t==="loading"?"Lecture du nom…":t==="unread"?r:"Produit absent du catalogue en service"}var Ga=c('

                                        '),Os=c(' '),As=c('
                                      • '),Ns=c('

                                          ',1);function xa(a,t){Ue(t,!0);const r=Be(t,"remedy",3,""),i=50,d=u(()=>t.findings.slice(0,i)),p=u(()=>t.findings.length>e(d).length);Oe(a,{get title(){return t.title},get note(){return t.note},children:(y,z)=>{var m=Tt(),j=ae(m);{var S=W=>{var O=Ga(),C=s(O);f(K=>{be(O,"data-unread",t.list),o(C,K)},[()=>js(t.state)]),l(W,O)},A=W=>{var O=Ga(),C=s(O);f(()=>o(C,t.none)),l(W,O)},L=W=>{var O=Ns(),C=ae(O),K=s(C),U=n(K);{var E=D=>{var H=lt();f(()=>o(H,r())),l(D,H)};_(U,D=>{e(p)&&r()!==""&&D(E)})}var x=n(C,2),g=s(x);ze(g,21,()=>e(d),da,(D,H)=>{var V=As(),$=s(V),T=s($),w=n($,2);{var k=M=>{var b=Os(),N=s(b);f(()=>o(N,e(H).product_name)),l(M,b)};_(w,M=>{e(H).product_name!==""&&M(k)})}var R=n(w,2),F=s(R),h=n(R,2),G=s(h),Y=n(h,2),ne=s(Y);f(M=>{o(T,`ligne ${M??""}`),o(F,e(H).product_id),o(G,e(H).value),o(ne,e(H).message)},[()=>se(e(H).csv_line)]),l(D,V)}),f(D=>{be(C,"data-tally",t.list),o(K,`${D??""} `),be(x,"data-rows",t.list)},[()=>ca(e(d).length,t.findings.length,t.singular,t.plural)]),l(W,O)};_(j,W=>{t.state!=="read"?W(S):t.findings.length===0?W(A,1):W(L,-1)})}l(y,m)},$$slots:{default:!0}}),Me()}function wr(a,t){const r=a.rows_read_count,i=a.images_decoded_count,d=r-i,p=[{count:_t(a.weighable_count),label:"pesables",note:`(${_t(i)} avec photo, ${_t(d)} sans)`,link:""},{count:_t(a.not_weighable_count),label:"non pesables",note:Bs(t),link:""}];if(a.anomalies_count>0&&p.push({count:_t(a.anomalies_count),label:"anomalies",note:"à corriger dans Odoo",link:Ha(a.anomalies_count)}),a.unit_mismatches_count>0){const y=a.unit_mismatches_count;p.push({count:"+ "+_t(y),label:y===1?"unité divergente":"unités divergentes",note:"pesable, unité à corriger",link:Ha(y)})}return{headline:`Catalogue du ${va(a.occurred_at)} — ${_t(r)} produits reçus`,lines:p,oneLine:Is(a)}}function Is(a){const t=[`${_t(a.rows_read_count)} reçus`,`${_t(a.weighable_count)} pesables`,`${_t(a.not_weighable_count)} non pesables`,`${_t(a.anomalies_count)} anomalies`],r=a.unit_mismatches_count;return r>0&&t.push(`${_t(r)} ${r===1?"unité divergente":"unités divergentes"}`),t.join(" · ")}const Ds={applied:"appliqué",unchanged:"identique au précédent",rejected:"refusé",failed:"échec"},Fs={local_drop:"dépôt local",webdav:"WebDAV",manual:"déposé sur l’écran"};function ha(a){return Ds[a]??"résultat inconnu"}function yr(a){return Fs[a]??"source inconnue"}function Us(a,t){const r=[a.file_name===""?"Le dernier fichier":a.file_name,ha(a.result)],i=Lt(a.occurred_at);i!==""&&r.push("le "+i),r.push("via "+yr(a.source));const d=a.reason===""?"":" "+a.reason;return`${r.join(" ")} — ${wr(a,t).oneLine}.${d}`}function Ms(a){return a===""?"Aucun nouvel import enregistré à cet instant, et ce poste ne publie pas ce qu’il surveille.":"Aucun nouvel import enregistré à cet instant. Le poste surveille "+a+" : le fichier n’y était pas, ou il n’a pas encore fini d’arriver."}function Ws(a){return"Ce poste n’a pas de journal : il ne pourra rien dire de l’issue de cette relecture."+(a===""?"":" Le poste surveille "+a+".")}function Ha(a){return a===1?"voir la ligne":`voir les ${_t(a)} lignes`}function Bs(a){return a.map(t=>`${Vs(t)} (${_t(t.count)})`).join(", ")}function Vs(a){switch(a.code){case"PREPACKAGED_PRODUCT":return"préemballés";case"INTERNAL_CODE_NOT_WEIGHABLE":return a.value===""?"code interne":"code interne "+a.value;case"NO_BARCODE":return"sans code-barres";default:return a.code}}function _t(a){return String(a)}var Gs=c('

                                          '),Hs=c('

                                          Aucun import dans l’historique.

                                          '),Js=c(' '),Ks=c('

                                          QuandFichierSourceRésultatMotifLuesPesablesNon pesablesAnomaliesRetirés
                                          ',1);function $s(a,t){Ue(t,!0);function r(i){const d=ha(i.result);return i.code===""?d:`${d} (${i.code})`}Oe(a,{title:"Vingt derniers imports",children:(i,d)=>{var p=Tt(),y=ae(p);{var z=S=>{var A=Gs(),L=s(A);f(W=>o(L,W),[()=>Rs(t.state)]),l(S,A)},m=S=>{var A=Hs();l(S,A)},j=S=>{var A=Ks(),L=ae(A),W=s(L),O=n(L,2),C=s(O),K=n(s(C));ze(K,21,()=>t.imports,U=>U.id,(U,E)=>{var x=Js(),g=s(x),D=s(g),H=n(g),V=s(H),$=n(H),T=s($),w=n($),k=s(w),R=n(w),F=s(R),h=n(R),G=s(h),Y=n(h),ne=s(Y),M=n(Y),b=s(M),N=n(M),X=s(N),de=n(N),he=s(de);f((fe,oe,I,Q,re,ee,ve,ye)=>{o(D,fe),o(V,e(E).file_name),o(T,oe),o(k,I),o(F,e(E).reason),o(G,Q),o(ne,re),o(b,ee),o(X,ve),o(he,ye)},[()=>Lt(e(E).occurred_at),()=>yr(e(E).source),()=>r(e(E)),()=>se(e(E).rows_read_count),()=>se(e(E).weighable_count),()=>se(e(E).not_weighable_count),()=>se(e(E).anomalies_count),()=>se(e(E).products_withdrawn_count)]),l(U,x)}),f(U=>o(W,U),[()=>Ls(t.imports.length)]),l(S,A)};_(y,S=>{t.state!=="read"?S(z):t.imports.length===0?S(m,1):S(j,-1)})}l(i,p)},$$slots:{default:!0}}),Me()}var Ys=c(' '),Xs=c(''),Qs=c('
                                          '),Zs=c('

                                          ');function xr(a,t){Ue(t,!0);const r=u(()=>wr(t.record,t.motives));var i=Zs(),d=s(i),p=s(d),y=n(d,2);ze(y,21,()=>e(r).lines,j=>j.label,(j,S)=>{var A=Qs(),L=s(A),W=s(L),O=n(L,2),C=s(O),K=s(C),U=n(C,2);{var E=D=>{var H=Ys(),V=s(H);f(()=>o(V,e(S).note)),l(D,H)};_(U,D=>{e(S).note!==""&&D(E)})}var x=n(U,2);{var g=D=>{var H=Xs(),V=s(H);f(()=>o(V,e(S).link)),Ce("click",H,()=>t.onshowrows(e(S).label==="anomalies"?"anomalies":"units")),l(D,H)};_(x,D=>{e(S).link!==""&&t.onshowrows!==void 0&&D(g)})}f(()=>{be(A,"data-row",e(S).label),o(W,e(S).count),o(K,e(S).label)}),l(j,A)});var z=n(y,2),m=s(z);f(()=>{o(p,e(r).headline),o(m,e(r).oneLine)}),l(a,i),Me()}ot(["click"]);var el=c(" "),tl=c(' '),al=c('');function Na(a,t){Ue(t,!0);const r=Be(t,"hint",3,"");var i=al(),d=s(i),p=n(d,2),y=s(p),z=s(y),m=n(y,2);{var j=L=>{var W=el(),O=s(W);f(()=>o(O,t.path)),l(L,W)};_(m,L=>{St.showTechnicalNames&&L(j)})}var S=n(m,2);{var A=L=>{var W=tl(),O=s(W);f(()=>o(O,r())),l(L,W)};_(S,L=>{r()!==""&&L(A)})}f(L=>{be(i,"data-flag",t.path),be(i,"data-on",L),ea(d,t.on),o(z,t.label)},[()=>String(t.on)]),Ce("change",d,L=>t.onchange(L.currentTarget.checked)),l(a,i),Me()}ot(["change"]);function Ja(a){const t=`${se(a.columns)} ${a.columns>1?"colonnes":"colonne"}`,r=`${se(a.rows)} ${a.rows>1?"rangées":"rangée"}`;return`${t} × ${r}`}function rl(a){const t=a.layout,r=[];if(a.columns===Xt)return r.push(t===null?"Automatique : la grille suit la largeur de l’écran. Un écran plus large en montre davantage sans qu’on y revienne.":`Automatique : ${Ja(t)} sur cet écran. Un écran plus large en montrera davantage sans qu’on y revienne.`),r;if(t===null)return r.push(`${se(a.columns)} ${a.columns>1?"colonnes":"colonne"} sur tous les écrans. Cet écran ne sait pas dire combien de rangées cela fait ici.`),r;const i=t.columns*t.rows;if(r.push(`${Ja(t)} — ${se(i)} ${i>1?"tuiles":"tuile"} d’un coup, sur cet écran (${String(a.viewport.width)} × ${String(a.viewport.height)}).`),a.tileCount>0){const p=Math.ceil(a.tileCount/i);r.push(a.tileCount>1?`Les ${se(a.tileCount)} tuiles de la grille tiennent en ${se(p)} ${p>1?"écrans":"écran"}.`:"La seule tuile de la grille tient en un écran.")}const d=a.floor;if(d!==null&&d.names>0){const p=d.names>1;r.push(`${se(d.names)} ${p?"noms":"nom"} sur ${se(a.tileCount)} ${p?"atteignent":"atteint"} le plancher de ${se(ka)} px : `+(d.rows>1?`leurs ${se(d.rows)} rangées peuvent être plus hautes que les autres.`:"leur rangée peut être plus haute que les autres."))}return r}function nl(a,t,r){if(a==="loading")return"Lecture du catalogue en service…";if(a==="unread")return"Le catalogue en service n’a pas pu être lu : cet écran ne sait pas combien de produits se vendent à l’unité.";if(t===0)return"Aucun produit vendu à l’unité dans le catalogue en service.";const i=t>1,d=i?"produits vendus à l’unité sont":"produit vendu à l’unité est",p=r?`${i?"montrés":"montré"} dans la grille de ce poste`:`${i?"masqués":"masqué"} sur ce poste`;return`${se(t)} ${d} ${p}.`}function sl(a){return["localhost","127.0.0.1"].includes(a)?"":"Cet écran n’est pas celui du poste : ce compte vaut pour l’écran que vous lisez."}const ll=-1,il=10;class kr{#t=B("");get sentence(){return e(this.#t)}set sentence(t){v(this.#t,t,!0)}#r=0;#a="";#e=0;begin(t){this.sentence="",this.#r=t.last_import_id,this.#a=t.watched,this.#e=il}forget(){this.sentence="",this.#e=0}observe(t){if(this.#e===0)return;if(t.counters.journal_rows_count===ll){this.#n(Ws(this.#a));return}const r=t.catalog;if(r!==null&&r.id!==this.#r){this.#n(Us(r,t.catalog_motives));return}this.#e-=1,this.#e===0&&(this.sentence=Ms(this.#a))}#n(t){this.sentence=t,this.#e=0}}var ol=c('

                                          Aucun import enregistré sur ce poste.

                                          '),ul=c('

                                          '),cl=c(`

                                          « Oublier la quarantaine » fait relire un fichier que le poste avait écarté : c’est le + seul geste de cette page qui puisse remettre en service un catalogue refusé.

                                          `,1),Ka=c(""),dl=c(''),vl=c('

                                          '),pl=c('

                                          '),fl=c('
                                          '),gl=c(`

                                          Un produit masqué reste vendable : la caisse lit toujours son code-barres, et une + étiquette déjà imprimée reste valable. Ce réglage ne fait que retirer sa tuile.

                                          Colonnes de la grille

                                          Se tromper ne coûte rien d’autre que de revenir ici : le réglage ne change ni le + fichier reçu, ni les étiquettes déjà imprimées.

                                          En dessous de ce nombre, la catégorie n’a pas de puce dans la barre du bas. Ses + produits restent dans « Tout » et à la recherche : ce réglage ne retire aucune + tuile. Pour ne plus montrer une catégorie du tout, c’est sa case « visible » qu’il + faut décocher.

                                          `,1),ml=c('

                                          '),hl=c(`

                                          Déposez ici le fichier clé

                                          Ce dépôt remplace toute la grille par le fichier apporté : il change ce que le poste + vend, et le mot de passe est donc demandé au moment du dépôt.

                                          `,1),_l=c('

                                          Aucun import enregistré : rien n’a encore pu être retiré.

                                          '),bl=c('

                                          Aucun produit retiré par le dernier import.

                                          '),wl=c(`

                                          Ils restent enregistrés avec leur historique : une étiquette déjà collée reste + lisible en caisse, et un produit qui revient dans un prochain fichier retrouve sa + tuile. Ce poste n’en publie encore que le nombre — leurs noms se lisent dans Odoo, + en comparant avec l’export précédent.

                                          `,1),yl=c('
                                        • '),xl=c(`

                                            Un produit retiré de la grille ne se trouve plus ici : il se reprend dans + « Décisions en vigueur », plus bas.

                                            `,1),kl=c(`

                                            Sans motif, le poste refuse la décision. Le seul acte qui s’en passe est celui qui + EFFACE la décision en vigueur : proposer de nouveau un produit qui ne porte aucune + dérogation.

                                            `),ql=c(`

                                            Produit choisi :

                                            Ce produit est-il proposé dans la grille ?

                                            La dérogation s’enregistre seule : elle ne remet pas dans la grille un produit + qui en a été retiré, et retirer un produit n’efface pas sa dérogation.

                                            `),Sl=c(' ',1),Ll=c('
                                            ');function El(a,t){Ue(t,!0);const r=Be(t,"admin",7),i=20,d=["NO_BARCODE","PREPACKAGED_PRODUCT","INTERNAL_CODE_NOT_WEIGHABLE"],p="ui.grid_columns",y=[3,4,5,6,7,8,9,10,11,12],z="ui.min_products_for_chip";let m=B(vt([])),j=B(vt([])),S=B(null);const A=u(()=>e(S)?.products??[]),L=u(()=>e(S)?.presentation??{}),W=u(()=>e(S)?.pricing?.primary_code??"");let O=B(""),C=B(""),K=B(""),U=B(""),E=B(!1),x=B(""),g=B(""),D=B("loading"),H=B("loading");const V=u(()=>r().busy||e(g)!==""),$=u(()=>e(j).filter(J=>J.issue==="anomaly")),T=u(()=>e(j).filter(J=>d.includes(J.code))),w=u(()=>e(j).filter(J=>J.code==="UNIT_MISMATCH")),k=u(()=>new Map(e(A).map(J=>[J.id,J.name]))),R=u(()=>e(A).filter(J=>J.mode==="by_unit").length),F=u(()=>nl(e(H),e(R),t.draft.flag("ui.show_by_unit_products"))),h=u(()=>t.draft.number(p)),G=u(()=>t.draft.number(z)),Y=u(()=>M("ui.show_by_unit_products",e(L).show_by_unit_products??!1)?e(A):e(A).filter(J=>J.mode!=="by_unit")),ne=u(()=>e(Y)[0]===void 0?null:{...e(Y)[0],name:"·",image_url:"",prices:e(Y)[0].prices??[]});function M(J,me){return t.draft.value(J)===void 0?me:t.draft.flag(J)}let b=B(null),N=B(null),X=B(null),de=B(null),he=B(null),fe=B(0),oe=B(null);wt(()=>{const J=()=>{v(fe,e(fe)+1)};return window.addEventListener("resize",J),()=>window.removeEventListener("resize",J)}),wt(()=>{const J=e(h);e(fe),v(oe,ee(J),!0)});const I=u(()=>{const J=e(oe);if(J===null||J.contentWidthPx<=0||J.nameBoxPx<=0||e(Y).length===0)return null;const me=ye();if(me===null)return null;const we=Mr(me.family,me.weight);if(we===null)return null;const Le=Wr(J.tileScale),Ke=new Set;let Ve=0;for(const[Ye,Fe]of e(Y).entries())Br(Fe.name,J.contentWidthPx,we,J.nameBoxPx,Le)>ka||(Ve+=1,Ke.add(Math.floor(Ye/J.columns)));return{names:Ve,rows:Ke.size}}),Q=u(()=>rl({columns:e(h),layout:e(oe),tileCount:e(Y).length,floor:e(I),viewport:{width:window.innerWidth,height:window.innerHeight}})),re=u(()=>e(oe)===null?"":sl(window.location.hostname));function ee(J){const me=e(b),we=e(N),Le=e(X),Ke=e(de),Ve=e(he);if(me===null||we===null||Ke===null||Ve===null||Le===null)return null;we.style.gridTemplateColumns=Fr(J);const Ye=ve(we),Fe=Ye[0];if(Fe===void 0)return null;const Ae=Ke.clientWidth,Xe=J===Xt||Ae<=0?1:Fe/Ae;me.style.setProperty("--tile-scale",String(Xe));const Ge=Number.parseFloat(getComputedStyle(we).rowGap),Qe=Ve.clientHeight,it=Le.offsetHeight,qe=Le.querySelector(".name-box");return!Number.isFinite(Ge)||Qe<=0||it<=0||qe===null?null:{columns:Ye.length,rows:Math.max(1,Math.floor((Qe+Ge)/(it+Ge))),contentWidthPx:qe.clientWidth,nameBoxPx:qe.clientHeight,tileScale:Xe}}function ve(J){const me=getComputedStyle(J).gridTemplateColumns.split(/\s+/u).filter(Le=>Le!=="");if(me.length===0)return[];const we=me.map(Le=>Le.endsWith("px")?Number.parseFloat(Le):Number.NaN);return we.every(Le=>Number.isFinite(Le)&&Le>0)?we:[]}function ye(){const J=e(X)?.querySelector(".name")??null;if(J===null)return null;const me=getComputedStyle(J),we=Number.parseInt(me.fontWeight,10);return me.fontFamily===""||!Number.isFinite(we)?null:{family:me.fontFamily,weight:we}}const Ie=u(()=>e(O)===""?[]:Vr(e(A),Gr,e(O))),q=u(()=>e(Ie).slice(0,i)),le=u(()=>Ss(e(q).length,e(Ie).length)),ge=u(()=>[...t.health.decisions].sort((J,me)=>me.decided_at.localeCompare(J.decided_at))),ue=u(()=>e(C)===""?null:e(ge).find(J=>J.product_id===e(C))??null),ie=u(()=>e(ue)===null?!0:e(ue).offered),Ne=u(()=>e(ue)?.min_weight_g??null),je=u(()=>xs(e(U))),ke=u(()=>e(K).trim()),Re=u(()=>qs({motive:e(ke),offeredInForce:e(ie),waiverInForce:e(Ne),typedWaiver:e(je)})),We=u(()=>t.health.catalog?.products_withdrawn_count??0),xe=u(()=>e(m).find(J=>t.health.catalog!==null&&J.idEs(e(We),e(xe)?.occurred_at??""));let rt;wt(()=>{const J=t.health.catalog?.id??null;J!==rt&&(rt=J,pt())});const nt=new kr;wt(()=>{nt.observe(t.health)});async function pt(){const J=t.health.catalog_findings_id,me=await r().load(()=>Cn(J===0?void 0:J));me===null?v(D,"unread"):(v(m,me.imports,!0),v(j,me.findings,!0),v(D,"read"));const we=await r().load(()=>cr());if(we===null){v(H,"unread");return}v(S,we,!0),v(H,"read")}async function ct(J,me){v(g,J,!0),r().actionError="",r().notice="",nt.forget();try{const we=await r().protect(me);return we===null?null:(r().notice=we.message,await r().refresh(),we)}finally{v(g,"")}}async function dt(){const J=await ct("reload",Pn);J!==null&&nt.begin(J)}function ft(J){return Aa(e(k).get(J),e(H),"Nom inconnu : le catalogue n’a pas pu être lu")}function bt(J){v(C,J,!0),v(K,"");const me=e(ge).find(we=>we.product_id===J)?.min_weight_g??null;v(U,me===null?"":String(me),!0)}async function et(J){if(e(C)==="")return;const me=e(C),we=e(Ne),Le=e(ke);await ct("offered",()=>Ma(me,{offered:J,min_weight_g:we,reason:Le}))!==null&&v(K,"")}async function _e(J){if(e(C)==="")return;const me=e(C),we=e(ie),Le=e(ke);await ct(J===null?"waiver-off":"waiver",()=>Ma(me,{offered:we,min_weight_g:J,reason:Le}))!==null&&v(K,"")}async function Se(J){if(v(E,!1),J!=null){v(x,""),r().actionError="",r().notice="",v(g,"import");try{const me=await r().protect(()=>pr(J));if(me===null)return;v(x,`${J.name} : ${se(me.rows_read_count)} lignes lues, ${se(me.weighable_count)} pesables. `+(me.reason===""?"Le fichier est déposé ; son résultat s’inscrira dans l’historique des imports.":me.reason)),await r().refresh()}finally{v(g,"")}}}function st(J){J.preventDefault(),v(E,!1);const me=J.dataTransfer?.files.item(0);if(e(V)){v(x,`${me?.name??"Ce fichier"} n’a pas été déposé : un acte est déjà en cours sur cette page. Réessayez quand il aura répondu.`);return}Se(me)}function Dt(J){const me=J.files?.item(0);J.value="",Se(me)}var zt=Ll(),Et=s(zt);ws(Et,{get draft(){return t.draft},get station(){return t.health.station}});var jt=n(Et,2);Oe(jt,{title:"Dernier import",children:(J,me)=>{var we=cl(),Le=ae(we);{var Ke=qe=>{var Ze=ol();l(qe,Ze)},Ve=qe=>{xr(qe,{get record(){return t.health.catalog},get motives(){return t.health.catalog_motives}})};_(Le,qe=>{t.health.catalog===null?qe(Ke):qe(Ve,-1)})}var Ye=n(Le,2),Fe=s(Ye),Ae=n(Ye,2);{var Xe=qe=>{var Ze=ul(),Je=s(Ze);f(()=>o(Je,nt.sentence)),l(qe,Ze)};_(Ae,qe=>{nt.sentence!==""&&qe(Xe)})}var Ge=n(Ae,2),Qe=s(Ge);{let qe=u(()=>e(g)==="reload");Te(Qe,{act:"reload",kind:"write",label:"Recharger le catalogue",protected:!0,get busy(){return e(qe)},get disabled(){return e(V)},onrun:()=>{dt()}})}var it=n(Qe,2);{let qe=u(()=>e(g)==="quarantine");Te(it,{act:"quarantine",kind:"destructive",label:"Oublier la quarantaine",protected:!0,get busy(){return e(qe)},get disabled(){return e(V)},onrun:()=>{ct("quarantine",Tn)}})}f(()=>o(Fe,t.health.catalog_source===null?"Aucune source de catalogue publiée par ce poste.":"Source : "+t.health.catalog_source.label)),l(J,we)},$$slots:{default:!0}});var Rt=n(jt,2);Oe(Rt,{title:"Ce que la grille montre",note:"Un réglage d’affichage : il ne change ni le fichier reçu, ni ce que le poste sait peser.",children:(J,me)=>{var we=gl(),Le=ae(we);{let Z=u(()=>t.draft.flag("ui.show_by_unit_products"));Na(Le,{path:"ui.show_by_unit_products",label:"Afficher les produits vendus à l’unité",hint:"Décoché, leurs tuiles quittent la grille et la recherche ne les retrouve plus. Ce que le poste perd : une tuile vendue à l’unité imprime une étiquette sans jamais lire la balance, et c’est le seul geste que ce réglage retire.",get on(){return e(Z)},onchange:ce=>t.draft.set("ui.show_by_unit_products",ce)})}var Ke=n(Le,2),Ve=s(Ke),Ye=n(Ke,4),Fe=n(s(Ye));{var Ae=Z=>{var ce=Ka();ce.textContent="ui.grid_columns",l(Z,ce)};_(Fe,Z=>{St.showTechnicalNames&&Z(Ae)})}var Xe=n(Ye,2),Ge=s(Xe),Qe=s(Ge),it=n(Ge,2);ze(it,16,()=>y,Z=>Z,(Z,ce)=>{var Pe=dl(),pe=s(Pe),Ee=n(pe);f((tt,mt)=>{be(Pe,"data-columns",ce),be(Pe,"data-on",tt),Gt(pe,ce),ea(pe,e(h)===ce),o(Ee,` ${mt??""}`)},[()=>String(e(h)===ce),()=>se(ce)]),Ce("change",pe,()=>t.draft.set(p,ce)),l(Z,Pe)});var qe=n(Xe,2),Ze=s(qe);ze(Ze,17,()=>e(Q),da,(Z,ce)=>{var Pe=vl(),pe=s(Pe);f(()=>o(pe,e(ce))),l(Z,Pe)});var Je=n(Ze,2);{var ut=Z=>{var ce=pl(),Pe=s(ce);f(()=>o(Pe,e(re))),l(Z,ce)};_(Je,Z=>{e(re)!==""&&Z(ut)})}var gt=n(qe,4),yt=n(s(gt),2);{var Ct=Z=>{var ce=Ka();ce.textContent="ui.min_products_for_chip",l(Z,ce)};_(yt,Z=>{St.showTechnicalNames&&Z(Ct)})}var xt=n(gt,2),Ot=n(xt,4),kt=s(Ot),Mt=s(kt);{var na=Z=>{var ce=fl(),Pe=s(ce);{let pe=u(()=>M("ui.show_grid_prices",e(L).show_grid_prices??!0));Ur(Pe,{get product(){return e(ne)},get nameSizePx(){return ka},get primaryCode(){return e(W)},get showPrice(){return e(pe)},onpick:()=>{}})}sa(ce,pe=>v(X,pe),()=>e(X)),l(Z,ce)};_(Mt,Z=>{e(ne)!==null&&Z(na)})}sa(kt,Z=>v(N,Z),()=>e(N));var P=n(kt,2);sa(P,Z=>v(de,Z),()=>e(de));var te=n(P,2);sa(te,Z=>v(he,Z),()=>e(he)),sa(Ot,Z=>v(b,Z),()=>e(b)),f(Z=>{o(Ve,e(F)),be(Ge,"data-columns",Xt),be(Ge,"data-on",Z),Gt(Qe,Xt),ea(Qe,e(h)===Xt),Gt(xt,e(G))},[()=>String(e(h)===Xt)]),Ce("change",Qe,()=>t.draft.set(p,Xt)),Ce("input",xt,Z=>t.draft.set(z,Number(Z.currentTarget.value))),l(J,we)},$$slots:{default:!0}});var At=n(Rt,2);Oe(At,{title:"Déposer un catalogue",note:"Glissez le fichier CSV ici, ou choisissez-le. Il passe par le même chemin que le fichier du producteur.",children:(J,me)=>{var we=hl(),Le=ae(we);{var Ke=qe=>{var Ze=ml(),Je=s(Ze);f(()=>o(Je,e(x))),l(qe,Ze)};_(Le,qe=>{e(x)!==""&&qe(Ke)})}var Ve=n(Le,2);let Ye;var Fe=s(Ve),Ae=n(s(Fe)),Xe=s(Ae),Ge=n(Fe,2),Qe=s(Ge),it=n(Qe);f(()=>{Ye=Nt(Ve,1,"drop svelte-3gk7u7",null,Ye,{dropping:e(E),working:e(g)==="import"}),o(Xe,`flv_${t.health.station??""}.csv`),o(Qe,`${e(g)==="import"?"Import en cours…":"Choisir un fichier"} `),it.disabled=e(V)}),Ht("dragover",Ve,qe=>{qe.preventDefault(),!e(V)&&v(E,!0)}),Ht("dragleave",Ve,()=>v(E,!1)),Ht("drop",Ve,st),Ce("change",it,qe=>Dt(qe.currentTarget)),l(J,we)},$$slots:{default:!0}});var Ft=n(At,2);xa(Ft,{title:"Anomalies à corriger dans Odoo",note:"Chaque ligne porte le nom du produit, son numéro dans le CSV, son motif et la valeur fautive.",list:"anomalies",get state(){return e(D)},get findings(){return e($)},singular:"anomalie",plural:"anomalies",none:"Aucune anomalie sur le dernier import.",remedy:"Corrigez celles-ci dans Odoo : l’import suivant ne signalera que ce qui reste."});var Jt=n(Ft,2);xa(Jt,{title:"Unités divergentes",note:"Le produit reste proposé : le code-barres fait foi, seul le libellé du prix est faux.",list:"mismatches",get state(){return e(D)},get findings(){return e(w)},singular:"unité divergente",plural:"unités divergentes",none:"Aucune unité divergente sur le dernier import."});var Ut=n(Jt,2);xa(Ut,{title:"Produits non pesables",note:"Un inventaire, pas une liste d’erreurs : ces produits portent déjà leur code-barres et n’ont aucune raison d’être pesés.",list:"not-weighable",get state(){return e(D)},get findings(){return e(T)},singular:"produit non pesable",plural:"produits non pesables",none:"Aucun produit non pesable sur le dernier import."});var ta=n(Ut,2);Oe(ta,{title:"Produits retirés depuis l’import précédent",note:"Un produit absent du nouveau fichier est marqué retiré à sa date, jamais supprimé.",children:(J,me)=>{var we=Tt(),Le=ae(we);{var Ke=Fe=>{var Ae=_l();l(Fe,Ae)},Ve=Fe=>{var Ae=bl();l(Fe,Ae)},Ye=Fe=>{var Ae=wl(),Xe=ae(Ae),Ge=s(Xe);f(()=>o(Ge,e(De))),l(Fe,Ae)};_(Le,Fe=>{t.health.catalog===null?Fe(Ke):e(We)===0?Fe(Ve,1):Fe(Ye,-1)})}l(J,we)},$$slots:{default:!0}});var Kt=n(ta,2);Oe(Kt,{title:"Décider d’un produit",note:"Retirer un produit et l’autoriser à peser moins sont deux décisions séparées : l’une n’efface pas l’autre.",children:(J,me)=>{var we=Sl(),Le=n(ae(we),2),Ke=n(Le,2);{var Ve=Ae=>{var Xe=xl(),Ge=ae(Xe),Qe=s(Ge),it=n(Ge,2),qe=s(it);ze(qe,21,()=>e(q),Ze=>Ze.id,(Ze,Je)=>{var ut=yl(),gt=s(ut),yt=s(gt),Ct=n(gt,2),xt=s(Ct),Ot=n(Ct,2),kt=s(Ot);f(()=>{o(yt,e(Je).name),o(xt,e(Je).id),o(kt,`${e(Je).unit_price_text??""}${e(Je).price_suffix??""}`)}),Ce("click",gt,()=>bt(e(Je).id)),l(Ze,ut)}),f(()=>o(Qe,e(le))),l(Ae,Xe)};_(Ke,Ae=>{e(O)!==""&&Ae(Ve)})}var Ye=n(Ke,2);{var Fe=Ae=>{var Xe=ql(),Ge=s(Xe),Qe=n(s(Ge)),it=s(Qe),qe=n(Qe),Ze=n(Ge,2),Je=s(Ze),ut=n(Ze,4),gt=n(ut,2);{var yt=pe=>{var Ee=kl();l(pe,Ee)};_(gt,pe=>{e(ke)===""&&pe(yt)})}var Ct=n(gt,2),xt=n(s(Ct),2),Ot=s(xt);{var kt=pe=>{{let Ee=u(()=>e(g)==="offered"),tt=u(()=>e(V)||!e(Re).canWithdraw);Te(pe,{act:"offered",kind:"destructive",label:"Ne plus proposer ce produit",protected:!0,get busy(){return e(Ee)},get disabled(){return e(tt)},onrun:()=>{et(!1)}})}},Mt=pe=>{{let Ee=u(()=>e(g)==="offered"),tt=u(()=>e(V)||!e(Re).canOfferAgain);Te(pe,{act:"offered",kind:"write",label:"Le proposer de nouveau",protected:!0,get busy(){return e(Ee)},get disabled(){return e(tt)},onrun:()=>{et(!0)}})}};_(Ot,pe=>{e(ie)?pe(kt):pe(Mt,-1)})}var na=n(Ct,2),P=n(s(na),2),te=n(P,2),Z=s(te);{let pe=u(()=>e(g)==="waiver"),Ee=u(()=>e(V)||!e(Re).canSaveWaiver);Te(Z,{act:"waiver",kind:"write",label:"Enregistrer la dérogation",protected:!0,get busy(){return e(pe)},get disabled(){return e(Ee)},onrun:()=>{_e(e(je))}})}var ce=n(Z,2);{var Pe=pe=>{{let Ee=u(()=>e(g)==="waiver-off"),tt=u(()=>e(V)||!e(Re).canDropWaiver);Te(pe,{act:"waiver-off",kind:"write",label:"Retirer la dérogation",protected:!0,get busy(){return e(Ee)},get disabled(){return e(tt)},onrun:()=>{_e(null)}})}};_(ce,pe=>{e(Ne)!==null&&pe(Pe)})}f((pe,Ee)=>{be(Xe,"data-decision",e(C)),o(it,pe),o(qe,` (${e(C)??""})`),o(Je,`En vigueur : ${Ee??""}`),Gt(P,e(U))},[()=>ft(e(C)),()=>e(ue)===null?"aucune décision — ce produit suit les règles générales":br(e(ue))]),ia(ut,()=>e(K),pe=>v(K,pe)),Ce("input",P,pe=>v(U,pe.currentTarget.value,!0)),l(Ae,Xe)};_(Ye,Ae=>{e(C)!==""&&Ae(Fe)})}ia(Le,()=>e(O),Ae=>v(O,Ae)),l(J,we)},$$slots:{default:!0}});var $t=n(Kt,2);zs($t,{get decisions(){return e(ge)},nameOf:ft,onchoose:bt});var aa=n($t,2);$s(aa,{get imports(){return e(m)},get state(){return e(D)}}),l(a,zt),Me()}ot(["change","input","click"]);function Cl(a){switch(a){case"ok":return"OK";case"warn":return"À SURVEILLER";case"fault":return"EN PANNE";case"off":return"SANS OBJET";default:return"INCONNU"}}var Pl=c('

                                            '),Tl=c('

                                            ');function zl(a,t){Ue(t,!0);var r=Tl(),i=s(r),d=s(i),p=n(d,2),y=s(p),z=n(p,2),m=s(z),j=n(i,2),S=s(j),A=n(j,2);{var L=W=>{var O=Pl(),C=s(O);f(()=>o(C,t.light.remedy)),l(W,O)};_(A,W=>{t.light.remedy!==""&&W(L)})}f(W=>{be(r,"data-light",t.light.id),be(r,"data-level",t.light.level),be(d,"data-level",t.light.level),o(y,t.light.label),o(m,W),o(S,t.light.value)},[()=>Cl(t.light.level)]),l(a,r),Me()}function jl(a){return[Rl(a),Ol(a),Nl(a),Il(a),Fl(a),Ul(a)]}function Rl(a){const t=a.state.scale,r=ua(t.median_ms);return a.scale_present?t.connected?t.too_slow?{id:"scale",label:"Balance",level:"warn",value:`une mesure toutes les ${r}, plus lent que la péremption`,remedy:"À cette cadence, un poids serait déclaré périmé avant l’arrivée de la mesure suivante. Vérifiez le câble et l’adaptateur USB, puis la cadence sur la page Matériel."}:{id:"scale",label:"Balance",level:"ok",value:Ml(t.provisional,`une mesure toutes les ${r}`),remedy:""}:{id:"scale",label:"Balance",level:"fault",value:"elle ne répond plus",remedy:"Vérifiez le câble et l’alimentation de la balance, puis touchez « Tester la balance ». En attendant, « Basculer en saisie manuelle » permet de continuer à servir."}:{id:"scale",label:"Balance",level:"off",value:"ce poste est déclaré sans balance",remedy:""}}function Ol(a){const t=a.state.printer;switch(t.health){case"faulted":return{id:"printer",label:"Imprimante",level:"fault",value:t.detail===""?"elle ne peut pas imprimer":t.detail,remedy:Al(a)};case"consumable":return{id:"printer",label:"Imprimante",level:"warn",value:"elle imprime, mais le rouleau arrive en fin de vie",remedy:"Changez le rouleau quand vous passez derrière le comptoir, puis touchez « J’ai changé le rouleau ». Le poste continue de servir en attendant."};case"ready":return{id:"printer",label:"Imprimante",level:"ok",value:"elle répond et n’a rien à signaler",remedy:""};default:return{id:"printer",label:"Imprimante",level:"unknown",value:"elle prend les étiquettes et ne dit rien en retour",remedy:"C’est la réponse normale d’une file Windows en RAW ou d’un fichier de périphérique, pas une panne. Pour savoir si elle imprime, touchez « Imprimer une étiquette de test »."}}}function Al(a){const t="Regardez le capot, le rouleau et le câble, puis touchez « Tester l’imprimante ».";return a.printing?.fallback_available===!0?t+" Le poste peut servir en attendant : « Imprimer sur l’imprimante du poste voisin ».":t}function Nl(a){const t=a.roll;return t===null?{id:"roll",label:"Rouleau",level:"unknown",value:"aucun compteur d’étiquettes sur ce poste",remedy:`Sans imprimante construite, il n’y a pas de rouleau à compter : vérifiez le « ${qt("printer.type")} » et ses réglages sur la page Matériel.`}:t.known?t.level==="warn"?{id:"roll",label:"Rouleau",level:"warn",value:t.message,remedy:"Changez le rouleau, puis touchez « J’ai changé le rouleau »."}:{id:"roll",label:"Rouleau",level:"ok",value:t.message,remedy:""}:{id:"roll",label:"Rouleau",level:"unknown",value:t.message,remedy:"Touchez « J’ai changé le rouleau » en mettant un rouleau neuf : c’est le seul geste qui dise quelque chose de vrai du papier."}}function Il(a){const t=a.catalog_source?.label??"";if(a.state.catalog_count===0)return{id:"catalog",label:"Catalogue",level:"fault",value:"aucun produit dans la grille",remedy:Dl(t)};const r=a.catalog;return r!==null&&r.result!=="applied"&&r.result!=="unchanged"?{id:"catalog",label:"Catalogue",level:"warn",value:`dernier fichier refusé : ${r.reason===""?r.result:r.reason}`,remedy:"La grille tourne toujours sur le catalogue précédent. Corrigez le fichier dans Odoo, ou touchez « Oublier la quarantaine » puis redéposez-le."}:{id:"catalog",label:"Catalogue",level:"ok",value:`${se(a.state.catalog_count)} produits dans la grille`,remedy:""}}function Dl(a){return"Déposez le fichier du catalogue là où le poste le guette, ou utilisez « Importer un catalogue » ci-contre pour le glisser depuis une clé USB."+(a===""?"":` Ce poste surveille : ${a}.`)}function Fl(a){const t=a.disk;if(t===null)return{id:"disk",label:"Disque",level:"unknown",value:"la place libre n’a pas pu être mesurée",remedy:"Téléchargez le fichier de diagnostic : il porte ce que le poste a pu lire du volume, et c’est la pièce qu’un support demandera."};const r=Sa(t.free_bytes),i=`seuil ${se(t.alert_mb)} Mo`;return t.free_bytes===0?{id:"disk",label:"Disque",level:"fault",value:`plus un octet libre sur ${t.path}`,remedy:"Le journal ne peut plus rien écrire, alors que les étiquettes continuent de sortir. Faites de la place sur le disque, puis rechargez cette page."}:t.alert_mb>0&&t.free_bytes0?{id:"journal",label:"Journal",level:"fault",value:`${se(t)} pesées imprimées mais non enregistrées`,remedy:"Les étiquettes sortent, les ventes sont bonnes, c’est la trace qui manque. Téléchargez le fichier de diagnostic et prévenez le support : ne redémarrez rien."}:a.counters.journal_rows_count<0?{id:"journal",label:"Journal",level:"unknown",value:"ce poste n’a pas de journal ouvert",remedy:"Le poste pèse et imprime quand même. Téléchargez le fichier de diagnostic : il dit pourquoi la base n’a pas pu être ouverte."}:{id:"journal",label:"Journal",level:"ok",value:`${se(a.counters.journal_rows_count)} pesées enregistrées`,remedy:""}}function Ml(a,t){return a?t+" (cadence encore provisoire)":t}function Wl(a){return a===null||!a.known?"unknown":a.configured?"ok":"missing"}var Bl=c("Une mesure toutes les ",1),Vl=c('

                                            '),Gl=c("Source : ",1),Hl=c(`

                                            Aucun import enregistré sur ce poste : le catalogue n’est jamais arrivé, ou le + journal ne le porte pas.

                                            `),Jl=c('

                                            ',1),Kl=c('

                                            ',1),$l=c('

                                            Aucune décision locale : le catalogue est proposé tel qu’il arrive.

                                            '),Yl=c('

                                            '),Xl=c(' '),Ql=c('
                                          • '),Zl=c('
                                              ',1),ei=c(`INCONNU — la question n’a pas pu être posée à ce système. Ce + n’est pas « non configuré » : personne ne sait encore.`,1),ti=c('OK ',1),ai=c('NON CONFIGURÉ ',1),ri=c('

                                              '),ni=c('

                                              ',1),si=c('

                                              Rien à signaler depuis le démarrage du poste.

                                              '),li=c(' '),ii=c('
                                            • '),oi=c('
                                                '),ui=c(''),ci=c('

                                                '),di=c('
                                                Numéro de poste
                                                Nom
                                                Coopérative
                                                Version
                                                Empreinte de configuration
                                                ',1),vi=c('
                                                ');function pi(a,t){Ue(t,!0);const r=u(()=>t.health.new_version),i=u(()=>jl(t.health)),d=u(()=>t.health.state.scale),p=u(()=>t.health.unattended_restart),y=u(()=>t.health.decisions),z=5,m=u(()=>e(y).slice(0,z));var j=vi(),S=s(j);ze(S,21,()=>e(i),U=>U.id,(U,E)=>{zl(U,{get light(){return e(E)}})});var A=n(S,2);Oe(A,{title:"Cadence de la balance",children:(U,E)=>{var x=Vl(),g=s(x);{var D=T=>{var w=lt("Ce poste est déclaré sans balance : le poids est saisi à la main.");l(T,w)},H=T=>{var w=lt(`La balance ne répond pas : aucune cadence n’est mesurable tant qu’aucune trame + n’arrive.`);l(T,w)},V=T=>{var w=lt(`Aucun intervalle n’a encore été mesuré : la cadence apparaîtra dès les premières + trames.`);l(T,w)},$=T=>{var w=Bl(),k=n(ae(w)),R=s(k),F=n(k),h=n(F);{var G=M=>{var b=lt("C’est encore une valeur d’attente : moins de huit intervalles ont été observés.");l(M,b)};_(h,M=>{e(d).provisional&&M(G)})}var Y=n(h,2);{var ne=M=>{var b=lt("À cette cadence, un poids serait périmé avant l’arrivée de la mesure suivante.");l(M,b)};_(Y,M=>{e(d).too_slow&&M(ne)})}f((M,b)=>{o(R,M),o(F,`, médiane + observée sur ${b??""} intervalles. `)},[()=>ua(e(d).median_ms),()=>se(e(d).observations_count)]),l(T,w)};_(g,T=>{t.health.scale_present?e(d).connected?e(d).observations_count===0?T(V,2):T($,-1):T(H,1):T(D)})}l(U,x)},$$slots:{default:!0}});var L=n(A,2);Oe(L,{title:"Catalogue",children:(U,E)=>{var x=Kl(),g=ae(x),D=s(g);{var H=k=>{var R=lt("Aucune source de catalogue n’est publiée par ce poste.");l(k,R)},V=k=>{var R=Gl(),F=n(ae(R)),h=s(F);f(()=>o(h,t.health.catalog_source.label)),l(k,R)};_(D,k=>{t.health.catalog_source===null?k(H):k(V,-1)})}var $=n(g,2);{var T=k=>{var R=Hl();l(k,R)},w=k=>{var R=Jl(),F=ae(R),h=s(F),G=n(F,2);xr(G,{get record(){return t.health.catalog},get motives(){return t.health.catalog_motives},get onshowrows(){return t.onshowrows}}),f((Y,ne)=>o(h,`Dernier essai : ${Y??""} — + ${ne??""}${t.health.catalog.file_name===""?"":" ("+t.health.catalog.file_name+")"}${t.health.catalog.reason===""?"":" : "+t.health.catalog.reason}`),[()=>Lt(t.health.catalog.occurred_at),()=>ha(t.health.catalog.result)]),l(k,R)};_($,k=>{t.health.catalog===null?k(T):k(w,-1)})}l(U,x)},$$slots:{default:!0}});var W=n(L,2);Oe(W,{title:"Décisions locales en vigueur",note:"Ce qu’un humain a décidé de ce catalogue, avec son motif et sa date.",children:(U,E)=>{var x=Tt(),g=ae(x);{var D=V=>{var $=$l();l(V,$)},H=V=>{var $=Zl(),T=ae($);{var w=R=>{var F=Yl(),h=s(F);f(G=>o(h,`${G??""} décisions en vigueur — les + 5 plus récentes ci-dessous. La liste entière est sur la page + Catalogue.`),[()=>se(e(y).length)]),l(R,F)};_(T,R=>{e(y).length>z&&R(w)})}var k=n(T,2);ze(k,21,()=>e(m),R=>R.product_id,(R,F)=>{var h=Ql(),G=s(h),Y=s(G),ne=n(G,2);{var M=he=>{var fe=Xl(),oe=s(fe);f(I=>o(oe,`peut peser à partir de ${I??""} g`),[()=>se(e(F).min_weight_g)]),l(he,fe)};_(ne,he=>{e(F).min_weight_g!==null&&he(M)})}var b=n(ne,2),N=s(b),X=n(b,2),de=s(X);f(he=>{o(Y,`${e(F).offered?"Dérogation de poids":"Produit retiré"} — ${e(F).product_id??""}`),o(N,e(F).reason),o(de,`${he??""}, ${e(F).decided_by??""}`)},[()=>va(e(F).decided_at)]),l(R,h)}),l(V,$)};_(g,V=>{e(y).length===0?V(D):V(H,-1)})}l(U,x)},$$slots:{default:!0}});var O=n(W,2);Oe(O,{title:"Redémarrage sans intervention",children:(U,E)=>{var x=ni(),g=ae(x),D=s(g);{var H=k=>{var R=ei();l(k,R)},V=k=>{var R=ti(),F=n(ae(R));f(()=>o(F,` — après une coupure de courant, ce poste revient seul sur + l’écran client. ${e(p).detail??""}`)),l(k,R)},$=k=>{var R=ai(),F=n(ae(R));f(()=>o(F,` — ${e(p).detail??""}`)),l(k,R)};_(D,k=>{e(p)===null||!e(p).known?k(H):e(p).configured?k(V,1):k($,-1)})}var T=n(g,2);{var w=k=>{var R=ri(),F=s(R);f(()=>o(F,e(p).remedy)),l(k,R)};_(T,k=>{e(p)!==null&&e(p).known&&!e(p).configured&&e(p).remedy!==""&&k(w)})}f(k=>be(g,"data-verdict",k),[()=>Wl(e(p))]),l(U,x)},$$slots:{default:!0}});var C=n(O,2);Oe(C,{title:"Dix derniers événements",children:(U,E)=>{var x=Tt(),g=ae(x);{var D=V=>{var $=si();l(V,$)},H=V=>{var $=oi();ze($,21,()=>t.health.events,T=>T.id,(T,w)=>{var k=ii(),R=s(k),F=s(R),h=n(R,2),G=s(h),Y=n(h,2);{var ne=N=>{var X=li(),de=s(X);f(()=>o(de,e(w).code)),l(N,X)};_(Y,N=>{e(w).code!==""&&St.showTechnicalNames&&N(ne)})}var M=n(Y,2),b=s(M);f((N,X)=>{be(k,"data-level",e(w).level),o(F,N),o(G,X),o(b,e(w).message)},[()=>ys(e(w).occurred_at),()=>mr(e(w).source)]),l(T,k)}),l(V,$)};_(g,V=>{t.health.events.length===0?V(D):V(H,-1)})}l(U,x)},$$slots:{default:!0}});var K=n(C,2);Oe(K,{title:"Ce poste",children:(U,E)=>{var x=di(),g=ae(x),D=n(s(g),2),H=s(D),V=n(D,4),$=s(V),T=n(V,4),w=s(T),k=n(T,4),R=s(k),F=n(k,4),h=s(F),G=n(g,2);{var Y=ne=>{var M=ci(),b=s(M),N=n(b);{var X=de=>{var he=ui();Ce("click",he,function(...fe){t.onshowupdate?.apply(this,fe)}),l(de,he)};_(N,de=>{t.onshowupdate!==void 0&&de(X)})}f(()=>{be(M,"data-update-available",e(r)),o(b,`Version ${e(r)??""} disponible. `)}),l(ne,M)};_(G,ne=>{e(r)!==""&&ne(Y)})}f(()=>{o(H,t.health.station),o($,t.health.station_name===""?"non renseigné":t.health.station_name),o(w,t.health.coop),o(R,t.health.version),o(h,t.health.config_fingerprint)}),l(U,x)},$$slots:{default:!0}}),l(a,j),Me()}ot(["click"]);var fi=c('

                                                ');function $a(a,t){Ue(t,!0);var r=fi(),i=s(r),d=s(i),p=n(i,2),y=n(s(p),1,!0);f(()=>{o(d,t.title),be(p,"data-standing",t.name),be(p,"data-level",t.standing.level),o(y,t.standing.word)}),l(a,r),Me()}const gi={0:"NUL",2:"STX",3:"ETX",4:"EOT",5:"ENQ",6:"ACK",9:"TAB",10:"LF",13:"CR",21:"NAK",27:"ESC",127:"DEL"};function mi(a){return[...new TextEncoder().encode(a)].map(t=>t.toString(16).toUpperCase().padStart(2,"0")).join(" ")}function hi(a){let t="";for(const r of a){const i=r.codePointAt(0)??0;if(i>=32&&i!==127){t+=r;continue}t+=`⟨${gi[i]??i.toString(16).toUpperCase().padStart(2,"0")}⟩`}return t}function _i(a){const t=bi(a);return t===""?Ya(a):`${Ya(a)} ${t}`}function Ya(a){return a.configRead?a.declaredWithoutScale?"Ce poste est déclaré sans balance : aucun port n’est écouté.":a.port===""?"Aucun port n’est indiqué : choisissez-en un dans la liste ci-dessus pour écouter les trames.":a.listed?a.portKnown?a.halt!==""?`L’écoute de ${a.port} est arrêtée.`:a.acting!==""?`L’écoute de ${a.port} est suspendue le temps de l’acte en cours.`:`Écoute de ${a.port}.`:`${a.port} n’est pas visible depuis ce poste : rien n’est écouté en continu.`:a.acting==="ports"?`Énumération des ports en cours : l’écoute de ${a.port} démarre dès qu’il est vu.`:`Les ports de ce poste n’ont pas été énumérés : « Lister les ports » dira si ${a.port} existe.`:"Lecture de la configuration en cours : le port à écouter n’est pas encore connu."}function bi(a){return a.framesShown===0?"Aucune trame reçue pour l’instant.":a.framesShown===1?`Une seule trame reçue — ${se(a.framesKept)} au plus sont gardées.`:`Les ${se(a.framesShown)} dernières trames — ${se(a.framesKept)} au plus, la plus récente en bas.`}function wi(a){return!a.configRead||a.declaredWithoutScale||a.port===""?"":a.halt!==""?"Reprendre l’écoute":a.listed&&!a.portKnown?"Écouter ce port une fois":""}function yi(a){return a.acting!=="detect"?"Détecter automatiquement":a.listening?"Détection : le port se libère…":a.toScan===0?"Détection : énumération des ports…":`Détection : port ${se(a.scanned)} sur ${se(a.toScan)}…`}function xi(a,t){return a?t.connected?t.too_slow?{level:"warn",word:"Trop lente",detail:Xa(t)+" À cette cadence, un poids serait déclaré périmé avant l’arrivée de la mesure suivante."}:{level:"ok",word:"Connectée",detail:"Elle répond. "+Xa(t)}:{level:"fault",word:"Sans réponse",detail:"Elle ne répond plus. Vérifiez le câble et l’alimentation, puis « Tester la balance » sur la page Dépannage."}:{level:"off",word:"Sans balance",detail:"Ce poste est déclaré sans balance : le feu est éteint et le poids se saisit à la main."}}function Xa(a){return a.observations_count===0?"Aucun intervalle n’a encore été mesuré : la cadence sera connue dès les premières trames.":`Une mesure toutes les ${ua(a.median_ms)} sur ${se(a.observations_count)} intervalles`+(a.provisional?", cadence encore provisoire.":".")}const ki={ready:{level:"ok",word:"Prête",detail:"Elle répond et n’a rien à signaler."},consumable:{level:"warn",word:"Rouleau en fin de vie",detail:"Elle imprime, mais le rouleau arrive en fin de vie."},faulted:{level:"fault",word:"En panne",detail:"Elle ne peut pas imprimer."},unknown:{level:"unknown",word:"Silencieuse",detail:"Elle prend les étiquettes et ne dit rien en retour : c’est la réponse normale d’une file Windows en RAW ou d’un fichier de périphérique, pas une panne."}};function qi(a){const t=ki[a.health]??{level:"unknown",word:"État inconnu",detail:"Le poste a répondu un état que cet écran ne sait pas nommer."};return{...t,detail:a.detail===""?t.detail:a.detail}}function Si(a){const t=a.observed_at===""?"Jamais observée depuis le démarrage":`Observée le ${Lt(a.observed_at)}`,r=a.pending_jobs_count;return`${t}, ${se(r)} ${r>1?"travaux":"travail"} en attente.`}const Li=["queue","path","address"],Ei="queue",Ci={queue:"Choisissez-la dans la liste ci-dessus : une file mal orthographiée ne s’imprime pas.",path:"Le nœud d’impression de ce poste, /dev/usb/lp0 ou le lien que la règle udev lui donne.",address:"L’adresse de l’imprimante sur le réseau, 192.168.0.43 — le port 9100 est ajouté s’il manque."};function Pi(a,t){return t!==""&&!a.some(r=>r.id===t)}function Ti(a,t){return a.length===0?[]:[...Pi(a,t)?[{value:t,label:`${t} — inconnu de ce poste`}]:[],...a.map(r=>({value:r.id,label:r.label}))]}function zi(a,t){return a.find(r=>r.id===t)?.key??Ei}function ji(a,t,r){const i=[...new Set(a.filter(y=>y.key!==r).map(y=>Ri(t,y.key)))].filter(y=>y!==""),d=a.filter(y=>y.key!==r).length,p=`${se(d)} ${d>1?"destinations ne sont pas proposées":"destination n’est pas proposée"}`;return i.length===0?`${p} : aucun transport de ce poste ne les lit.`:`${p} : choisissez « ${i.join(" » ou « ")} » pour les voir.`}function Ri(a,t){return a.find(r=>r.key===t)?.label??""}var Oi=c(''),Ai=c(`

                                                Lecture de la configuration en cours… tant qu’elle n’est pas arrivée, cette page ne + déclare rien de ce poste.

                                                `),Qa=c('
                                              • '),Ni=c('
                                                  '),Ii=c('

                                                  ',1),Di=c('
                                                • '),Fi=c('

                                                    ',1),Ui=c('

                                                    '),Mi=c('
                                                  • '),Wi=c('
                                                      '),Bi=c('

                                                      '),Vi=c('

                                                        ',1),Gi=c('

                                                        '),Hi=c('

                                                        L’état et la cadence viennent de ce que le poste observe vraiment, jamais d’un réglage.

                                                        Réglages série de la balance

                                                        Réglages de l’imprimante
                                                        ');function Ji(a,t){Ue(t,!0);const r=Be(t,"admin",7),i=20,d=3,p=250,y=50,z=d*1e3+p+1e3,m=12;let j=B(vt([])),S=B(vt([])),A=B(vt([])),L=B(vt([])),W=B(""),O=B(!1),C=B(""),K=B(0),U=B(0),E=B(!1),x=B(null),g=B(!1),D=B(!1),H=!1;const V=u(()=>t.health.state.scale),$=u(()=>t.health.state.printer),T=u(()=>t.draft.config!==null),w=u(()=>e(T)&&!t.draft.flag("scale.present")),k=u(()=>t.draft.text("scale.options.port")),R=u(()=>e(x)!==null&&e(x).port===e(k)?e(x).reason:""),F=u(()=>e(W)===e(k)?e(L):[]),h=u(()=>t.health.printer_transports),G=u(()=>t.draft.text("printer.options.transport")),Y=u(()=>Ti(e(h),e(G))),ne=u(()=>zi(e(h),e(G))),M=u(()=>"printer.options."+e(ne)),b=u(()=>e(S).filter(P=>P.key===e(ne))),N=u(()=>e(S).length-e(b).length),X=u(()=>xi(t.health.scale_present,e(V))),de=u(()=>qi(e($))),he=u(()=>({configRead:e(T),declaredWithoutScale:e(w),port:e(k),listed:e(O),portKnown:ve(e(k)),halt:e(R),acting:e(C),framesShown:e(F).length,framesKept:i})),fe=u(()=>_i(e(he))),oe=u(()=>wi(e(he))),I=u(()=>yi({acting:e(C),listening:e(E),scanned:e(K),toScan:e(U)})),Q=u(()=>$e(t.draft,"scale.type")!==""||$e(t.draft,"scale.options.port")!==""),re=u(()=>$e(t.draft,"printer.type")!==""||$e(t.draft,"printer.options.transport")!==""||Li.some(P=>$e(t.draft,"printer.options."+P)!==""));wt(()=>{e(Q)&&v(g,!0)}),wt(()=>{e(re)&&v(D,!0)}),Yr(()=>{H=!0}),Ta(()=>{ue("ports",ie)}),wt(()=>{e(E)||!ee(e(k))||ye(e(k))});function ee(P){return!H&&e(T)&&!e(w)&&P!==""&&P===e(k)&&ve(P)&&e(R)===""&&e(C)===""}function ve(P){return e(j).some(te=>te.name===P)}async function ye(P){v(E,!0);try{for(;ee(P);){try{q(P,await Ua(P,d))}catch(te){v(x,{port:P,reason:De(te)},!0);return}await le()}}finally{v(E,!1)}}async function Ie(){const P=e(k),te=await r().protect(()=>Ua(P,d));te!==null&&(q(P,te),v(x,null))}function q(P,te){if(H||te.length===0)return;const Z=P===e(W)?e(L):[];v(W,P,!0),v(L,[...Z,...te].slice(-i),!0)}function le(P=p){return new Promise(te=>setTimeout(te,P))}async function ge(){const P=Date.now()+z;for(;e(E)&&Date.now()fr(P));te!==null&&(r().notice=te.message,await r().refresh())}async function Re(){v(K,0),v(U,0),e(O)||await ie();const P=e(j);if(P.length===0){v(A,[],!0);return}await r().protect(()=>We(P))}async function We(P){v(A,[],!0),v(U,P.length,!0);for(const[te,Z]of P.entries()){v(K,te+1);try{const ce=await kn(Z.name);v(A,[...e(A),{port:Z.name,message:ce.message,refused:!1}],!0)}catch(ce){if(fa(ce))throw ce;v(A,[...e(A),{port:Z.name,message:De(ce),refused:!0}],!0)}}}function xe(P,te){const Z=Qt(t.draft,P);return Z.length>0?Z:te.slice(0,m)}function De(P){return P instanceof Pt?P.message:P instanceof Error?"Le poste n’a pas répondu : "+P.message:"Le poste n’a pas répondu."}const rt=[{what:"label",name:"étiquette"},{what:"alignment",name:"alignement"},{what:"ruler",name:"réglette"}],nt=u(()=>rt.filter(P=>t.health.printer_self_tests.includes(P.what)));var pt=Hi(),ct=s(pt),dt=s(ct);$a(dt,{title:"Balance",name:"scale",get standing(){return e(X)}});var ft=n(dt,2),bt=s(ft),et=n(ft,4);{var _e=P=>{var te=Oi(),Z=s(te);f(()=>ea(Z,e(w))),Ce("change",Z,ce=>t.draft.set("scale.present",!ce.currentTarget.checked)),l(P,te)},Se=P=>{var te=Ai();l(P,te)};_(et,P=>{e(T)?P(_e):P(Se,-1)})}var st=n(et,2),Dt=s(st);{let P=u(()=>e(C)==="ports"),te=u(()=>e(C)!=="");Te(Dt,{label:"Lister les ports",get busy(){return e(P)},get disabled(){return e(te)},onrun:()=>{ue("ports",ie)}})}var zt=n(Dt,2);{let P=u(()=>e(C)!=="");Te(zt,{act:"detect",get label(){return e(I)},protected:!0,get disabled(){return e(P)},onrun:()=>{ue("detect",Re)}})}var Et=n(st,2);{var jt=P=>{var te=Ii(),Z=ae(te),ce=s(Z),Pe=n(Z,2);{var pe=Ee=>{var tt=Ni();ze(tt,21,()=>e(j).slice(0,m),mt=>mt.name,(mt,ht)=>{var Wt=Qa(),Yt=s(Wt),_a=s(Yt),Cr=n(Yt,2),Pr=s(Cr);f(()=>{o(_a,e(ht).name),o(Pr,`${(e(ht).description===""?"aucune description USB":e(ht).description)??""} + ${e(ht).vid===""?"":` — VID ${e(ht).vid} PID ${e(ht).pid}`}`)}),Ce("click",Yt,()=>t.draft.set("scale.options.port",e(ht).name)),l(mt,Wt)}),l(Ee,tt)};_(Pe,Ee=>{e(j).length>0&&Ee(pe)})}f(Ee=>o(ce,Ee),[()=>e(j).length===0?"Aucun port série n’est visible depuis ce poste.":ya("port détecté","ports détectés",e(j).length,m)]),l(P,te)};_(Et,P=>{e(O)&&P(jt)})}var Rt=n(Et,2);{var At=P=>{var te=Fi(),Z=ae(te),ce=s(Z),Pe=n(Z,2);ze(Pe,21,()=>e(A).slice(0,m),pe=>pe.port,(pe,Ee)=>{var tt=Di();let mt;var ht=s(tt),Wt=s(ht),Yt=n(ht,2),_a=s(Yt);f(()=>{mt=Nt(tt,1,"svelte-1p4i0xm",null,mt,{refused:e(Ee).refused}),o(Wt,e(Ee).port),o(_a,e(Ee).message)}),l(pe,tt)}),f(pe=>o(ce,pe),[()=>ya("port interrogé","ports interrogés",e(A).length,m)]),l(P,te)};_(Rt,P=>{e(A).length>0&&P(At)})}var Ft=n(Rt,2),Jt=n(s(Ft),2),Ut=s(Jt);{let P=u(()=>t.draft.text("scale.type")),te=u(()=>$e(t.draft,"scale.type")),Z=u(()=>Qt(t.draft,"scale.type")),ce=u(()=>!e(T));at(Ut,{label:"Protocole",path:"scale.type",get value(){return e(P)},hint:"Les valeurs acceptées apparaissent ici si l’enregistrement est refusé.",get fault(){return e(te)},get allowed(){return e(Z)},get disabled(){return e(ce)},onchange:Pe=>t.draft.set("scale.type",Pe)})}var ta=n(Ut,2);{let P=u(()=>t.draft.text("scale.options.port")),te=u(()=>$e(t.draft,"scale.options.port")),Z=u(()=>xe("scale.options.port",e(j).map(Pe=>Pe.name))),ce=u(()=>!e(T));at(ta,{label:"Port série",path:"scale.options.port",get value(){return e(P)},hint:"Choisissez-le dans la liste détectée ci-dessus plutôt que de le taper : l’écoute permanente ne suit que des ports détectés.",get fault(){return e(te)},get allowed(){return e(Z)},get disabled(){return e(ce)},onchange:Pe=>t.draft.set("scale.options.port",Pe)})}var Kt=n(Ft,2),$t=s(Kt),aa=s($t),J=n($t,2);{var me=P=>{var te=Ui(),Z=s(te),ce=n(Z);{var Pe=pe=>{{let Ee=u(()=>e(C)==="listen"),tt=u(()=>e(C)!=="");Te(pe,{act:"listen",get label(){return e(oe)},protected:!0,get busy(){return e(Ee)},get disabled(){return e(tt)},onrun:()=>{ue("listen",Ie)}})}};_(ce,pe=>{e(oe)!==""&&pe(Pe)})}f(()=>o(Z,`${e(R)??""} `)),l(P,te)};_(J,P=>{(e(R)!==""||e(oe)!=="")&&P(me)})}var we=n(J,2);{var Le=P=>{var te=Wi();ze(te,21,()=>e(F),da,(Z,ce)=>{var Pe=Mi(),pe=s(Pe),Ee=s(pe),tt=n(pe,2),mt=s(tt);f((ht,Wt)=>{o(Ee,ht),o(mt,Wt)},[()=>mi(e(ce)),()=>hi(e(ce))]),l(Z,Pe)}),l(P,te)};_(we,P=>{e(F).length>0&&P(Le)})}var Ke=n(ct,2),Ve=s(Ke);$a(Ve,{title:"Imprimante",name:"printer",get standing(){return e(de)}});var Ye=n(Ve,2),Fe=s(Ye),Ae=n(Ye,2),Xe=s(Ae),Ge=n(Ae,2),Qe=s(Ge);{let P=u(()=>e(C)==="printers"),te=u(()=>e(C)!=="");Te(Qe,{label:"Lister les files",get busy(){return e(P)},get disabled(){return e(te)},onrun:()=>{ue("printers",Ne)}})}var it=n(Qe,2);{let P=u(()=>e(C)==="discover"),te=u(()=>e(C)!=="");Te(it,{label:"Rechercher l’imprimante",protected:!0,get busy(){return e(P)},get disabled(){return e(te)},onrun:()=>{ue("discover",je)}})}var qe=n(it,2);ze(qe,17,()=>e(nt),P=>P.what,(P,te)=>{{let Z=u(()=>`Auto-test : ${e(te).name}${e(C)===e(te).what?" — en cours…":""}`),ce=u(()=>e(C)!=="");Te(P,{get act(){return e(te).what},get label(){return e(Z)},protected:!0,get disabled(){return e(ce)},onrun:()=>{ue(e(te).what,()=>ke(e(te).what))}})}});var Ze=n(Ge,2);{var Je=P=>{var te=Bi(),Z=s(te);f(()=>o(Z,e(nt).length===0?"Le driver d’impression en service n’imprime aucun auto-test.":"Les autres auto-tests ne sont pas proposés : le driver d’impression en service ne les imprime pas.")),l(P,te)};_(Ze,P=>{e(nt).length{var te=Vi(),Z=ae(te),ce=s(Z),Pe=n(Z,2);ze(Pe,21,()=>e(b).slice(0,m),pe=>pe.name,(pe,Ee)=>{var tt=Qa(),mt=s(tt),ht=s(mt),Wt=n(mt,2),Yt=s(Wt);f(()=>{o(ht,e(Ee).name),o(Yt,`${e(Ee).detail??""}${e(Ee).default?" — file par défaut du système":""}`)}),Ce("click",mt,()=>t.draft.set(e(M),e(Ee).name)),l(pe,tt)}),f(pe=>o(ce,pe),[()=>ya("destination","destinations",e(b).length,m)]),l(P,te)};_(ut,P=>{e(b).length>0&&P(gt)})}var yt=n(ut,2);{var Ct=P=>{var te=Gi(),Z=s(te);f(ce=>o(Z,ce),[()=>ji(e(S),e(h),e(ne))]),l(P,te)};_(yt,P=>{e(N)>0&&P(Ct)})}var xt=n(yt,2),Ot=n(s(xt),2),kt=s(Ot);{let P=u(()=>t.draft.text("printer.type")),te=u(()=>$e(t.draft,"printer.type")),Z=u(()=>Qt(t.draft,"printer.type")),ce=u(()=>!e(T));at(kt,{label:"Driver",path:"printer.type",get value(){return e(P)},hint:"Gardez le driver raster : c’est celui que les postes en service utilisent.",get fault(){return e(te)},get allowed(){return e(Z)},get disabled(){return e(ce)},onchange:Pe=>t.draft.set("printer.type",Pe)})}var Mt=n(kt,2);{let P=u(()=>$e(t.draft,"printer.options.transport")),te=u(()=>Qt(t.draft,"printer.options.transport")),Z=u(()=>!e(T));at(Mt,{label:"Transport",path:"printer.options.transport",get value(){return e(G)},hint:"Local par défaut : une file Windows ou un nœud d’impression de ce poste.",get fault(){return e(P)},get allowed(){return e(te)},get choices(){return e(Y)},get disabled(){return e(Z)},onchange:ce=>t.draft.set("printer.options.transport",ce)})}var na=n(Mt,2);{let P=u(()=>qt(e(M))),te=u(()=>t.draft.text(e(M))),Z=u(()=>Ci[e(ne)]??""),ce=u(()=>$e(t.draft,e(M))),Pe=u(()=>xe(e(M),e(b).map(Ee=>Ee.name))),pe=u(()=>!e(T));at(na,{get label(){return e(P)},get path(){return e(M)},get value(){return e(te)},get hint(){return e(Z)},get fault(){return e(ce)},get allowed(){return e(Pe)},get disabled(){return e(pe)},onchange:Ee=>t.draft.set(e(M),Ee)})}f(P=>{o(bt,e(X).detail),o(aa,e(fe)),o(Fe,e(de).detail),o(Xe,P)},[()=>Si(e($))]),Fa("open","toggle",Ft,P=>v(g,P),()=>e(g)),Fa("open","toggle",xt,P=>v(D,P),()=>e(D)),l(a,pt),Me()}ot(["change","click"]);const Za={sent:"envoyée à l’imprimante",rejected:"refusée",failed:"en échec",reprint:"réimpression"},er=[{value:"",label:"toutes"},{value:"sent",label:"envoyées à l’imprimante"},{value:"rejected",label:"refusées"},{value:"failed",label:"en échec"},{value:"reprint",label:"réimpressions"}],Ki={scale:"balance",manual:"saisie manuelle",replay:"trame rejouée"},$i={stable:"stable",unstable:"instable",unknown:"non déclarée par la balance",not_applicable:"sans objet — saisie manuelle"},Yi={by_weight:"au poids",by_unit:"à l’unité"},Xi={debug:"mise au point",info:"information",warn:"avertissement",error:"erreur",critical:"critique"};function ra(a,t,r){return a[t]??r}var Qi=c(' '),Zi=c(' '),eo=c('
                                                      • '),to=c('
                                                          ');function ao(a,t){Ue(t,!0);var r=to(),i=s(r);ze(i,21,()=>t.lines,d=>d.id,(d,p)=>{var y=eo(),z=s(y),m=s(z),j=n(z,2),S=s(j),A=n(j,2),L=s(A),W=n(A,2);{var O=x=>{var g=Qi(),D=s(g);f(()=>o(D,e(p).code)),l(x,g)};_(W,x=>{e(p).code!==""&&St.showTechnicalNames&&x(O)})}var C=n(W,2),K=s(C),U=n(C,2);{var E=x=>{var g=Zi(),D=s(g);f(()=>o(D,e(p).detail)),l(x,g)};_(U,x=>{e(p).detail!==""&&x(E)})}f((x,g,D)=>{be(y,"data-level",e(p).level),o(m,x),o(S,g),o(L,D),o(K,e(p).message)},[()=>Lt(e(p).occurred_at),()=>ra(Xi,e(p).level,"niveau inconnu"),()=>mr(e(p).source)]),l(d,y)}),l(a,r),Me()}var ro=c(""),no=c('L’export sera proposé quand le journal aura répondu.'),so=c('L’export n’est pas proposé : ce poste n’a pas répondu à la lecture du journal.'),lo=c('Exporter en CSV'),io=c('

                                                          '),oo=c('

                                                          '),uo=c(`

                                                          Aucune trame brute n’a été enregistrée pour cette pesée : il n’y a + rien à rejouer.

                                                          `),co=c(`

                                                          La trame repart dans le décodeur du poste EN SERVICE : le poids + affiché au client change, et rien ne le remet comme il était. C’est + ce qui fait d’un refus inexpliqué un test permanent, sans + déplacement au magasin et sans balance.

                                                          `,1),vo=c('

                                                          Produit
                                                          Référence
                                                          Vente
                                                          Brut / tare / net
                                                          Stabilité
                                                          Origine du poids
                                                          Résultat
                                                          Trame brute
                                                          '),po=c(' ',1),fo=c('

                                                          QuandProduitNetCode-barresRésultatDuréeDétail
                                                          ',1),go=c('
                                                          ',1),mo=c('

                                                          '),ho=c('

                                                          ',1),_o=c('
                                                          ');function bo(a,t){Ue(t,!0);const r=Be(t,"admin",7),i=200,d=5e3,p=50,y=500,z=7;let m=B(vt([])),j=B(vt([])),S=B(""),A=B(null),L=B(!1),W=B(!1),O=B("loading"),C=B("loading"),K=B(""),U=B("");const E=u(()=>({limit:String(i),...e(S)===""?{}:{result:e(S)}})),x=u(()=>({...e(E),limit:String(d)})),g=u(()=>e(m).find(h=>h.id===e(A))??null),D=u(()=>er.find(h=>h.value===e(S))?.label??""),H=u(()=>w(e(m).length,i,"pesée","pesées",`L’export CSV en emporte jusqu’à ${se(d)}.`)),V=u(()=>w(e(j).length,p,"ligne","lignes",`Le fichier de diagnostic emporte les ${se(y)} dernières.`));$();async function $(){v(A,null),v(W,!0),v(O,"loading"),v(C,"loading"),v(m,[],!0),v(j,[],!0),v(K,""),v(U,"");try{const h=await r().load(()=>Sn(e(E)));v(O,h===null?"unread":"read",!0),v(K,h===null?r().actionError:"",!0),v(m,h??[],!0);const G=await r().load(()=>En({limit:String(p)}));v(C,G===null?"unread":"read",!0),v(U,G===null?r().actionError:"",!0),v(j,G??[],!0)}finally{v(W,!1),r().actionError=""}}async function T(h){r().notice="",r().actionError="",v(L,!0);try{const G=await r().protect(()=>zn(h));G!==null&&(r().notice=G.message)}finally{v(L,!1)}}function w(h,G,Y,ne,M){const b=h>1?ne:Y;return h{var Y=go(),ne=ae(Y),M=n(s(ne),2);ze(M,21,()=>er,ee=>ee.value,(ee,ve)=>{var ye=ro(),Ie=s(ye),q={};f(()=>{o(Ie,e(ve).label),q!==(q=e(ve).value)&&(ye.value=(ye.__value=e(ve).value)??"")}),l(ee,ye)});var b=n(M,2);Te(b,{label:"Rafraîchir",get busy(){return e(W)},onrun:()=>{$()}});var N=n(b,2);{var X=ee=>{var ve=no();l(ee,ve)},de=ee=>{var ve=so();l(ee,ve)},he=ee=>{var ve=lo();f(ye=>be(ve,"href",ye),[()=>Ln(e(x))]),l(ee,ve)};_(N,ee=>{e(O)==="loading"?ee(X):e(O)==="unread"?ee(de,1):ee(he,-1)})}var fe=n(ne,2);{var oe=ee=>{var ve=io(),ye=s(ve);f(Ie=>o(ye,`L’export emporte le même filtre que le tableau, mais pas son plafond : il descend + jusqu’à ${Ie??""} pesées, en point-virgule et en UTF-8 — il s’ouvre + tel quel dans le tableur d’un Windows français. Il ne demande aucun mot de passe : la + lecture du journal n’en demande pas non plus, et le fichier de diagnostic emporte déjà + les deux cents dernières pesées.`),[()=>se(d)]),l(ee,ve)};_(fe,ee=>{e(O)==="read"&&ee(oe)})}var I=n(fe,2);{var Q=ee=>{var ve=oo(),ye=s(ve);{var Ie=ue=>{var ie=lt("Lecture du journal…");l(ue,ie)},q=ue=>{var ie=lt();f(()=>o(ie,`Le journal n’a pas pu être lu : ${e(K)??""} Ce n’est pas « aucune pesée ».`)),l(ue,ie)},le=ue=>{var ie=lt("Le journal ne contient aucune pesée.");l(ue,ie)},ge=ue=>{var ie=lt();f(()=>o(ie,`Aucune pesée ne correspond au filtre « ${e(D)??""} ».`)),l(ue,ie)};_(ye,ue=>{e(O)==="loading"?ue(Ie):e(O)==="unread"?ue(q,1):e(S)===""?ue(le,2):ue(ge,-1)})}l(ee,ve)},re=ee=>{var ve=fo(),ye=ae(ve),Ie=s(ye),q=n(ye,2),le=s(q),ge=n(s(le));ze(ge,21,()=>e(m),ue=>ue.id,(ue,ie)=>{var Ne=po(),je=ae(Ne);let ke;var Re=s(je),We=s(Re),xe=n(Re),De=s(xe),rt=n(xe),nt=s(rt),pt=n(rt),ct=s(pt),dt=n(pt),ft=s(dt),bt=n(dt),et=s(bt),_e=n(bt),Se=s(_e),st=s(Se),Dt=n(je,2);{var zt=Et=>{var jt=vo(),Rt=s(jt);be(Rt,"colspan",z);var At=s(Rt),Ft=s(At),Jt=n(At,2),Ut=n(s(Jt),2),ta=s(Ut),Kt=n(Ut,4),$t=s(Kt),aa=n(Kt,4),J=s(aa),me=n(aa,4),we=s(me),Le=n(me,4),Ke=s(Le),Ve=n(Le,4),Ye=s(Ve),Fe=n(Ve,4),Ae=s(Fe),Xe=n(Fe,4),Ge=s(Xe),Qe=s(Ge),it=n(Jt,2);{var qe=Je=>{var ut=uo();l(Je,ut)},Ze=Je=>{var ut=co(),gt=ae(ut),yt=s(gt);Te(yt,{kind:"destructive",label:"Rejouer cette trame",protected:!0,get busy(){return e(L)},onrun:()=>{T(e(g).frame)}}),l(Je,ut)};_(it,Je=>{e(g).frame===""?Je(qe):Je(Ze,-1)})}f((Je,ut,gt,yt,Ct,xt,Ot,kt,Mt)=>{be(jt,"data-detail",e(ie).id),o(Ft,`Pesée ${e(g).id??""}`),o(ta,`${e(g).product_name??""} (${e(g).product_id??""})`),o($t,e(g).reference),o(J,`${Je??""} — + ${ut??""} + ${e(g).quantity>1?"unités":"unité"}`),o(we,`${gt??""} g / ${yt??""} g / + ${Ct??""} g`),o(Ke,`${xt??""} — cadence + médiane ${Ot??""}`),o(Ye,kt),o(Ae,`${Mt??""}${e(g).detail===""?"":` — ${e(g).detail}`}`),o(Qe,e(g).frame===""?"aucune trame enregistrée":e(g).frame)},[()=>ra(Yi,e(g).mode,"mode de vente inconnu"),()=>se(e(g).quantity),()=>se(e(g).gross_g),()=>se(e(g).tare_g),()=>se(e(g).net_g),()=>ra($i,e(g).stability,"stabilité inconnue"),()=>ua(e(g).rate_ms),()=>ra(Ki,e(g).source,"origine inconnue"),()=>ra(Za,e(g).result,"résultat inconnu")]),l(Et,jt)};_(Dt,Et=>{e(A)===e(ie).id&&e(g)!==null&&Et(zt)})}f((Et,jt,Rt,At)=>{ke=Nt(je,1,"svelte-86o8oz",null,ke,{open:e(A)===e(ie).id}),o(We,Et),o(De,e(ie).product_name),o(nt,`${jt??""} g`),o(ct,e(ie).barcode),o(ft,Rt),o(et,At),be(Se,"aria-expanded",e(A)===e(ie).id),o(st,e(A)===e(ie).id?"fermer":"détail")},[()=>Lt(e(ie).occurred_at),()=>se(e(ie).net_g),()=>ra(Za,e(ie).result,"résultat inconnu"),()=>ua(e(ie).duration_ms)]),Ce("click",Se,()=>v(A,e(A)===e(ie).id?null:e(ie).id,!0)),l(ue,Ne)}),f(()=>o(Ie,e(H))),l(ee,ve)};_(I,ee=>{e(m).length===0?ee(Q):ee(re,-1)})}Ce("change",M,()=>{$()}),$r(M,()=>e(S),ee=>v(S,ee)),l(h,Y)},$$slots:{default:!0}});var F=n(R,2);Oe(F,{title:"Journal technique",children:(h,G)=>{var Y=Tt(),ne=ae(Y);{var M=N=>{var X=mo(),de=s(X);{var he=I=>{var Q=lt("Lecture du journal technique…");l(I,Q)},fe=I=>{var Q=lt();f(()=>o(Q,`Le journal technique n’a pas pu être lu : ${e(U)??""} Ce n’est pas « aucune + ligne ».`)),l(I,Q)},oe=I=>{var Q=lt("Aucune ligne technique.");l(I,Q)};_(de,I=>{e(C)==="loading"?I(he):e(C)==="unread"?I(fe,1):I(oe,-1)})}l(N,X)},b=N=>{var X=ho(),de=ae(X),he=s(de),fe=n(de,2);ao(fe,{get lines(){return e(j)}}),f(()=>o(he,e(V))),l(N,X)};_(ne,N=>{e(j).length===0?N(M):N(b,-1)})}l(h,Y)},$$slots:{default:!0}}),l(a,k),Me()}ot(["change","click"]);const tr="L’aperçu n’a pas pu être rendu par le poste.",wo="Le poste a rendu l’aperçu, mais le navigateur ne l’a pas affiché.";async function yo(a){try{const t=await fetch(a,{headers:{accept:"application/json"}});if(t.ok)return wo;const r=JSON.parse(await t.text());return typeof r.message=="string"&&r.message!==""?r.message:tr}catch{return tr}}function ar(a,t){let r=a;for(const i of t.split(".")){if(r===null||typeof r!="object")return 0;r=r[i]}return typeof r=="number"?r:0}function qr(a){const t=Number(a);return a.trim()===""||Number.isNaN(t)?null:t}function xo(a){const t=a.trim().replace(",",".");if(!/^\d{1,3}(\.\d)?$/u.test(t))return null;const r=Number(t);return r>100?null:r}function Sr(a,t){!(a instanceof HTMLInputElement)||a.value===t||(a.value=t)}var ko=c('

                                                          '),qo=c('

                                                          '),So=c('

                                                          '),Lo=c('

                                                          '),Eo=c('

                                                          Lecture de la configuration en cours… les flèches attendent qu’elle soit arrivée.

                                                          '),Co=c(`
                                                          Aperçu de l’étiquette telle qu’elle serait imprimée

                                                          Décalage horizontal

                                                          Décalage vertical

                                                          Le décalage ne descend pas sous zéro : le poste refuse un décalage négatif quel que + soit le gabarit. Le maximum, lui, dépend de la géométrie du gabarit ; il est annoncé + ici si l’enregistrement le dépasse.

                                                          Le symbole code-barres est volontairement tronqué : un symbole conforme n’entre pas + sur 40 × 25 mm avec les cinq champs texte. Ce n’est pas un défaut de rendu et il n’y + a rien à corriger.

                                                          Le détail chiffré du symbole — largeur de module, modules rendus, modules attendus — + n’est pas servi par ce poste. Cet écran n’affiche pas un chiffre qu’il aurait deviné.

                                                          `,1),Po=c(`

                                                          Chaque appui sort une étiquette pour de bon : le mot de passe est demandé au moment + d’imprimer, et l’impression repart d’elle-même une fois la session ouverte.

                                                          `,1),To=c('
                                                          ');function zo(a,t){Ue(t,!0);const r=Be(t,"admin",7),i="printer.template",d="printer.options.offset_x",p="printer.options.offset_y",y=0,z=[{what:"alignment",label:"Imprimer la mire d’alignement"},{what:"ruler",label:"Imprimer la réglette"}];let m=B(1),j=B(!0),S=B(!1),A=B(""),L=B(null),W=B(""),O=null,C=!1;const K=u(()=>t.draft.text(i)),U=u(()=>qn(e(K),e(j),e(S),e(m))),E=u(()=>t.draft.config!==null),x=u(()=>t.draft.number(d)),g=u(()=>t.draft.number(p)),D=u(()=>$e(t.draft,d)),H=u(()=>$e(t.draft,p)),V=u(F),$=u(()=>r().busy||e(W)!=="");wt(()=>{if(t.draft.config===null)return;if(t.draft.dirty){O===null&&T();return}const N={x:t.draft.number(d),y:t.draft.number(p)};if(O!==null&&O.x===N.x&&O.y===N.y)return;const X=O===null;O=N,v(L,N,!0),X||k()});async function T(){if(!C){C=!0;try{const N=await za(),X={x:ar(N.config,d),y:ar(N.config,p)};O=X,v(L,X,!0)}catch{}finally{C=!1}}}function w(N,X){const de=t.draft.number(N)+X;de=2?"s":""}`}function G(N,X){const de=qr(X);de!==null&&t.draft.set(N,de)}async function Y(N){if(!e($)){v(W,N,!0),r().actionError="",r().notice="";try{const X=await r().protect(()=>fr(N));if(X===null)return;r().notice=X.message,await r().refresh()}finally{v(W,"")}}}var ne=To(),M=s(ne);Oe(M,{title:"Aperçu de l’étiquette",note:"Le même moteur que l’impression (A2) : le décalage se voit parce qu’il est cuit dans le bitmap. L’image porte le gabarit en cours d’édition, mais le décalage ENREGISTRÉ — jamais celui que les flèches sont en train de régler.",children:(N,X)=>{var de=Co(),he=ae(de);{var fe=_e=>{var Se=ko(),st=s(Se);f(()=>o(st,e(V))),l(_e,Se)};_(he,_e=>{e(V)!==""&&_e(fe)})}var oe=n(he,2),I=s(oe),Q=s(I),re=n(Q,2);{var ee=_e=>{var Se=qo(),st=s(Se);f(()=>o(st,e(A))),l(_e,Se)};_(re,_e=>{e(A)!==""&&_e(ee)})}var ve=n(I,2),ye=s(ve),Ie=n(s(ye),2),q=s(Ie),le=n(ye,2),ge=s(le);{let _e=u(()=>!e(E)||e(x)<=y);Te(ge,{label:"← 1 dot",get disabled(){return e(_e)},onrun:()=>w(d,-1)})}var ue=n(ge,2);{let _e=u(()=>!e(E));Te(ue,{label:"1 dot →",get disabled(){return e(_e)},onrun:()=>w(d,1)})}var ie=n(le,2);{var Ne=_e=>{var Se=So(),st=s(Se);f(()=>o(st,e(D))),l(_e,Se)};_(ie,_e=>{e(D)!==""&&_e(Ne)})}var je=n(ie,2),ke=n(s(je),2),Re=s(ke),We=n(je,2),xe=s(We);{let _e=u(()=>!e(E)||e(g)<=y);Te(xe,{label:"↑ 1 dot",get disabled(){return e(_e)},onrun:()=>w(p,-1)})}var De=n(xe,2);{let _e=u(()=>!e(E));Te(De,{label:"1 dot ↓",get disabled(){return e(_e)},onrun:()=>w(p,1)})}var rt=n(We,2);{var nt=_e=>{var Se=Lo(),st=s(Se);f(()=>o(st,e(H))),l(_e,Se)};_(rt,_e=>{e(H)!==""&&_e(nt)})}var pt=n(rt,4),ct=s(pt),dt=n(pt,2),ft=s(dt),bt=n(dt,2);{var et=_e=>{var Se=Eo();l(_e,Se)};_(bt,_e=>{e(E)||_e(et)})}f((_e,Se)=>{be(Q,"src",e(U)),o(q,_e),o(Re,Se)},[()=>h(e(x)),()=>h(e(g))]),Ht("load",Q,()=>v(A,"")),Ht("error",Q,()=>{R(e(U))}),Ce("change",ct,()=>k()),Da(ct,()=>e(j),_e=>v(j,_e)),Ce("change",ft,()=>k()),Da(ft,()=>e(S),_e=>v(S,_e)),l(N,de)},$$slots:{default:!0}});var b=n(M,2);Oe(b,{title:"Gabarit et impression",children:(N,X)=>{var de=Po(),he=ae(de);{let re=u(()=>$e(t.draft,i)),ee=u(()=>!e(E));at(he,{label:"Gabarit",path:i,get value(){return e(K)},hint:"Le gabarit reproduit à l’identique s’appelle weighing_identical (A1).",get fault(){return e(re)},get disabled(){return e(ee)},onchange:ve=>{t.draft.set(i,ve),k()}})}var fe=n(he,2);{let re=u(()=>t.draft.text("printer.options.darkness")),ee=u(()=>$e(t.draft,"printer.options.darkness")),ve=u(()=>!e(E));at(fe,{label:"Noircissement",path:"printer.options.darkness",kind:"number",get value(){return e(re)},hint:"Trop bas, l’étiquette pâlit au soleil ; trop haut, elle bave et le scanner refuse.",get fault(){return e(ee)},get disabled(){return e(ve)},onchange:ye=>G("printer.options.darkness",ye)})}var oe=n(fe,2);{let re=u(()=>t.draft.text("printer.options.speed")),ee=u(()=>$e(t.draft,"printer.options.speed")),ve=u(()=>!e(E));at(oe,{label:"Vitesse",path:"printer.options.speed",kind:"number",get value(){return e(re)},get fault(){return e(ee)},get disabled(){return e(ve)},onchange:ye=>G("printer.options.speed",ye)})}var I=n(oe,2);{let re=u(()=>t.draft.text("printer.options.copies")),ee=u(()=>$e(t.draft,"printer.options.copies")),ve=u(()=>!e(E));at(I,{label:"Exemplaires",path:"printer.options.copies",kind:"number",get value(){return e(re)},hint:"Un client repart avec une étiquette : deux exemplaires se justifient, pas se devinent.",get fault(){return e(ee)},get disabled(){return e(ve)},onchange:ye=>G("printer.options.copies",ye)})}var Q=n(I,2);ze(Q,21,()=>z,re=>re.what,(re,ee)=>{{let ve=u(()=>e(W)===e(ee).what);Te(re,{get act(){return e(ee).what},get label(){return e(ee).label},protected:!0,get busy(){return e(ve)},get disabled(){return e($)},onrun:()=>{Y(e(ee).what)}})}}),l(N,de)},$$slots:{default:!0}}),l(a,ne),Me()}ot(["change"]);const Lr=[{rank:1,code:"OVERLOAD",label:"Surcharge",when:"La balance annonce elle-même OL, ou le poids brut dépasse la capacité.",severity:"Bloquant",blocking:!0,message:"La balance est en surcharge. Retirez votre article.",thresholds:[],switchPath:"",switchLabel:"",note:`Seuil : la capacité, réglée au garde-fou 9 sous « ${qt("limits.max_weight_g")} ».`},{rank:2,code:"MEASUREMENT_EXPIRED",label:"Poids périmé",when:"La mesure est plus vieille que la péremption, dans les deux modes de stabilité.",severity:"Bloquant",blocking:!0,message:"Poids indisponible. Patientez ou appelez un bénévole.",thresholds:[],switchPath:"",switchLabel:"",note:"Aucun seuil à régler : le poste calcule lui-même à partir du rythme de la balance."},{rank:3,code:"BASKET_MISSING",label:"Panier absent",when:"Le poids brut tombe dans la fenêtre négative du panier : il a été soulevé.",severity:"Bloquant",blocking:!0,message:"Le panier n'est pas sur la balance. Reposez-le.",thresholds:[{path:"limits.basket_min_g",label:"Bas de la fenêtre du panier",hint:"En grammes, NÉGATIF : c’est le poids du panier que la balance a perdu."},{path:"limits.basket_max_g",label:"Haut de la fenêtre du panier",hint:"Négatif lui aussi, et plus proche de zéro que le bas."}],switchPath:"limits.basket_check_enabled",switchLabel:"Ce poste travaille avec un panier taré",note:"La règle s’active ou non, en bloc : il n’y a pas de demi-mesure à régler."},{rank:4,code:"SCALE_EMPTY",label:"Plateau vide",when:"Le poids brut ne sort pas de la bande « il n’y a rien sur le plateau ».",severity:"Bloquant — un filet, hors parcours nominal",blocking:!0,message:"Posez votre produit.",thresholds:[{path:"limits.empty_max_g",label:"Plateau considéré vide",hint:"En dessous, le poste considère qu’il n’y a rien sur le plateau."}],switchPath:"",switchLabel:"",note:"Toucher une tuile sur un plateau vide ARME la sélection au lieu d’être refusé : la règle reste évaluée pour la saisie manuelle et les chemins dérivés."},{rank:5,code:"TARE_REQUIRED",label:"Remise à zéro nécessaire",when:"Le brut est sous la bande du plateau vide, et hors de la fenêtre du panier.",severity:"Bloquant",blocking:!0,message:"La balance doit être remise à zéro.",thresholds:[],switchPath:"",switchLabel:"",note:`Seuil : la valeur négative de celui du garde-fou 4, « ${qt("limits.empty_max_g")} ».`},{rank:6,code:"WEIGHT_UNSTABLE",label:"Pesée instable",when:"La trame déclare la mesure instable.",severity:"Information par défaut (A3)",blocking:!1,message:"Pesée en cours…",thresholds:[],switchPath:"",switchLabel:"",note:"La sévérité suit l’exigence de stabilité : elle passe à Bloquant quand celle-ci est réglée sur « blocking ». L’impression n’est jamais bloquée par défaut."},{rank:7,code:"TARE_INVALID",label:"Emballage incohérent",when:"Une tare a été saisie, et elle atteint la pesée ou dépasse le maximum.",severity:"Bloquant",blocking:!0,message:"Le poids de l'emballage est supérieur ou égal à la pesée.",thresholds:[{path:"limits.max_tare_g",label:"Tare maximum",hint:"Une tare plus lourde que le maximum est une faute de frappe."}],switchPath:"",switchLabel:"",note:""},{rank:8,code:"WEIGHT_TOO_LOW",label:"Poids trop faible",when:"Vente au poids : le NET est strictement positif et ne dépasse pas le plancher.",severity:"Bloquant",blocking:!0,message:"La balance doit être retarée, ou l'emballage est trop lourd.",thresholds:[{path:"limits.min_weight_g",label:"Poids minimum",hint:"Une dérogation par produit existe, dans l’onglet Catalogue."}],switchPath:"",switchLabel:"",note:""},{rank:9,code:"WEIGHT_TOO_HIGH",label:"Poids trop élevé",when:"Le NET dépasse la capacité — strictement, pour que la capacité reste atteignable.",severity:"Bloquant",blocking:!0,message:"{{.Weight}} kg, ça paraît un peu lourd !",thresholds:[{path:"limits.max_weight_g",label:"Poids maximum",hint:"C’est la capacité du champ NNDDD du code-barres, pas un seuil de vraisemblance."}],switchPath:"",switchLabel:"",note:""},{rank:10,code:"UNITS_OUT_OF_RANGE",label:"Nombre d’unités hors plage",when:"Vente à l’unité : la quantité sort de la plage.",severity:"Bloquant",blocking:!0,message:"{{.Quantity}} unités, ça paraît un peu beaucoup !",thresholds:[{path:"limits.min_units",label:"Unités minimum",hint:""},{path:"limits.max_units",label:"Unités maximum",hint:""}],switchPath:"",switchLabel:"",note:""},{rank:11,code:"AMOUNT_OUT_OF_CAPACITY",label:"Montant hors capacité du code-barres",when:"La charge utile encode un PRIX, et il dépasse ce que le champ peut porter.",severity:"Bloquant",blocking:!0,message:"Prix trop élevé pour le code-barres.",thresholds:[{path:"limits.max_amount_cents",label:"Montant maximum",hint:"En centimes. Aucun préfixe du plan livré n’encode un prix : la règle est éprouvée sans qu’aucun produit puisse l’atteindre."}],switchPath:"",switchLabel:"",note:""},{rank:12,code:"ZERO_PRICE",label:"Prix nul",when:"Le montant du tarif imprimé en grand vaut zéro.",severity:"Bloquant",blocking:!0,message:"Prix nul. Appelez un bénévole.",thresholds:[],switchPath:"",switchLabel:"",note:"Aucun seuil : un produit à 0 € est une anomalie sans nuance."},{rank:13,code:"LIGHT_PRODUCT_ALLOWED",label:"Produit léger autorisé",when:"Le garde-fou 8 n’a pas déclenché grâce à la dérogation du produit.",severity:"Information",blocking:!1,message:"",thresholds:[],switchPath:"",switchLabel:"",note:"Aucun seuil général : c’est la dérogation par produit, listée plus bas et posée depuis l’onglet Catalogue. Rien ne s’affiche au client ; l’id du produit est journalisé."},{rank:14,code:"PRODUCT_WITHDRAWN",label:"Produit retiré",when:"Quelqu’un a décidé de ne plus proposer ce produit.",severity:"Bloquant",blocking:!0,message:"Ce produit n'est pas disponible.",thresholds:[],switchPath:"",switchLabel:"",note:"Aucun seuil : c’est une décision humaine, prise depuis l’onglet Catalogue. Aucune règle d’import ne peut la déduire."}],jo=["{{.Weight}}","{{.Quantity}}"];function Ro(a){return Lr.find(t=>t.code===a)?.label??"Garde-fou inconnu de cet écran"}function Oo(a){return a==="blocking"?"Bloquant":a==="info"?"Information":"Sévérité inconnue de cet écran"}var Ao=c('

                                                          Rien ne s’affiche au client : c’est une information.

                                                          '),No=c('

                                                          '),Io=c('
                                                          '),Do=c('

                                                          '),Fo=c('
                                                        • '),Uo=c(`

                                                            Un seuil vidé garde la valeur du fichier : il n’écrit pas zéro, et la case la retrouve + dès qu’on quitte le champ. Pour changer un seuil, on tape l’autre valeur.

                                                            `,1);function Mo(a,t){Ue(t,!0);function r(z,m){const j=qr(m);j!==null&&t.draft.set(z,j)}var i=Uo(),d=ae(i);ze(d,21,()=>Lr,z=>z.code,(z,m)=>{var j=Fo(),S=s(j),A=s(S),L=s(A),W=n(A,2),O=s(W),C=n(W,2),K=s(C),U=n(C,2),E=s(U),x=n(S,2),g=s(x),D=n(x,2);{var H=F=>{var h=Ao();l(F,h)},V=F=>{var h=No(),G=s(h);f(()=>o(G,`« ${e(m).message??""} »`)),l(F,h)};_(D,F=>{e(m).message===""?F(H):F(V,-1)})}var $=n(D,2);{var T=F=>{{let h=u(()=>t.draft.flag(e(m).switchPath));Na(F,{get path(){return e(m).switchPath},get label(){return e(m).switchLabel},get on(){return e(h)},onchange:G=>t.draft.set(e(m).switchPath,G)})}};_($,F=>{e(m).switchPath!==""&&F(T)})}var w=n($,2);ze(w,17,()=>e(m).thresholds,F=>F.path,(F,h)=>{var G=Io(),Y=s(G);{let ne=u(()=>t.draft.text(e(h).path));at(Y,{get label(){return e(h).label},get path(){return e(h).path},kind:"number",get value(){return e(ne)},get hint(){return e(h).hint},onchange:M=>r(e(h).path,M)})}Ce("focusout",G,ne=>Sr(ne.target,t.draft.text(e(h).path))),l(F,G)});var k=n(w,2);{var R=F=>{var h=Do(),G=s(h);f(()=>o(G,e(m).note)),l(F,h)};_(k,F=>{e(m).note!==""&&F(R)})}f(F=>{be(j,"data-code",e(m).code),o(L,e(m).rank),o(O,e(m).label),o(K,e(m).code),be(U,"data-blocking",F),o(E,e(m).severity),o(g,e(m).when)},[()=>String(e(m).blocking)]),l(z,j)});var p=n(d,4),y=s(p);f(z=>o(y,`Les marqueurs ${z??""} sont remplacés par les valeurs de la pesée au + moment où le message s’affiche.`),[()=>jo.join(" et ")]),l(a,i),Me()}ot(["focusout"]);function Wo(a){const t=a.value("pricing.tiers");return Array.isArray(t)?t.map(r=>{const i=r??{},d=i.discount_percent;return{code:String(i.code??""),label:String(i.label??""),abbrev:String(i.abbrev??""),written:d===void 0?null:String(d),discount:Bo(d)?d:null,rank:Number(i.rank??0)}}):[]}function Bo(a){return typeof a!="number"||!Number.isFinite(a)||a<0||a>100?!1:Math.abs(a*10-Math.round(a*10))<1e-9}function rr(a){return a===null?"":String(a).replace(".",",")}function Vo(a){const t=1e3-Math.round(a*10);return`${String(Math.trunc(t/100))},${String(t%100).padStart(2,"0")}`}var Go=c('

                                                            Aucun tarif déclaré dans la configuration lue.

                                                            '),Ho=c('Prix du catalogue Odoo — pas de remise'),nr=c(' '),Jo=c(' % ',1),Ko=c(' '),$o=c(`

                                                            CodeLibelléAbrégéRemiseOrdre

                                                            Un champ vidé garde la valeur du fichier : il n’écrit pas zéro, et la case la + retrouve dès qu’on quitte le champ. Une remise effacée serait le plein tarif pour + tous les adhérents.

                                                            `,1);function Yo(a,t){Ue(t,!0);const r=u(()=>Wo(t.draft)),i=u(()=>String(t.draft.value("pricing.reference_code")??""));function d(S){return S<=1?`${String(S)} tarif déclaré`:`${String(S)} tarifs déclarés`}function p(S,A){const L=xo(A);L!==null&&t.draft.set(S,L)}var y=Tt(),z=ae(y);{var m=S=>{var A=Go();l(S,A)},j=S=>{var A=$o(),L=ae(A),W=s(L),O=n(L,2),C=s(O),K=n(s(C));ze(K,21,()=>e(r),da,(U,E,x)=>{var g=Ko(),D=s(g),H=s(D),V=n(D),$=s(V);be($,"aria-label",`Libellé du tarif ${x+1}`);var T=n(V),w=s(T);be(w,"aria-label",`Abrégé du tarif ${x+1}`);var k=n(T),R=s(k);{var F=b=>{var N=Ho();l(b,N)},h=b=>{var N=nr(),X=s(N);f(()=>o(X,`${e(E).written??""} — le tarif de référence est le prix du catalogue : il + ne peut pas porter de remise, et l’enregistrement la refusera.`)),l(b,N)},G=b=>{var N=nr(),X=s(N);f(()=>o(X,`${e(E).written??""} — une remise s’écrit au dixième de point ; celle-ci se + change dans le fichier de configuration.`)),l(b,N)},Y=b=>{var N=Jo(),X=ae(N);be(X,"aria-label",`Remise du tarif ${x+1}`);var de=n(X,2),he=s(de);f((fe,oe)=>{Gt(X,fe),o(he,`un produit à 10,00 €/kg s’affiche ${oe??""} €/kg`)},[()=>rr(e(E).discount??0),()=>Vo(e(E).discount??0)]),Ce("input",X,fe=>p(`pricing.tiers.${String(x)}.discount_percent`,fe.currentTarget.value)),Ce("focusout",X,fe=>Sr(fe.currentTarget,rr(e(E).discount??0))),l(b,N)};_(R,b=>{e(E).code===e(i)&&e(E).written===null?b(F):e(E).code===e(i)?b(h,1):e(E).written!==null&&e(E).discount===null?b(G,2):b(Y,-1)})}var ne=n(k),M=s(ne);f(()=>{o(H,e(E).code),Gt($,e(E).label),Gt(w,e(E).abbrev),o(M,e(E).rank)}),Ce("input",$,b=>t.draft.set(`pricing.tiers.${String(x)}.label`,b.currentTarget.value)),Ce("input",w,b=>t.draft.set(`pricing.tiers.${String(x)}.abbrev`,b.currentTarget.value)),l(U,g)}),f(U=>o(W,`${U??""}.`),[()=>d(e(r).length)]),l(S,A)};_(z,S=>{e(r).length===0?S(m):S(j,-1)})}l(a,y),Me()}ot(["input","focusout"]);var Xo=c('

                                                            Aucune dérogation : la limite générale s’applique à tous les produits.

                                                            '),Qo=c(`

                                                            Les noms de produits n’ont pas pu être lus : le catalogue en service n’a pas + répondu. Les identifiants Odoo restent affichés.

                                                            `),Zo=c(`Produit retiré : le garde-fou 14 refuse le produit avant que cette + dérogation ait un sens.`),eu=c('
                                                          1. '),tu=c('

                                                              ',1);function au(a,t){Ue(t,!0);function r(L){return Aa(t.names[L],t.namesState,"Nom non lu")}const i=20,d=u(()=>t.waivers.filter(L=>!L.offered)),p=u(()=>t.waivers.slice(0,i)),y=u(()=>`${se(e(p).length)} ${e(p).length>1?"lignes affichées":"ligne affichée"} sur ${se(t.waivers.length)} `+(e(d).length===0?`${t.waivers.length>1?"dérogations en vigueur":"dérogation en vigueur"}.`:`${t.waivers.length>1?"dérogations enregistrées":"dérogation enregistrée"}, dont ${se(e(d).length)} sur un produit retiré, sans effet : le garde-fou 14 refuse le produit avant que le 8 ait un sens.`)+(t.waivers.length>e(p).length?" Les autres se lisent produit par produit depuis l’onglet Catalogue.":""));function z(L){return`plancher ${se(L)} g : refusé à ${se(L)} g et en dessous`}var m=Tt(),j=ae(m);{var S=L=>{var W=Xo();l(L,W)},A=L=>{var W=tu(),O=ae(W),C=s(O),K=n(O,2);{var U=x=>{var g=Qo();l(x,g)};_(K,x=>{t.namesState==="unread"&&x(U)})}var E=n(K,2);ze(E,21,()=>e(p),x=>x.product_id,(x,g)=>{var D=eu(),H=s(D),V=s(H),$=n(H,2),T=s($),w=n($,2),k=s(w),R=n(w,2),F=s(R),h=n(R,2),G=s(h),Y=n(h,2);{var ne=M=>{var b=Zo();l(M,b)};_(Y,M=>{e(g).offered||M(ne)})}f((M,b,N,X)=>{be(D,"data-withdrawn",M),o(V,b),o(T,e(g).product_id),o(k,N),o(F,e(g).reason),o(G,`${X??""}, ${e(g).decided_by??""}`)},[()=>String(!e(g).offered),()=>r(e(g).product_id),()=>z(e(g).min_weight_g??0),()=>va(e(g).decided_at)]),l(x,D)}),f(()=>o(C,e(y))),l(L,W)};_(j,L=>{t.waivers.length===0?L(S):L(A,-1)})}l(a,m),Me()}var ru=c(" ",1),nu=c(`

                                                              Les deux arrondis sont distincts parce qu’ils tombent à des endroits différents du + calcul : arrondir le prix au kilo puis le multiplier, ou multiplier puis arrondir, ne + donne pas le même centime sur une étiquette. Le poste applique l’arrondi commercial, + et l’écart se voit au centime près sur la même pesée.

                                                              `,1),su=c(`

                                                              Aucun verdict en ce moment : ces lignes portent sur la pesée EN COURS, et le poste + est au repos. Elles apparaissent le temps d’un cycle, quand un client pose son sac + et touche une tuile.

                                                              `),lu=c(' '),iu=c(' ',1),ou=c('
                                                            • '),uu=c('

                                                                ',1),cu=c(`

                                                                L’ordre compte : le premier verdict bloquant décide de ce que le client lit. Les + garde-fous 1 à 7 portent sur l’état de la balance, c’est-à-dire le poids brut ; les + garde-fous 8 à 14 portent sur la vente, c’est-à-dire le net. Le code en gris est + celui du journal, celui qu’on lit au téléphone.

                                                                Ce que les garde-fous disent de la pesée en cours

                                                                `,1),du=c(`

                                                                Le plan de numérotation n’est PAS ici, et c’est tout l’intérêt : préfixes, largeur de + la référence, largeur de la charge utile, décimales et mode de vente sont une + constante du binaire, indexée par préfixe et vérifiée au démarrage. + Un champ qui change le SENS du code lu par la caisse n’est pas un réglage, c’est un + contrat externe : il change avec une version du binaire, relue et testée, jamais + depuis l’écran d’un poste.

                                                                `,1),vu=c('
                                                                ');function pu(a,t){Ue(t,!0);const r=u(()=>t.health.state.diagnostics),i=u(()=>t.health.decisions.filter(C=>C.min_weight_g!==null));let d=B(vt({})),p=B("loading");y();async function y(){try{const C=await cr();v(d,Object.fromEntries(C.products.map(K=>[K.id,K.name])),!0),v(p,"read")}catch{v(p,"unread")}}function z(C){return Aa(e(d)[C],e(p),"Nom non lu")}const m=u(()=>`${se(e(r).length)} ${e(r).length>1?"verdicts":"verdict"} sur les quatorze garde-fous.`);var j=vu(),S=s(j);Oe(S,{title:"Grille de tarifs",note:"Un second tarif n’est pas une case à cocher : c’est une ligne de plus dans cette grille.",children:(C,K)=>{var U=ru(),E=ae(U);Yo(E,{get draft(){return t.draft}});var x=n(E,2);{let D=u(()=>t.draft.text("pricing.primary_code"));at(x,{label:"Tarif imprimé en grand",path:"pricing.primary_code",get value(){return e(D)},hint:"Le prix que le client lit sur l’étiquette (A7).",onchange:H=>t.draft.set("pricing.primary_code",H)})}var g=n(x,2);{let D=u(()=>t.draft.text("pricing.reference_code"));at(g,{label:"Tarif qui serait encodé si le code-barres portait un prix",path:"pricing.reference_code",get value(){return e(D)},hint:"Le plan livré ne porte pas de prix, mais un poids ou un nombre d’unités : la caisse retrouve le prix par la référence, dans Odoo, et ne doit jamais sous-facturer.",onchange:H=>t.draft.set("pricing.reference_code",H)})}l(C,U)},$$slots:{default:!0}});var A=n(S,2);Oe(A,{title:"Les deux arrondis",children:(C,K)=>{var U=nu(),E=ae(U);{let g=u(()=>t.draft.text("pricing.unit_price_rounding"));at(E,{label:"Arrondi du prix unitaire dérivé",path:"pricing.unit_price_rounding",get value(){return e(g)},hint:"Il s’applique au prix au kilo calculé par la remise.",onchange:D=>t.draft.set("pricing.unit_price_rounding",D)})}var x=n(E,2);{let g=u(()=>t.draft.text("pricing.amount_rounding"));at(x,{label:"Arrondi du montant",path:"pricing.amount_rounding",get value(){return e(g)},hint:"Il s’applique au montant de l’étiquette.",onchange:D=>t.draft.set("pricing.amount_rounding",D)})}l(C,U)},$$slots:{default:!0}});var L=n(A,2);Oe(L,{title:"Les quatorze garde-fous, dans l’ordre d’évaluation",note:"Le seuil se modifie ici. La sévérité ne se règle pas : elle dit si le poste refuse ou avertit, et cela ne dépend pas du magasin. Le message affiché au client n’est pas encore modifiable depuis cet écran.",children:(C,K)=>{var U=cu(),E=n(ae(U),2);Mo(E,{get draft(){return t.draft}});var x=n(E,4);{var g=H=>{var V=su();l(H,V)},D=H=>{var V=uu(),$=ae(V),T=s($),w=n($,2);ze(w,21,()=>e(r),k=>k.code,(k,R)=>{var F=ou(),h=s(F),G=s(h),Y=n(h,2),ne=s(Y),M=n(Y,2),b=s(M),N=n(M,2);{var X=fe=>{var oe=lu(),I=s(oe);f(()=>o(I,`« ${e(R).message??""} »`)),l(fe,oe)};_(N,fe=>{e(R).message!==""&&fe(X)})}var de=n(N,2);{var he=fe=>{var oe=iu(),I=ae(oe),Q=s(I),re=n(I,2),ee=s(re);f(ve=>{o(Q,ve),o(ee,e(R).product_id)},[()=>z(e(R).product_id)]),l(fe,oe)};_(de,fe=>{e(R).product_id!==""&&fe(he)})}f((fe,oe,I)=>{be(F,"data-blocking",fe),o(G,oe),o(ne,e(R).code),o(b,I)},[()=>String(e(R).blocking),()=>Ro(e(R).code),()=>Oo(e(R).severity)]),l(k,F)}),f(k=>{be($,"data-verdicts",k),o(T,e(m))},[()=>String(e(r).length)]),l(H,V)};_(x,H=>{e(r).length===0?H(g):H(D,-1)})}l(C,U)},$$slots:{default:!0}});var W=n(L,2);Oe(W,{title:"Code-barres",note:"Un seul réglage, et c’est voulu : le reste du plan de numérotation n’est pas de la configuration.",children:(C,K)=>{var U=du(),E=ae(U);{let x=u(()=>t.draft.flag("barcode.verify_reference_check_digit"));Na(E,{path:"barcode.verify_reference_check_digit",label:"Refuser une référence dont la clé de contrôle est fausse",hint:"Décoché, le poste recalcule une clé juste sur une référence fausse, en silence — et la caisse encaisse un autre article.",get on(){return e(x)},onchange:g=>t.draft.set("barcode.verify_reference_check_digit",g)})}l(C,U)},$$slots:{default:!0}});var O=n(W,2);Oe(O,{title:"Dérogations de poids minimum",note:"En lecture ici ; elles se modifient depuis l’onglet Catalogue, là où se trouve le produit.",children:(C,K)=>{au(C,{get waivers(){return e(i)},get names(){return e(d)},get namesState(){return e(p)}})},$$slots:{default:!0}}),l(a,j),Me()}var fu=c(" "),gu=c(' '),mu=c('
                                                                ChampEn serviceDans le fichier
                                                                ');function hu(a,t){Ue(t,!0);var r=mu(),i=s(r),d=n(s(i));ze(d,21,()=>t.rows,p=>p.path,(p,y)=>{const z=u(()=>qt(e(y).path));var m=gu(),j=s(m),S=s(j),A=n(S);{var L=U=>{var E=fu(),x=s(E);f(()=>o(x,e(y).path)),l(U,E)};_(A,U=>{St.showTechnicalNames&&e(z)!==e(y).path&&U(L)})}var W=n(j),O=s(W),C=n(W),K=s(C);f(()=>{be(m,"data-path",e(y).path),o(S,`${e(z)??""} `),o(O,e(y).before),o(K,e(y).after)}),l(p,m)}),l(a,r),Me()}var _u=c(" "),bu=c(' '),wu=c("
                                                              • "),yu=c(`

                                                                  Recopier reste possible : les valeurs entrent dans le brouillon, où elles se + corrigent champ par champ avant l’enregistrement.

                                                                  `);function xu(a,t){Ue(t,!0);const r=20,i=u(()=>t.faults.slice(0,r)),d=u(()=>ca(e(i).length,t.faults.length,"contrôle refuse une clé","contrôles refusent une clé"));var p=yu(),y=s(p),z=s(y),m=n(y,2);ze(m,21,()=>e(i),da,(j,S)=>{var A=wu(),L=s(A),W=s(L),O=n(L,2);{var C=x=>{var g=_u(),D=s(g);f(()=>o(D,e(S).field)),l(x,g)};_(O,x=>{St.showTechnicalNames&&x(C)})}var K=n(O),U=n(K);{var E=x=>{var g=bu(),D=s(g);f(H=>o(D,`Valeurs acceptées : ${H??""}.`),[()=>e(S).allowed.join(", ")]),l(x,g)};_(U,x=>{e(S).allowed!==void 0&&e(S).allowed.length>0&&x(E)})}f(x=>{o(W,x),o(K,` ${e(S).message??""} `)},[()=>qt(e(S).field)]),l(j,A)}),f(()=>o(z,`Ce fichier serait refusé en l’état : ${e(d)??""}`)),l(a,p),Me()}const ku=2e3,qu=300*1e3;async function Su(){const a=Date.now()+qu;for(;Date.now()setTimeout(t,ku));try{if((await fetch("/healthz",{cache:"no-store"})).ok)return!0}catch{}}return!1}var Lu=c('

                                                                  ',1),Eu=c(`

                                                                  Touchez de nouveau pour confirmer. Rien ne défait un redémarrage une fois + l’ordinateur parti.

                                                                  `),Cu=c(" ",1),Pu=c('

                                                                  '),Tu=c('
                                                                • '),zu=c('
                                                                    '),ju=c(`

                                                                    Trois gestes de reprise, du plus doux au plus brutal. Le premier ne coupe rien.

                                                                    Relire le fichier de configuration
                                                                    Met en service config.json tel qu’il est sur le disque, sans arrêter le + poste. À utiliser après une modification faite à la main dans le fichier.
                                                                    Redémarrer le poste
                                                                    Arrête l’application et la relance. La pesée est interrompue quelques secondes ; + l’écran client revient tout seul.
                                                                    Redémarrer l’ordinateur
                                                                    Redémarre la machine entière. Comptez une minute avant que l’écran revienne.
                                                                    `,1);function Ru(a,t){Ue(t,!0);let r=B(""),i=B(""),d=B(vt([])),p=B(!1),y=B(0),z=B(0),m=B(!1);const j=u(()=>e(y)>0);wt(()=>{if(!e(j))return;const O=setInterval(()=>{v(z,Math.max(0,Math.round((e(y)-Date.now())/1e3)),!0)},1e3);return()=>clearInterval(O)});async function S(){v(r,"reload-config"),v(i,""),v(d,[],!0);try{const O=await t.admin.protect(async()=>{try{return await pn()}catch(C){if(fa(C))throw C;return t.admin.report(C),v(d,t.admin.lastFaults,!0),null}});if(O===null)return;v(i,`Le fichier est en service. Empreinte ${O.config_fingerprint}.`)}finally{v(r,"")}}async function A(){v(r,"restart"),v(i,""),v(d,[],!0);try{const O=await t.admin.protect(()=>fn());if(O===null)return;v(i,O.message,!0),v(p,!0),v(i,await Su()?"Le poste est revenu.":"Le poste n’a pas répondu dans les cinq minutes. Allez le voir.",!0)}finally{v(r,""),v(p,!1)}}async function L(){if(!e(m)){v(m,!0);return}v(r,"reboot"),v(i,""),v(d,[],!0);try{const O=await t.admin.protect(()=>gn());if(O===null)return;v(y,Date.parse(O.at),!0),v(z,O.seconds_left,!0)}finally{v(r,""),v(m,!1)}}async function W(){v(r,"cancel-reboot");try{const O=await t.admin.protect(()=>mn());if(O===null)return;v(y,0),v(i,O.message,!0)}finally{v(r,"")}}Oe(a,{title:"Maintenance",children:(O,C)=>{var K=ju(),U=n(ae(K),2),E=n(s(U),2),x=n(s(E),3);{let h=u(()=>e(r)==="reload-config");Te(x,{kind:"write",act:"reload-config",label:"Relire le fichier",protected:!0,get busy(){return e(h)},onrun:S})}var g=n(E,4),D=n(s(g));{let h=u(()=>e(r)==="restart"||e(p));Te(D,{kind:"write",act:"restart",label:"Redémarrer le poste",protected:!0,get busy(){return e(h)},onrun:A})}var H=n(g,4),V=n(s(H));{var $=h=>{var G=Lu(),Y=ae(G),ne=s(Y),M=n(Y,2);{let b=u(()=>e(r)==="cancel-reboot");Te(M,{kind:"write",act:"cancel-reboot",label:"Annuler",get busy(){return e(b)},onrun:W})}f(()=>o(ne,`L’ordinateur redémarre dans ${e(z)??""} seconde${e(z)>1?"s":""}.`)),l(h,G)},T=h=>{var G=Cu(),Y=ae(G);{var ne=b=>{var N=Eu();l(b,N)};_(Y,b=>{e(m)&&b(ne)})}var M=n(Y,2);{let b=u(()=>e(m)?"Confirmer le redémarrage":"Redémarrer l’ordinateur"),N=u(()=>e(r)==="reboot");Te(M,{kind:"destructive",act:"reboot",get label(){return e(b)},protected:!0,get busy(){return e(N)},onrun:L})}l(h,G)};_(V,h=>{e(j)?h($):h(T,-1)})}var w=n(U,2);{var k=h=>{var G=Pu(),Y=s(G);f(()=>o(Y,e(i))),l(h,G)};_(w,h=>{e(i)!==""&&h(k)})}var R=n(w,2);{var F=h=>{var G=zu();ze(G,21,()=>e(d),Y=>Y.field,(Y,ne)=>{var M=Tu(),b=s(M),N=s(b),X=n(b);f(()=>{o(N,e(ne).field),o(X,` — ${e(ne).message??""}`)}),l(Y,M)}),l(h,G)};_(R,h=>{e(d).length>0&&h(F)})}l(O,K)},$$slots:{default:!0}}),Me()}function Ou(a,t){const r=[];for(const i of[...La(a),...La(t)]){if(r.some(y=>y.path===i))continue;const d=sr(ga(a,i)),p=sr(ga(t,i));d!==p&&r.push({path:i,before:d,after:p})}return r}function ga(a,t){let r=a;for(const i of t.split(".")){if(r===null||typeof r!="object")return;r=r[i]}return r}function La(a,t=""){if(a===null||typeof a!="object"||Array.isArray(a))return t===""?[]:[t];const r=[];for(const[i,d]of Object.entries(a))r.push(...La(d,t===""?i:`${t}.${i}`));return r}function sr(a){return a===void 0?"—":a===null?"vide":typeof a=="boolean"?a?"oui":"non":typeof a=="object"?JSON.stringify(a):String(a)}const Au=new Set(["modified_at","catalog.options.password"]),Nu=[{path:"station.name",name:"le nom du poste"},{path:"network.listen",name:"l’adresse d’écoute"},{path:"scale.options.port",name:"le port de la balance"},{path:"printer.options.queue",name:"la file d’impression"},{path:"printer.options.path",name:"le nœud d’impression"},{path:"printer.options.address",name:"l’adresse de l’imprimante"},{path:"catalog.options.url",name:"l’adresse du partage"},{path:"catalog.options.username",name:"le compte du partage"},{path:"catalog.images.path",name:"le chemin des images"}];function Iu(a,t){return a===null||t===null?[]:Ou(a,t).filter(r=>!Au.has(r.path))}function Du(a,t){if(a===null||t===null)return[];const r=a,i=t;return Nu.filter(d=>lr(i,d.path)&&!lr(r,d.path))}function lr(a,t){const r=ga(a,t);return r==null||r===""?!0:typeof r!="object"||Array.isArray(r)?!1:Object.keys(r).length===0}function Fu(a){return a.length<2?a.join(""):`${a.slice(0,-1).join(", ")} et ${a[a.length-1]??""}`}async function Uu(a,t){const i=await fetch(`/admin/api/config/export?hardware=${t?"1":"0"}`,{headers:{accept:"application/json"}});if(!i.ok)throw new Pt(i.status,Er(await i.text(),"L’export"));return{name:Wu(a,t),blob:await i.blob()}}async function Mu(a){const t=await fetch("/admin/api/config/import",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(a)}),r=await t.text();if(!t.ok)throw new Pt(t.status,Er(r,"L’import"));return JSON.parse(r)}function Wu(a,t){const r=t?"":"-sans-materiel";return`config-poste${String(a)}${r}-${Bu()}.json`}function Bu(){const a=new Date,t=String(a.getMonth()+1).padStart(2,"0"),r=String(a.getDate()).padStart(2,"0");return`${String(a.getFullYear())}-${t}-${r}`}function Vu(a,t){const r=URL.createObjectURL(t),i=document.createElement("a");i.href=r,i.download=a,document.body.appendChild(i),i.click(),i.remove(),setTimeout(()=>{URL.revokeObjectURL(r)},0)}function Er(a,t){try{const r=JSON.parse(a);if(typeof r?.message=="string"&&r.message!=="")return r.message}catch{}return`${t} a été refusé par le poste.`}var Gu=c('
                                                                    Empreinte de la configuration en service
                                                                    Version du binaire
                                                                    Répertoire de données
                                                                    Espace disque
                                                                    ',1),Hu=c('

                                                                    '),Ju=c(`

                                                                    Ce fichier n’a été comparé à rien : la configuration en service n’a pas pu être + lue. Ni « identique », ni « n champs changent » — ce que ce fichier changerait + sur ce poste reste inconnu.

                                                                    `),Ku=c(`

                                                                    Ce fichier décrit la même configuration que celle en service : il n’y a rien à + recopier. C’est ce qu’on veut lire à la fin d’un clonage. Deux champs ne sont pas + comparés : la date du dernier enregistrement, que chaque poste écrit lui-même, et + le mot de passe du catalogue, qu’aucun des deux ne porte en clair.

                                                                    `),$u=c('

                                                                    '),Yu=c(`

                                                                    Recopier n’applique rien : les valeurs entrent dans le brouillon, et c’est + « Enregistrer » qui les met en service.

                                                                    `,1),Xu=c('

                                                                    ',1),Qu=c(`

                                                                    L’export emporte encore l’empreinte du mot de passe : c’est la seule lecture que le + poste garde derrière la clé. L’import, lui, est lu PAR LE POSTE, qui écarte les deux + secrets et le numéro de poste avant de dire ce qui changerait.

                                                                    `,1),Zu=c('

                                                                    Lecture des versions enregistrées…

                                                                    '),ec=c(`

                                                                    Les versions enregistrées n’ont pas pu être lues : cette liste ne dit donc rien de + ce que ce poste garde. Ce n’est pas « aucune version ».

                                                                    `),tc=c('

                                                                    Aucune version enregistrée : ce poste n’a jamais été reconfiguré.

                                                                    '),ac=c('
                                                                  • '),rc=c(`

                                                                      Remettre une version en service remplace la configuration du poste sur-le-champ, et + ce qui n’a pas été enregistré est perdu : c’est le seul geste de cette page qui + change le poste tout de suite, et le seul qui garde ses 72 px.

                                                                      `,1),nc=c('
                                                                      ');function sc(a,t){Ue(t,!0);const r=Be(t,"admin",7),i=40,d=5;let p=B(vt([])),y=B("reading"),z=B(null),m=B(""),j=B(null),S=B(""),A=B(vt([])),L=B(""),W="";const O=u(()=>r().busy||e(L)!==""),C=u(()=>Iu(e(z),e(j))),K=u(()=>e(z)!==null&&e(j)!==null),U=u(()=>Du(e(z),e(j))),E=u(()=>e(C).slice(0,i)),x=u(()=>e(p).slice(0,d)),g=u(()=>ca(e(E).length,e(C).length,"champ qui change","champs qui changent")),D=u(()=>ca(e(x).length,e(p).length,"version enregistrée","versions enregistrées")),H=u(()=>e(C).length>1?`Recopier ces ${se(e(C).length)} champs dans le brouillon`:"Recopier ce champ dans le brouillon");V(),wt(()=>{const b=t.health.config_fingerprint;b!==W&&(W=b,$())});async function V(){const b=await r().load(_n);if(b===null){v(y,"unreadable");return}v(p,b,!0),v(y,"read")}async function $(){try{v(z,(await za()).config,!0),v(m,"")}catch(b){v(z,null),v(m,b instanceof Error?b.message:"Le poste n’a pas répondu.",!0)}}async function T(b){v(L,b?"export-all":"export-clone",!0),r().notice="",r().actionError="";try{const N=await r().protect(()=>Uu(t.health.station,b));if(N===null)return;Vu(N.name,N.blob),r().notice=`${N.name} est remis au navigateur : c’est lui qui l’enregistre, voyez ses téléchargements.`}finally{v(L,"")}}async function w(b){if(b==null)return;r().notice="",r().actionError="";let N;try{N=JSON.parse(await b.text())}catch{r().actionError=`${b.name} n’est pas un fichier JSON lisible.`;return}v(L,"import");try{const X=await r().protect(()=>Mu(N));if(X===null)return;v(j,X.config,!0),v(S,b.name,!0),v(A,X.faults,!0),await $(),r().notice=k(b.name)}finally{v(L,"")}}function k(b){return e(K)?e(C).length===0?`${b} décrit la même configuration que celle en service.`:`${b} est lu. Rien n’est appliqué : relisez le tableau.`:`${b} est lu, mais la configuration en service ne l’est pas : rien ne peut être comparé.`}async function R(){if(e(j)===null)return;const b=e(j),N=[...e(C)];for(const X of N)t.draft.set(X.path,ga(b,X.path));r().actionError="",r().notice="Le fichier est recopié dans le brouillon. Rien n’est appliqué avant « Enregistrer ».",await $()}async function F(b){v(L,`restore-${String(b)}`),r().notice="",r().actionError="";try{if(await r().protect(()=>bn(b))===null)return;await t.draft.load(),await V(),await $(),await r().refresh(),r().notice=`La version ${String(b)} est remise en service.`}finally{v(L,"")}}var h=nc(),G=s(h);Oe(G,{title:"Identité du poste",children:(b,N)=>{var X=Gu(),de=ae(X);{let ue=u(()=>t.draft.text("station.number")),ie=u(()=>$e(t.draft,"station.number")),Ne=u(()=>Qt(t.draft,"station.number"));at(de,{label:"Numéro du poste",path:"station.number",kind:"number",get value(){return e(ue)},get fault(){return e(ie)},get allowed(){return e(Ne)},hint:"C’est de lui que dérive le nom du fichier de catalogue attendu, flv_.csv.",onchange:je=>t.draft.set("station.number",Number(je))})}var he=n(de,2);{let ue=u(()=>t.draft.text("station.name")),ie=u(()=>$e(t.draft,"station.name")),Ne=u(()=>Qt(t.draft,"station.name"));at(he,{label:"Nom du poste",path:"station.name",get value(){return e(ue)},get fault(){return e(ie)},get allowed(){return e(Ne)},hint:"Ce que lit un bénévole : « Poste 2 — fruits ».",onchange:je=>t.draft.set("station.name",je)})}var fe=n(he,2);{let ue=u(()=>t.draft.text("station.coop")),ie=u(()=>$e(t.draft,"station.coop")),Ne=u(()=>Qt(t.draft,"station.coop"));at(fe,{label:"Coopérative",path:"station.coop",get value(){return e(ue)},get fault(){return e(ie)},get allowed(){return e(Ne)},onchange:je=>t.draft.set("station.coop",je)})}var oe=n(fe,2),I=n(s(oe),2),Q=s(I),re=n(I,4),ee=s(re),ve=n(re,4),ye=s(ve),Ie=n(ve,4),q=s(Ie);{var le=ue=>{var ie=lt("non mesuré");l(ue,ie)},ge=ue=>{var ie=lt();f((Ne,je,ke)=>o(ie,`${Ne??""} libres sur + ${je??""} — seuil d’alerte + ${ke??""} Mo`),[()=>Sa(t.health.disk.free_bytes),()=>Sa(t.health.disk.total_bytes),()=>se(t.health.disk.alert_mb)]),l(ue,ie)};_(q,ue=>{t.health.disk===null?ue(le):ue(ge,-1)})}f(()=>{o(Q,t.health.config_fingerprint),o(ee,t.health.version),o(ye,t.health.disk===null?"non publié par ce poste":t.health.disk.path)}),l(b,X)},$$slots:{default:!0}});var Y=n(G,2);Oe(Y,{title:"Exporter, importer",note:"Pour installer un autre poste : ce fichier emporte les tarifs, les garde-fous, l’étiquette, les catégories, et les réglages du matériel que les quatre postes partagent — le décalage d’étiquette, le noircissement, la vitesse, le débit de la balance. Reste ici ce qui désigne ce poste-ci ou ce magasin — le mot de passe, le code de secours, le numéro et le nom du poste, le port de la balance, la file d’impression, l’adresse du partage et son compte, le chemin des images et le réseau.",children:(b,N)=>{var X=Qu(),de=ae(X),he=s(de);{let q=u(()=>e(L)==="export-all");Te(he,{label:"Exporter tout",protected:!0,get busy(){return e(q)},get disabled(){return e(O)},onrun:()=>{T(!0)}})}var fe=n(he,2);{let q=u(()=>e(L)==="export-clone");Te(fe,{label:"Exporter sans le matériel",protected:!0,get busy(){return e(q)},get disabled(){return e(O)},onrun:()=>{T(!1)}})}var oe=n(fe,2);let I;var Q=s(oe),re=n(Q,3),ee=n(de,4);{var ve=q=>{var le=Hu(),ge=s(le);f(()=>o(ge,`La configuration en service n’a pas pu être lue : ${e(m)??""} La colonne + « En service » ne peut donc rien affirmer.`)),l(q,le)};_(ee,q=>{e(m)!==""&&q(ve)})}var ye=n(ee,2);{var Ie=q=>{var le=Xu(),ge=ae(le),ue=s(ge),ie=n(ge,2);{var Ne=xe=>{xu(xe,{get faults(){return e(A)}})};_(ie,xe=>{e(A).length>0&&xe(Ne)})}var je=n(ie,2);{var ke=xe=>{var De=Ju();l(xe,De)},Re=xe=>{var De=Ku();l(xe,De)},We=xe=>{var De=Yu(),rt=ae(De),nt=s(rt),pt=n(nt);{var ct=Se=>{var st=lt("Les autres sont dans le fichier, et « Recopier » les prend tous.");l(Se,st)};_(pt,Se=>{e(C).length>e(E).length&&Se(ct)})}var dt=n(rt,2);hu(dt,{get rows(){return e(E)}});var ft=n(dt,2);{var bt=Se=>{var st=$u(),Dt=s(st);f(zt=>o(Dt,`Ce fichier ne porte pas ${zt??""} : + l’export sans le matériel les retire, et l’import ne remet que le numéro du + poste. Les lignes correspondantes sont VIDES ci-dessus, et + « Recopier » recopie ce vide dans le brouillon.`),[()=>Fu(e(U).map(zt=>zt.name))]),l(Se,st)};_(ft,Se=>{e(U).length>0&&Se(bt)})}var et=n(ft,2),_e=s(et);Te(_e,{kind:"write",get label(){return e(H)},get disabled(){return e(O)},onrun:()=>{R()}}),f(()=>o(nt,`${e(g)??""} `)),l(xe,De)};_(je,xe=>{e(K)?e(C).length===0?xe(Re,1):xe(We,-1):xe(ke)})}f(()=>o(ue,`Fichier lu : ${e(S)??""}`)),l(q,le)};_(ye,q=>{e(j)!==null&&q(Ie)})}f(()=>{I=Nt(oe,1,"choose svelte-18wbxwu",null,I,{working:e(L)==="import",off:e(O)}),o(Q,`${e(L)==="import"?"Lecture du fichier…":"Importer un fichier"} `),re.disabled=e(O)}),Ce("change",re,q=>{w(q.currentTarget.files?.item(0))}),l(b,X)},$$slots:{default:!0}});var ne=n(Y,2);Oe(ne,{title:"Cinq versions restaurables",note:"Chaque enregistrement fait tourner les versions : la plus récente est la 1.",children:(b,N)=>{var X=Tt(),de=ae(X);{var he=Q=>{var re=Zu();l(Q,re)},fe=Q=>{var re=ec();l(Q,re)},oe=Q=>{var re=tc();l(Q,re)},I=Q=>{var re=rc(),ee=ae(re),ve=s(ee),ye=n(ee,2),Ie=s(ye);ze(Ie,21,()=>e(x),q=>q.version,(q,le)=>{var ge=ac(),ue=s(ge),ie=s(ue),Ne=n(ue,2),je=s(Ne),ke=n(Ne,2),Re=s(ke),We=n(ke,2);{let xe=u(()=>e(L)===`restore-${String(e(le).version)}`);Te(We,{kind:"destructive",label:"Remettre cette version en service",protected:!0,get busy(){return e(xe)},get disabled(){return e(O)},onrun:()=>{F(e(le).version)}})}f((xe,De)=>{o(ie,`version ${xe??""}`),o(je,De),o(Re,e(le).config_fingerprint)},[()=>se(e(le).version),()=>Lt(e(le).modified_at)]),l(q,ge)}),f(()=>o(ve,`${e(D)??""} Le poste n’en garde jamais plus de cinq.`)),l(Q,re)};_(de,Q=>{e(y)==="reading"?Q(he):e(y)==="unreadable"?Q(fe,1):e(p).length===0?Q(oe,2):Q(I,-1)})}l(b,X)},$$slots:{default:!0}});var M=n(ne,2);Ru(M,{get admin(){return r()}}),l(a,h),Me()}ot(["change"]);var lc=c('clé'),ic=c(' '),oc=c('');function Bt(a,t){const r=Be(t,"kind",3,"read"),i=Be(t,"hint",3,""),d=Be(t,"disabled",3,!1),p=Be(t,"engaged",3,!1),y=Be(t,"busy",3,!1),z=Be(t,"protected",3,!1);var m=oc();let j;var S=s(m),A=s(S),L=n(A);{var W=K=>{var U=lc();l(K,U)};_(L,K=>{z()&&K(W)})}var O=n(S,2);{var C=K=>{var U=ic(),E=s(U);f(()=>o(E,y()?"En cours…":i())),l(K,U)};_(O,K=>{i()!==""&&K(C)})}f(()=>{j=Nt(m,1,`big touch-target ${r()??""}`,"svelte-15nlt6i",j,{engaged:p(),busy:y()}),be(m,"data-kind",r()),m.disabled=d()||y(),o(A,`${t.label??""} `)}),Ce("click",m,function(...K){t.onrun?.apply(this,K)}),l(a,m)}ot(["click"]);const uc="ERR-SCL-09";var cc=c('

                                                                      '),dc=c('

                                                                      '),vc=c('

                                                                      '),pc=c('

                                                                      Déposez ici le fichier clé

                                                                      '),fc=c('

                                                                      ',1),gc=c('');function mc(a,t){Ue(t,!0);const r=Be(t,"admin",7);let i=B("");const d=u(()=>t.health.state.degraded?.code===uc),p=u(()=>t.health.printing),y=u(()=>e(p)?.on_fallback===!0);let z=B(!1),m=B("");const j=u(()=>r().busy||e(m)!==""),S=new kr;wt(()=>{S.observe(t.health)});const A=u(()=>t.health.catalog_source===null?"Ce poste ne publie pas la source de son catalogue.":"Catalogue surveillé : "+t.health.catalog_source.label),L=u(()=>t.health.catalog!==null&&(t.health.catalog.result==="rejected"||t.health.catalog.result==="failed")?t.health.catalog:null);function W(I){v(i,""),S.forget(),v(m,I,!0)}async function O(I,Q){W(I);try{await r().run(Q)}finally{v(m,"")}}async function C(){W("reload");try{const I=await r().run(en);I!==null&&S.begin(I)}finally{v(m,"")}}async function K(I,Q){W(I);try{const re=await r().load(Q);re!==null&&v(i,re.message,!0)}finally{v(m,"")}}async function U(I,Q){W(I);try{const re=await r().protect(Q);re!==null&&(r().notice=re.message,await r().refresh())}finally{v(m,"")}}async function E(I){if(v(z,!1),I!=null){W("import");try{const Q=await r().protect(()=>pr(I));if(Q===null)return;v(i,Q.result==="rejected"||Q.result==="failed"?`${I.name} : REFUSÉ${Q.reason===""?"":" — "+Q.reason}. Le catalogue en service n’a pas changé.`:`${I.name} : ${String(Q.rows_read_count)} lignes lues, ${String(Q.weighable_count)} pesables. La veille l’appliquera dans la seconde.`,!0),await r().refresh()}finally{v(m,"")}}}var x=gc(),g=s(x);{var D=I=>{var Q=cc(),re=s(Q);f(()=>o(re,e(i))),l(I,Q)};_(g,I=>{e(i)!==""&&I(D)})}var H=n(g,2);{var V=I=>{var Q=dc(),re=s(Q);f(()=>o(re,S.sentence)),l(I,Q)};_(H,I=>{S.sentence!==""&&I(V)})}var $=n(H,2);{var T=I=>{var Q=vc(),re=s(Q);f(()=>o(re,e(p).banner)),l(I,Q)};_($,I=>{e(p)!==null&&e(p).banner!==""&&I(T)})}var w=n($,2),k=s(w),R=n(w,2),F=s(R);{let I=u(()=>e(m)==="scale");Bt(F,{label:"Tester la balance",hint:"Ce que le poste a déjà observé — le port n’est pas rouvert.",get busy(){return e(I)},get disabled(){return e(j)},onrun:()=>{K("scale",nn)}})}var h=n(F,2);{let I=u(()=>e(m)==="printer");Bt(h,{label:"Tester l’imprimante",hint:"Ce que le superviseur a vu il y a moins d’une seconde.",get busy(){return e(I)},get disabled(){return e(j)},onrun:()=>{K("printer",sn)}})}var G=n(h,2);{let I=u(()=>e(m)==="label");Bt(G,{label:"Imprimer une étiquette de test",hint:"Une étiquette de démonstration sort de l’imprimante du poste.",get busy(){return e(I)},get disabled(){return e(j)},onrun:()=>{O("label",ln)}})}var Y=n(G,2);{let I=u(()=>e(m)==="reprint");Bt(Y,{label:"Réimprimer la dernière",hint:"La dernière étiquette imprimée sort une seconde fois.",get busy(){return e(I)},get disabled(){return e(j)},onrun:()=>{O("reprint",Zr)}})}var ne=n(Y,2);{let I=u(()=>e(m)==="reload");Bt(ne,{label:"Recharger le catalogue",kind:"write",hint:"La veille refait tout de suite le contrôle qu’elle fait toutes les cinq secondes.",get busy(){return e(I)},get disabled(){return e(j)},onrun:()=>{C()}})}var M=n(ne,2);{let I=u(()=>e(d)?"Revenir à la balance":"Basculer en saisie manuelle"),Q=u(()=>e(d)?"Le poids sera de nouveau lu sur la balance.":"Le poids se tape à la main : le poste continue de servir sans balance."),re=u(()=>e(m)==="manual");Bt(M,{get label(){return e(I)},kind:"destructive",get hint(){return e(Q)},get engaged(){return e(d)},protected:!0,get busy(){return e(re)},get disabled(){return e(j)},onrun:()=>{U("manual",()=>tn(!e(d)))}})}var b=n(M,2);{let I=u(()=>e(m)==="roll");Bt(b,{label:"J’ai changé le rouleau",kind:"write",hint:"Le compteur d’étiquettes repart à zéro. C’est le seul geste qui dise quelque chose de vrai du papier.",get busy(){return e(I)},get disabled(){return e(j)},onrun:()=>{O("roll",an)}})}var N=n(b,2);{var X=I=>{{let Q=u(()=>e(y)?"Revenir à l’imprimante du poste":"Imprimer sur l’imprimante du poste voisin"),re=u(()=>e(y)?"Les étiquettes repartiront sur l’imprimante de ce poste.":"Les étiquettes sortiront sur l’imprimante voisine, pour cette session seulement."),ee=u(()=>e(m)==="fallback");Bt(I,{get label(){return e(Q)},kind:"write",get hint(){return e(re)},get engaged(){return e(y)},get busy(){return e(ee)},get disabled(){return e(j)},onrun:()=>{O("fallback",()=>rn(!e(y)))}})}};_(N,I=>{e(p)!==null&&e(p).fallback_available&&I(X)})}var de=n(N,2),he=n(R,2);Oe(he,{title:"Importer un catalogue",note:"Glissez le fichier CSV ici, ou choisissez-le. Il passe par le même chemin que le fichier du producteur.",children:(I,Q)=>{var re=pc();let ee;var ve=s(re),ye=n(s(ve)),Ie=s(ye),q=n(ve,2),le=n(s(q));f(()=>{ee=Nt(re,1,"drop svelte-1yd0nzg",null,ee,{dropping:e(z)}),o(Ie,`flv_${t.health.station??""}.csv`)}),Ht("dragover",re,ge=>{ge.preventDefault(),v(z,!0)}),Ht("dragleave",re,()=>v(z,!1)),Ht("drop",re,ge=>{ge.preventDefault(),E(ge.dataTransfer?.files.item(0))}),Ce("change",le,ge=>{E(ge.currentTarget.files?.item(0))}),l(I,re)},$$slots:{default:!0}});var fe=n(he,2);{var oe=I=>{Oe(I,{title:"Le dernier fichier n’a pas pris service",children:(Q,re)=>{var ee=fc(),ve=ae(ee),ye=s(ve),Ie=n(ve,2),q=s(Ie);f((le,ge)=>{o(ye,`${le??""} — + ${(e(L).reason===""?"aucun motif enregistré.":e(L).reason)??""} + Le catalogue en service n’a pas changé.`),o(q,`Dernier essai : ${ge??""}.`)},[()=>ha(e(L).result),()=>Lt(e(L).occurred_at)]),l(Q,ee)},$$slots:{default:!0}})};_(fe,I=>{e(L)!==null&&I(oe)})}f(()=>{o(k,e(A)),be(de,"href",on)}),l(a,x),Me()}ot(["change"]);const hc={succeeded:"La dernière mise à jour a réussi.","rolled-back":"La dernière mise à jour a échoué. La version précédente a été remise et le poste fonctionne.","rolled-back-unhealthy":"La dernière mise à jour a échoué et le poste n’a pas redémarré. Appelez le support.","not-started":"La dernière mise à jour n’a pas démarré : rien n’a été remplacé. Vous pouvez réessayer."},_c=2e3,bc=300*1e3;var wc=c('

                                                                      '),ir=c('

                                                                      '),yc=c("
                                                                      Dernière vérification
                                                                      ",1),xc=c(`

                                                                      La mise à jour depuis cet écran n’existe que sur les postes Windows. Sur les + autres, elle se fait à la main — voir la notice d’installation.

                                                                      `),kc=c('Voir les nouveautés'),qc=c('

                                                                      Version disponible : .

                                                                      '),Sc=c('

                                                                      Ce poste est à jour.

                                                                      '),Lc=c("
                                                                      Version installée
                                                                      Dépôt suivi
                                                                      ",1),Ec=c(`

                                                                      Le poste va s’arrêter environ une minute. L’écran client s’éteindra puis reviendra + tout seul. Si la nouvelle version ne démarre pas, la précédente sera remise + automatiquement — mais les données enregistrées, elles, ne reviendront pas en + arrière.

                                                                      `),Cc=c('

                                                                      Mise à jour en cours. Le poste redémarre, ne le débranchez pas.

                                                                      '),Pc=c('
                                                                      ',1),Tc=c("
                                                                      Terminée le
                                                                      ",1),zc=c("
                                                                      Raison
                                                                      ",1),jc=c('

                                                                      Depuis
                                                                      Vers
                                                                      ',1),Rc=c(" ",1);function Oc(a,t){Ue(t,!0);let r=B(null),i=B(""),d=B(""),p=B(!1),y=B("");const z=u(()=>e(r)?.outcome??null),m=u(()=>e(r)?.supported===!0&&e(r).available),j=u(()=>e(r)?.latest??"");wt(()=>{S()});async function S(){try{v(r,await On(),!0),v(i,"")}catch(g){v(i,g instanceof Error?g.message:"Lecture impossible.",!0)}}async function A(){v(d,"check");try{const g=await t.admin.protect(()=>An());g!==null&&(v(r,g,!0),v(i,""))}finally{v(d,"")}}async function L(g){v(d,"apply"),v(y,"");try{if(await t.admin.protect(()=>Nn(g))===null)return;v(p,!0),v(y,await W()?"":"Le poste n’a pas répondu dans les cinq minutes. Allez le voir.",!0),await S()}finally{v(d,""),v(p,!1)}}async function W(){const g=Date.now()+bc;for(;Date.now()setTimeout(D,_c));try{if((await fetch("/healthz",{cache:"no-store"})).ok)return!0}catch{}}return!1}var O=Rc(),C=ae(O);Oe(C,{title:"Version de ce poste",children:(g,D)=>{var H=Tt(),V=ae(H);{var $=w=>{var k=wc(),R=s(k);f(()=>o(R,e(i)===""?"Lecture…":e(i))),l(w,k)},T=w=>{var k=Lc(),R=ae(k);{var F=oe=>{var I=ir(),Q=s(I);f(()=>o(Q,e(i))),l(oe,I)};_(R,oe=>{e(i)!==""&&oe(F)})}var h=n(R,2),G=n(s(h),2),Y=s(G),ne=n(G,4),M=s(ne),b=n(ne,2);{var N=oe=>{var I=yc(),Q=n(ae(I),2),re=s(Q);f(ee=>o(re,ee),[()=>Lt(e(r).checked_at)]),l(oe,I)};_(b,oe=>{e(r).checked_at!==""&&oe(N)})}var X=n(h,2);{var de=oe=>{var I=xc();l(oe,I)},he=oe=>{var I=qc(),Q=n(s(I)),re=s(Q),ee=n(Q,2);{var ve=q=>{var le=lt();f(ge=>o(le,`, publiée le ${ge??""}`),[()=>Lt(e(r).published_at)]),l(q,le)};_(ee,q=>{e(r).published_at!==""&&q(ve)})}var ye=n(ee,2);{var Ie=q=>{var le=kc();f(()=>be(le,"href",e(r).html_url)),l(q,le)};_(ye,q=>{e(r).html_url!==""&&q(Ie)})}f(()=>o(re,e(r).latest)),l(oe,I)},fe=oe=>{var I=Sc();l(oe,I)};_(X,oe=>{e(r).supported?e(r).available?oe(he,1):oe(fe,-1):oe(de)})}f(()=>{o(Y,e(r).running),o(M,e(r).repository)}),l(w,k)};_(V,w=>{e(r)===null?w($):w(T,-1)})}l(g,H)},$$slots:{default:!0}});var K=n(C,2);{var U=g=>{Oe(g,{title:"Installer",children:(D,H)=>{var V=Pc(),$=ae(V),T=s($);{let M=u(()=>e(d)==="check");Te(T,{kind:"read",label:"Vérifier maintenant",protected:!0,act:"check",get busy(){return e(M)},get disabled(){return e(p)},onrun:()=>{A()}})}var w=n(T,2);{var k=M=>{{let b=u(()=>`Installer la version ${e(j)}`),N=u(()=>e(d)==="apply");Te(M,{kind:"destructive",get label(){return e(b)},protected:!0,act:"apply",get busy(){return e(N)},onrun:()=>{L(e(j))}})}};_(w,M=>{e(m)&&M(k)})}var R=n($,2);{var F=M=>{var b=Ec();l(M,b)};_(R,M=>{e(m)&&M(F)})}var h=n(R,2);{var G=M=>{var b=Cc();l(M,b)};_(h,M=>{e(p)&&M(G)})}var Y=n(h,2);{var ne=M=>{var b=ir(),N=s(b);f(()=>o(N,e(y))),l(M,b)};_(Y,M=>{e(y)!==""&&M(ne)})}l(D,V)},$$slots:{default:!0}})};_(K,g=>{e(r)!==null&&e(r).supported&&g(U)})}var E=n(K,2);{var x=g=>{Oe(g,{title:"Dernière tentative",children:(D,H)=>{var V=jc(),$=ae(V),T=s($),w=n($,2),k=n(s(w),2),R=s(k),F=n(k,4),h=s(F),G=n(F,2);{var Y=b=>{var N=Tc(),X=n(ae(N),2),de=s(X);f(he=>o(de,he),[()=>Lt(e(z).finished_at)]),l(b,N)};_(G,b=>{e(z).finished_at!==""&&b(Y)})}var ne=n(G,2);{var M=b=>{var N=zc(),X=n(ae(N),2),de=s(X);f(()=>o(de,e(z).reason)),l(b,N)};_(ne,b=>{e(z).reason!==""&&b(M)})}f(()=>{be($,"data-outcome",e(z).status),o(T,hc[e(z).status]),o(R,e(z).from===""?"—":e(z).from),o(h,e(z).to===""?"—":e(z).to)}),l(D,V)},$$slots:{default:!0}})};_(E,g=>{e(z)!==null&&g(x)})}l(a,O),Me()}var Ac=c(''),Nc=c('

                                                                      ',1),Ic=c('

                                                                      ',1),Dc=c(''),Fc=c(''),Uc=c(''),Mc=c(''),Wc=c(' '),Bc=c(''),Vc=c('

                                                                      Lecture de l’état du poste…

                                                                      '),Gc=c('

                                                                      '),Hc=c(" "),Jc=c("
                                                                    • "),Kc=c('
                                                                        '),$c=c('
                                                                        '),Yc=c('

                                                                        ');function Xc(a,t){Ue(t,!0);const r=new $n,i=new In(r),d=[{title:"Au quotidien",pages:[{id:"dashboard",label:"Tableau de bord"},{id:"troubleshooting",label:"Dépannage"}]},{title:"Réglages",pages:[{id:"hardware",label:"Matériel"},{id:"label",label:"Étiquette"},{id:"rules",label:"Règles"},{id:"catalog",label:"Catalogue"},{id:"journal",label:"Journal"},{id:"station",label:"Poste"},{id:"update",label:"Mise à jour"}]}],p=u(()=>d.flatMap(q=>q.pages).find(q=>q.id===r.page)?.label??""),y=u(()=>r.health===null||r.health.station_name===""?`Poste ${String(r.health?.station??"")}`:r.health.station_name),z=u(()=>(i.pending?.changed_blocks??[]).map(q=>Mn(q)).join(", ")),m=u(()=>(i.pending?.changed_blocks??[]).join(", "));Ta(()=>(r.start(),()=>r.stop()));async function j(q){r.open(q),Wa(q)&&i.config===null&&await i.load()}async function S(){await r.protect(()=>i.save())===!0&&(r.notice="La configuration est enregistrée et appliquée.")}var A=Yc(),L=s(A),W=n(s(L),2);ze(W,17,()=>d,q=>q.title,(q,le)=>{var ge=Nc(),ue=ae(ge),ie=s(ue),Ne=n(ue,2);ze(Ne,17,()=>e(le).pages,je=>je.id,(je,ke)=>{var Re=Ac();let We;var xe=s(Re);f(()=>{We=Nt(Re,1,"entry svelte-9b2mjq",null,We,{current:r.page===e(ke).id}),be(Re,"aria-current",r.page===e(ke).id?"page":void 0),o(xe,e(ke).label)}),Ce("click",Re,()=>{j(e(ke).id)}),l(je,Re)}),f(()=>o(ie,e(le).title)),l(q,ge)});var O=n(W,2),C=s(O);{var K=q=>{var le=Ic(),ge=ae(le),ue=s(ge),ie=n(ge,2),Ne=s(ie),je=n(ie,2),ke=s(je);f(()=>{o(ue,e(y)),o(Ne,`${r.health.coop??""} · version ${r.health.version??""}`),o(ke,`configuration ${r.health.config_fingerprint??""}`)}),l(q,le)};_(C,q=>{r.health!==null&&q(K)})}var U=n(C,2),E=s(U),x=n(U,2);{var g=q=>{var le=Dc();Ce("click",le,function(...ge){t.onclose?.apply(this,ge)}),l(q,le)};_(x,q=>{t.onclose!==void 0&&q(g)})}var D=n(L,2),H=s(D);{var V=q=>{var le=Fc(),ge=s(le);f(()=>o(ge,r.notice)),l(q,le)};_(H,q=>{r.notice!==""&&q(V)})}var $=n(H,2);{var T=q=>{var le=Uc(),ge=s(le);f(()=>o(ge,r.linkError)),l(q,le)};_($,q=>{r.linkError!==""&&q(T)})}var w=n($,2);{var k=q=>{var le=Mc(),ge=s(le);f(()=>o(ge,r.actionError)),l(q,le)};_(w,q=>{r.actionError!==""&&q(k)})}var R=n(w,2);{var F=q=>{var le=Bc(),ge=s(le),ue=s(ge),ie=n(ue);{var Ne=Re=>{var We=Wc(),xe=s(We);f(()=>o(xe,e(m))),l(Re,We)};_(ie,Re=>{St.showTechnicalNames&&Re(Ne)})}var je=n(ie),ke=n(ge,2);Te(ke,{kind:"write",label:"Tout fonctionne : confirmer",onrun:()=>{r.protect(()=>i.confirm())}}),f(()=>{o(ue,`Configuration appliquée mais NON CONFIRMÉE. Ce qui a changé : ${e(z)??""}. `),o(je,` Le poste reviendra tout seul à la version précédente dans + ${i.pending.seconds_left??""} secondes si personne ne confirme.`)}),l(q,le)};_(R,q=>{i.pending!==null&&q(F)})}var h=n(R,2),G=s(h),Y=s(G),ne=n(G,2);{var M=q=>{var le=Vc();l(q,le)},b=q=>{pi(q,{get health(){return r.health},onshowrows:()=>{j("catalog")},onshowupdate:()=>{j("update")}})},N=q=>{mc(q,{get admin(){return r},get health(){return r.health}})},X=q=>{Ji(q,{get admin(){return r},get draft(){return i},get health(){return r.health}})},de=q=>{zo(q,{get admin(){return r},get draft(){return i}})},he=q=>{pu(q,{get draft(){return i},get health(){return r.health}})},fe=q=>{El(q,{get admin(){return r},get draft(){return i},get health(){return r.health}})},oe=q=>{bo(q,{get admin(){return r}})},I=q=>{sc(q,{get admin(){return r},get draft(){return i},get health(){return r.health}})},Q=q=>{Oc(q,{get admin(){return r}})};_(ne,q=>{r.health===null?q(M):r.page==="dashboard"?q(b,1):r.page==="troubleshooting"?q(N,2):r.page==="hardware"?q(X,3):r.page==="label"?q(de,4):r.page==="rules"?q(he,5):r.page==="catalog"?q(fe,6):r.page==="journal"?q(oe,7):r.page==="station"?q(I,8):r.page==="update"&&q(Q,9)})}var re=n(h,2);{var ee=q=>{var le=$c(),ge=s(le);{var ue=ke=>{var Re=Gc(),We=s(Re),xe=n(We);ze(xe,16,()=>i.retired,De=>De,(De,rt)=>{{let nt=u(()=>`retirer ${rt}`);Te(De,{kind:"write",get label(){return e(nt)},onrun:()=>i.dropRetired(rt)})}}),f(De=>o(We,`Ce fichier porte des réglages que cette version du poste ne connaît plus : + ${De??""}. `),[()=>i.retired.map(De=>qt(De)).join(", ")]),l(ke,Re)};_(ge,ke=>{i.retired.length>0&&ke(ue)})}var ie=n(ge,2);{var Ne=ke=>{var Re=Kc();ze(Re,21,()=>i.faults,We=>We.field,(We,xe)=>{var De=Jc(),rt=s(De),nt=s(rt),pt=n(rt,2);{var ct=et=>{var _e=Hc(),Se=s(_e);f(()=>o(Se,e(xe).field)),l(et,_e)};_(pt,et=>{St.showTechnicalNames&&et(ct)})}var dt=n(pt),ft=n(dt);{var bt=et=>{var _e=lt();f(Se=>o(_e,`— valeurs acceptées : ${Se??""}`),[()=>e(xe).allowed.join(", ")]),l(et,_e)};_(ft,et=>{e(xe).allowed!==void 0&&e(xe).allowed.length>0&&et(bt)})}f(et=>{o(nt,et),o(dt,` ${e(xe).message??""} `)},[()=>qt(e(xe).field)]),l(We,De)}),l(ke,Re)};_(ie,ke=>{i.faults.length>0&&ke(Ne)})}var je=n(ie,2);{let ke=u(()=>i.dirty?"Enregistrer la configuration":"Aucune modification à enregistrer"),Re=u(()=>!i.dirty||r.busy);Te(je,{kind:"write",get label(){return e(ke)},get disabled(){return e(Re)},onrun:()=>{S()}})}l(q,le)},ve=u(()=>Wa(r.page)&&i.config!==null);_(re,q=>{e(ve)&&q(ee)})}var ye=n(D,2);{var Ie=q=>{as(q,{get admin(){return r}})};_(ye,q=>{r.pending!==null&&q(Ie)})}f(()=>{ea(E,St.showTechnicalNames),o(Y,e(p))}),Ce("change",E,()=>St.toggleTechnicalNames()),l(a,A),Me()}ot(["click","change"]);let oa=null;function ed(a){if(oa!==null||a.querySelector("[data-admin]")!==null)return;const t=document.createElement("div");a.appendChild(t),oa={component:Hr(Xc,{target:t,props:{onclose:Qc}}),host:t}}function Qc(){if(oa===null)return;const{component:a,host:t}=oa;oa=null,Jr(a),t.remove()}export{Qc as closeAdmin,ed as mountAdmin}; diff --git a/internal/web/dist/index.html b/internal/web/dist/index.html index f6e00b1..38ff0c1 100644 --- a/internal/web/dist/index.html +++ b/internal/web/dist/index.html @@ -6,8 +6,8 @@ Pesée - - + + From 267dc16da7d489c9707e838979bb428e4677a216 Mon Sep 17 00:00:00 2001 From: "Fabien F." Date: Tue, 4 Aug 2026 12:57:35 +0200 Subject: [PATCH 16/27] =?UTF-8?q?test(domain):=20pinner=20le=20plancher=20?= =?UTF-8?q?du=20contr=C3=B4le=2050=20=C3=A0=20exactement=201,=20pas=20?= =?UTF-8?q?=C2=AB=20en=20dessous=20de=20-5=20=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit La mutation de la tâche 7 (< 1 remplacé par < 0 dans validateChipThreshold) n'a rien cassé au premier essai : TestControl50RefusesAThresholdUnderOne n'essayait que -1 et -5, jamais 0, alors que Validate voit bien 0 sur une Config construite en mémoire — seul le décodage JSON le corrige en DefaultMinProductsForChip. Le plancher n'était donc pinné à aucune valeur précise, seulement « quelque part en dessous de -5 ». Ajoute 0 à la liste des valeurs refusées et documente pourquoi il y figure malgré l'absence de tout fichier livré qui puisse le porter. Rejoué avec la mutation d'origine (< 0) : ROUGE exactement sur le sous-cas 0, les deux autres restent verts. Mutation annulée, suite complète repassée verte. --- internal/domain/validate_test.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/internal/domain/validate_test.go b/internal/domain/validate_test.go index 2daa556..6bdfd9f 100644 --- a/internal/domain/validate_test.go +++ b/internal/domain/validate_test.go @@ -475,8 +475,14 @@ func TestControl49SaysWhatZeroMeans(t *testing.T) { // shelf leaves the bar with « Tout » alone and is undone by coming back to the field; // a threshold under 1 would give a chip to a category with no tile, whose press opens an // empty grid. +// +// 0 is in the list even though no delivered file can carry it: Config.UnmarshalJSON +// corrects a decoded zero to DefaultMinProductsForChip before Validate ever sees it +// (§11.2). But this test builds the Config in memory, past that correction, and that is +// the point -- it pins the floor ITSELF at exactly 1, not merely at "somewhere below the +// negatives this slice happens to try". func TestControl50RefusesAThresholdUnderOne(t *testing.T) { - for _, refused := range []int{-1, -5} { + for _, refused := range []int{-1, -5, 0} { t.Run(strconv.Itoa(refused), func(t *testing.T) { config := loadDelivered(t) config.UI.MinProductsForChip = refused From 7f31206fe8401ef4c93a46e76218f82a5f77d868 Mon Sep 17 00:00:00 2001 From: "Fabien F." Date: Tue, 4 Aug 2026 13:27:27 +0200 Subject: [PATCH 17/27] =?UTF-8?q?fix(front-admin):=20dire=20que=20le=20seu?= =?UTF-8?q?il=20retire=20une=20puce,=20pas=20une=20cat=C3=A9gorie?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le libellé affirmait qu'en dessous du seuil la catégorie disparaît de l'écran ; elle perd seulement sa puce, ses produits restent dans « Tout » et à la recherche. « Produits » désignait aussi le mauvais effectif : le compte porte sur les tuiles servies, pas sur les lignes du CSV. Ce libellé est servi sans aucune note autour dans la barre de refus et le tableau de différences d'un import, il doit donc porter le sens à lui seul. --- web/src/admin/lib/fields.ts | 2 +- web/src/admin/pages/Catalog.svelte | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/admin/lib/fields.ts b/web/src/admin/lib/fields.ts index 36f2a66..5f28d1a 100644 --- a/web/src/admin/lib/fields.ts +++ b/web/src/admin/lib/fields.ts @@ -29,7 +29,7 @@ export const FIELD_LABELS: Readonly> = { 'ui.show_grid_prices': 'Afficher les prix sur les tuiles', 'ui.show_by_unit_products': 'Afficher les produits vendus à l’unité', 'ui.grid_columns': 'Colonnes de la grille', - 'ui.min_products_for_chip': 'Produits minimum pour afficher une catégorie', + 'ui.min_products_for_chip': 'Tuiles minimum pour donner sa puce à une catégorie', 'scale.type': 'Protocole de la balance', 'scale.present': 'Ce poste a une balance', diff --git a/web/src/admin/pages/Catalog.svelte b/web/src/admin/pages/Catalog.svelte index 83af1ce..3711596 100644 --- a/web/src/admin/pages/Catalog.svelte +++ b/web/src/admin/pages/Catalog.svelte @@ -879,7 +879,7 @@

                                                                        - + {#if preferences.showTechnicalNames}{CHIP_THRESHOLD_PATH}{/if}

                                                                        Date: Tue, 4 Aug 2026 13:27:36 +0200 Subject: [PATCH 18/27] docs(web): dire que le renommage attend le prochain chargement du catalogue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Les deux commentaires affirmaient qu'un bénévole voit un rayon renommé sur la grille sans attendre. Faux : le navigateur ne redemande le catalogue que sur catalog_count ou presentation_digest, et un renommage ne bouge ni l'un ni l'autre (web/src/lib/session.svelte.ts). Le serveur sert bien le nouveau libellé au prochain GET, mais rien ne force ce GET -- un poste-kiosque allumé garde l'ancien libellé jusqu'à son prochain chargement. Le mécanisme n'est pas touché ; c'est ce qui reste ouvert, dit en une incise pour que le prochain lecteur n'y voie pas un oubli. --- internal/web/catalog.go | 17 ++++++++++++----- internal/web/catalog_test.go | 19 +++++++++++-------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/internal/web/catalog.go b/internal/web/catalog.go index 479096e..506c4ea 100644 --- a/internal/web/catalog.go +++ b/internal/web/catalog.go @@ -314,14 +314,21 @@ func (s *Server) catalogOf(ctx context.Context, catalog *domain.Catalog, cfg dom // From the CONFIGURATION, and not from the snapshot. The exchange file carries a // letter; the label, the rank, the colour and « montrer cette catégorie sur ce - // poste » are shop decisions that live in config.json and reload hot (§10.2 bis, - // §11.4). The snapshot carries them too — the store hands back the `categories` - // table, which exists as the parent of the foreign key products.category_code and - // which only an IMPORT ever upserts. Reading the wording of the grid from there tied - // a configuration change to the next catalog a producer happens to publish: a shelf + // poste » are shop decisions that live in config.json and reach THIS endpoint + // immediately (§10.2 bis, §11.4) — no restart, no waiting for the next import. + // The snapshot carries them too — the store hands back the `categories` table, + // which exists as the parent of the foreign key products.category_code and which + // only an IMPORT ever upserts. Reading the wording of the grid from there tied a + // configuration change to the next catalog a producer happens to publish: a shelf // renamed on Monday kept its old name until the Friday export, with nothing on any // screen to say why. The effectif below still comes from the catalog in service, // because that is the one number a customer can check against the tiles. + // + // What is still open: this endpoint answers with the new wording the moment it is + // asked, but nothing tells a browser already holding a loaded grid to ask again — + // catalog_count and presentation_digest are what trigger that (web/src/lib/ + // session.svelte.ts), and a rename moves neither. A kiosk left running keeps the + // old label until its next catalog load. for _, c := range cfg.Catalog.Categories { out.Categories = append(out.Categories, categoryDTO{ Code: c.Code, Label: c.Label, Rank: c.Rank, Color: c.Color, diff --git a/internal/web/catalog_test.go b/internal/web/catalog_test.go index c2c41a6..e268a2d 100644 --- a/internal/web/catalog_test.go +++ b/internal/web/catalog_test.go @@ -362,16 +362,19 @@ func TestTheStationServesEveryWeighableTileWhateverTheGridShows(t *testing.T) { // exchange file carries a LETTER, and the configuration carries the label, the rank, // the colour and « montrer cette catégorie sur ce poste ». // -// Those four reload hot (§11.4), so a volunteer who renames a shelf sees it on the grid -// without waiting for a producer to publish a file. Reading them from the snapshot -// instead made them wait: `categories` is a table an IMPORT upserts — it exists as the -// parent of the foreign key products.category_code — so a rename applied on the next -// catalog and on nothing else, and a station whose producer publishes weekly showed the -// old wording for a week with no way to tell why. +// Those four reach this endpoint hot (§11.4), with no restart and no waiting for a +// producer to publish a file. Reading them from the snapshot instead made them wait: +// `categories` is a table an IMPORT upserts — it exists as the parent of the foreign +// key products.category_code — so a rename applied on the next catalog and on nothing +// else, and a station whose producer publishes weekly showed the old wording for a +// week with no way to tell why. // // The snapshot below therefore carries what the last import wrote, and the -// configuration carries what somebody has just typed. What the screen gets is the -// second one. +// configuration carries what somebody has just typed. What THIS endpoint gets is the +// second one. What is still open: a browser that already loaded the grid has no +// signal telling it to ask again — a rename moves neither catalog_count nor +// presentation_digest (web/src/lib/session.svelte.ts) — so a kiosk left running keeps +// showing the old label until its next catalog load. func TestTheCategoryLabelsFollowTheConfigurationAndNotTheLastImport(t *testing.T) { imported := domain.NewCatalog([]domain.Product{ {ID: "1", Name: "AIL", Reference: "0493021000003", Mode: domain.ByWeight, From 9fbe9001bab5fe497d82f166f0f4dfd213b3afb6 Mon Sep 17 00:00:00 2001 From: "Fabien F." Date: Tue, 4 Aug 2026 13:27:44 +0200 Subject: [PATCH 19/27] =?UTF-8?q?docs(architecture):=20ne=20plus=20recopie?= =?UTF-8?q?r=20le=20nombre=20de=20contr=C3=B4les=20de=20config.go?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit §11.3 dit désormais que la liste énumérée y fait foi, pas un total recopié ailleurs, qui a déjà menti une fois pour avoir oublié une suppression. Ce seuil de puce en ajoutant un 50e était le premier ajout suivant que cette règle anticipait. --- docs/02-architecture.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/02-architecture.md b/docs/02-architecture.md index 602e6ca..a15482b 100644 --- a/docs/02-architecture.md +++ b/docs/02-architecture.md @@ -234,7 +234,7 @@ balance/ │ │ ├── machine.go State, Event, Effect, Transition (PURE) │ │ ├── prepare.go Prepare : le SEUL chemin produit+mesure → Label │ │ ├── template.go Template, Element, Validate (9 règles dures) -│ │ ├── config.go Config, Validate (47 contrôles), Fingerprint, Export +│ │ ├── config.go Config, Validate (liste des contrôles en §11.3), Fingerprint, Export │ │ ├── profiles.go NeutralProfile SEUL (§11.5) — les valeurs d'un site │ │ │ sont un fichier livré, jamais du code compilé │ │ └── frame/ Parse, Accumulator — grammaire des trames balance (fuzzé) From 2b79c9cd4e1f4aa75e6751279faa6649d17b1b97 Mon Sep 17 00:00:00 2001 From: "Fabien F." Date: Tue, 4 Aug 2026 13:27:59 +0200 Subject: [PATCH 20/27] =?UTF-8?q?test(domain):=20comparer=20des=20tuiles?= =?UTF-8?q?=20pesables=20entre=20elles,=20pas=20des=20lignes=20re=C3=A7ues?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 355 est un nombre de lignes reçues, 107 un nombre de tuiles pesables : les deux commentaires les mettaient en balance comme s'ils mesuraient la même chose. Ce contrôle porte sur des tuiles ; la paire cohérente est 331 pesables / 107 pesables. Corrigé aux deux endroits du domaine et dans le dossier de conception d'où l'erreur vient. --- .../specs/2026-08-04-seuil-de-puce-reglable-design.md | 4 ++-- internal/domain/validate_settings.go | 4 ++-- internal/domain/validate_test.go | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/superpowers/specs/2026-08-04-seuil-de-puce-reglable-design.md b/docs/superpowers/specs/2026-08-04-seuil-de-puce-reglable-design.md index c5058cf..f881fcf 100644 --- a/docs/superpowers/specs/2026-08-04-seuil-de-puce-reglable-design.md +++ b/docs/superpowers/specs/2026-08-04-seuil-de-puce-reglable-design.md @@ -138,8 +138,8 @@ catégorie sans tuile — et le refuser plutôt que le corriger obligerait à di // validateChipThreshold is control 50: ui.min_products_for_chip is at least 1. // // A floor and no ceiling, on purpose. No pair of bounds is true of every catalogue -- -// the same number is generous on a 355-product export and severe on a 107-product one, -// and the two are the SAME cooperative four years apart. What a ceiling would protect +// the same number is generous on a 331-weighable-tile export and severe on a 107-tile +// one, and the two are the SAME cooperative four years apart. What a ceiling would protect // against, a value above the biggest shelf, leaves the bar with « Tout » alone and is // undone by coming back to the field. // diff --git a/internal/domain/validate_settings.go b/internal/domain/validate_settings.go index 40c7f0e..36e70ba 100644 --- a/internal/domain/validate_settings.go +++ b/internal/domain/validate_settings.go @@ -429,8 +429,8 @@ func (c *Config) validateGrid() []Fault { // A floor and no ceiling. What a ceiling would guard against -- a threshold above the // biggest shelf, which leaves the category bar with « Tout » alone -- is undone by coming // back to the field, and no pair of bounds is true of every catalogue: the same number is -// generous on the 355-product export of 2026 and severe on the 107-product one of 2022, -// and those are the same cooperative. +// generous on the 331-weighable-tile export of 2026 and severe on the 107-tile one of +// 2022, and those are the same cooperative. // // The floor is the half that has no legitimate reading: under 1, a category with no tile // at all would get a chip, and pressing it would open an empty grid. Zero never reaches diff --git a/internal/domain/validate_test.go b/internal/domain/validate_test.go index 6bdfd9f..dbbbb87 100644 --- a/internal/domain/validate_test.go +++ b/internal/domain/validate_test.go @@ -470,11 +470,11 @@ func TestControl49SaysWhatZeroMeans(t *testing.T) { // TestControl50RefusesAThresholdUnderOne guards the value that has no reading. // // A floor and NO ceiling, deliberately: no pair of bounds is true of every catalogue -- -// the same number is generous on a 355-product export and severe on a 107-product one, -// and those two are the SAME cooperative four years apart. A threshold above the biggest -// shelf leaves the bar with « Tout » alone and is undone by coming back to the field; -// a threshold under 1 would give a chip to a category with no tile, whose press opens an -// empty grid. +// the same number is generous on a 331-weighable-tile export and severe on a 107-tile +// one, and those two are the SAME cooperative four years apart. A threshold above the +// biggest shelf leaves the bar with « Tout » alone and is undone by coming back to the +// field; a threshold under 1 would give a chip to a category with no tile, whose press +// opens an empty grid. // // 0 is in the list even though no delivered file can carry it: Config.UnmarshalJSON // corrects a decoded zero to DefaultMinProductsForChip before Validate ever sees it From 923a5bab7baecfe83f8448aebb4475b2eb83ba8f Mon Sep 17 00:00:00 2001 From: "Fabien F." Date: Tue, 4 Aug 2026 13:28:08 +0200 Subject: [PATCH 21/27] =?UTF-8?q?test(domain):=20compter=20le=20seuil=20de?= =?UTF-8?q?=20puce=20dans=20l'empreinte=20partag=C3=A9e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit grid_columns et show_by_unit_products ont chacun une entrée dans TestFingerprintChangesWhenASharedValueChanges ; min_products_for_chip n'en avait pas. Le chemin était correct dans BlockFingerprint, c'est la garde qui manquait -- et le cache de catalogBytes d'internal/web en dépend. --- internal/domain/fingerprint_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/domain/fingerprint_test.go b/internal/domain/fingerprint_test.go index d8d67c1..90ba275 100644 --- a/internal/domain/fingerprint_test.go +++ b/internal/domain/fingerprint_test.go @@ -76,6 +76,10 @@ func TestFingerprintChangesWhenASharedValueChanges(t *testing.T) { // its neighbour follows the screen. Neither is wrong, and a fleet that diverges // by accident must be able to see it by eye. "le nombre de colonnes de la grille": func(c *Config) { c.UI.GridColumns = 7 }, + // internal/web caches the served catalog bytes on this digest (catalogBytes): + // a raised threshold that left the eight characters alone would keep serving + // a category bar built for the old one until an unrelated change flushed it. + "le seuil de puce des catégories": func(c *Config) { c.UI.MinProductsForChip = 7 }, } { t.Run(name, func(t *testing.T) { diverging := loadDelivered(t) From b01936799d06f13edf0bfb5bed3e32845a0ae3dc Mon Sep 17 00:00:00 2001 From: "Fabien F." Date: Tue, 4 Aug 2026 13:28:33 +0200 Subject: [PATCH 22/27] =?UTF-8?q?test(domain):=20ne=20plus=20recopier=20le?= =?UTF-8?q?=20nombre=20de=20champs=20cass=C3=A9s=20dans=20le=20nom=20du=20?= =?UTF-8?q?cas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit « trente-et-un champs cassés » est exact aujourd'hui, mais c'est la classe de défaut que cette branche a déjà retirée quatre fois ailleurs : un compteur recopié à côté d'une liste finit par la contredire. La liste want fait foi ; le nom du cas s'en tient à ce qu'il décrit sans le redire, « du contrôle 1 au contrôle 50 ». --- internal/domain/validate_order_test.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/internal/domain/validate_order_test.go b/internal/domain/validate_order_test.go index 3a3aac2..686b39f 100644 --- a/internal/domain/validate_order_test.go +++ b/internal/domain/validate_order_test.go @@ -33,10 +33,11 @@ import "testing" // station and a document that disagree about which control is 46 cost more than // the tidiness gained. // -// The two cases below are deliberately different in nature. The first breaks thirty-one -// fields at once and walks the whole numbering from control 1 to control 50. The -// second is the one a lookup by field could never hold: printer.options.transport is -// reported THREE times, by controls 7, 8 and 42, and only its POSITION says which. +// The two cases below are deliberately different in nature. The first walks the whole +// numbering from control 1 to control 50, one broken field at a time -- the `want` +// slice is what is authoritative on how many, not this comment. The second is the one +// a lookup by field could never hold: printer.options.transport is reported THREE +// times, by controls 7, 8 and 42, and only its POSITION says which. func TestValidateReportsItsFaultsInTheOrderTheControlsAreNumbered(t *testing.T) { for _, testCase := range []struct { name string @@ -44,7 +45,7 @@ func TestValidateReportsItsFaultsInTheOrderTheControlsAreNumbered(t *testing.T) want []string }{ { - name: "trente-et-un champs cassés, du contrôle 1 au contrôle 50", + name: "du contrôle 1 au contrôle 50", break_: func(c *Config, _ *Registries) { c.Station.Number = 0 // 1 c.Network.Listen = "pas une adresse" // 2 From ef1c70d2f015b6b67620ac66224662ac7237c56b Mon Sep 17 00:00:00 2001 From: "Fabien F." Date: Tue, 4 Aug 2026 13:28:42 +0200 Subject: [PATCH 23/27] =?UTF-8?q?docs(web):=20renvoyer=20=C3=A0=20MIN=5FPR?= =?UTF-8?q?ODUCTS=5FFOR=5FCHIP=20plut=C3=B4t=20que=20de=20recopier=20sa=20?= =?UTF-8?q?valeur?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit La TSDoc écrivait « Défaut 5. », qui devait rester en phase avec la constante déclarée 35 lignes plus bas dans le même fichier. Un {@link} ne peut pas diverger de ce qu'il désigne. --- web/src/lib/catalog.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/lib/catalog.ts b/web/src/lib/catalog.ts index 33c6796..42793a5 100644 --- a/web/src/lib/catalog.ts +++ b/web/src/lib/catalog.ts @@ -77,7 +77,7 @@ export interface Presentation { grid_columns: number /** * Le nombre de tuiles qu'une catégorie doit avoir sur ce poste pour obtenir sa puce - * de filtre. Défaut 5. + * de filtre. Défaut {@link MIN_PRODUCTS_FOR_CHIP}. * * En deçà, la catégorie perd sa PUCE et jamais ses tuiles : ses produits restent dans * « Tout » et à la recherche. Ce qui retire vraiment des produits de l'écran est From 2a6aff712a615e0678f71b28d35abad8aab22a42 Mon Sep 17 00:00:00 2001 From: "Fabien F." Date: Tue, 4 Aug 2026 13:28:50 +0200 Subject: [PATCH 24/27] =?UTF-8?q?style(web):=20replier=20un=20import=20qui?= =?UTF-8?q?=20d=C3=A9passe=20le=20printWidth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 104 caractères pour un printWidth de 100. Le fichier est sur .prettierignore, mais cette liste est un cliquet de dette et pas une amnistie pour une ligne neuve. --- web/test/fixtures/odoo.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/web/test/fixtures/odoo.ts b/web/test/fixtures/odoo.ts index 4c0cd17..0e562d0 100644 --- a/web/test/fixtures/odoo.ts +++ b/web/test/fixtures/odoo.ts @@ -2,7 +2,12 @@ import { createHash } from 'node:crypto' import { readFileSync } from 'node:fs' import { dirname, resolve } from 'node:path' import { fileURLToPath } from 'node:url' -import { MIN_PRODUCTS_FOR_CHIP, type Catalog, type Category, type Product } from '../../src/lib/catalog' +import { + MIN_PRODUCTS_FOR_CHIP, + type Catalog, + type Category, + type Product, +} from '../../src/lib/catalog' import { normalize } from '../../src/lib/normalize' /** From 7cf9a380ee6615be2225f649fe7cc1a15428ea80 Mon Sep 17 00:00:00 2001 From: "Fabien F." Date: Tue, 4 Aug 2026 13:29:01 +0200 Subject: [PATCH 25/27] test(web): nommer le seuil servi, pas la constante cliente MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit C'est presentation.min_products_for_chip que chips() applique, MIN_PRODUCTS_FOR_CHIP n'étant que son repli. Le fixture leur donne ici la même valeur, donc le test ne changeait pas de comportement, mais le commentaire nommait la mauvaise chose. --- web/test/unit-products.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/test/unit-products.test.ts b/web/test/unit-products.test.ts index e98f417..cf9b5e1 100644 --- a/web/test/unit-products.test.ts +++ b/web/test/unit-products.test.ts @@ -92,7 +92,8 @@ describe('les puces se recomptent sur ce que la grille montre', () => { const counts = Object.fromEntries(bar.map((chip) => [chip.code, chip.count])) // Les quinze masqués se répartissent 8 légumes, 5 fruits, 2 vrac : aucun rayon ne - // passe sous MIN_PRODUCTS_FOR_CHIP, donc aucune puce ne disparaît. + // passe sous le seuil servi (`presentation.min_products_for_chip`), donc aucune + // puce ne disparaît. expect(bar.map((chip) => chip.code)).toEqual([ ALL_CATEGORIES, 'fruits', From 51efaf3c1452ffa89a56d470b5bb7bd8802882d7 Mon Sep 17 00:00:00 2001 From: "Fabien F." Date: Tue, 4 Aug 2026 13:29:17 +0200 Subject: [PATCH 26/27] =?UTF-8?q?build(web):=20r=C3=A9g=C3=A9n=C3=A8re=20l?= =?UTF-8?q?e=20front=20embarqu=C3=A9=20avec=20les=20corrections=20de=20rel?= =?UTF-8?q?ecture?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le libellé du seuil de puce, sa TSDoc et l'import replié changent les octets des bundles admin et écran client ; le dépôt a déjà livré une fois l'écran d'avant pour avoir oublié cette étape. --- internal/web/dist/admin.html | 4 ++-- internal/web/dist/assets/admin-DEu9sOlj.js | 1 + internal/web/dist/assets/admin-PxVR9VI8.js | 1 - .../web/dist/assets/{index-CY0bHh3L.js => index-BoOKWXIu.js} | 4 ++-- .../web/dist/assets/{mount-zw3SIkYn.js => mount-D3UhzFzr.js} | 4 ++-- internal/web/dist/index.html | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) create mode 100644 internal/web/dist/assets/admin-DEu9sOlj.js delete mode 100644 internal/web/dist/assets/admin-PxVR9VI8.js rename internal/web/dist/assets/{index-CY0bHh3L.js => index-BoOKWXIu.js} (99%) rename internal/web/dist/assets/{mount-zw3SIkYn.js => mount-D3UhzFzr.js} (94%) diff --git a/internal/web/dist/admin.html b/internal/web/dist/admin.html index cb64c92..7e6fcba 100644 --- a/internal/web/dist/admin.html +++ b/internal/web/dist/admin.html @@ -5,9 +5,9 @@ Administration - + - + diff --git a/internal/web/dist/assets/admin-DEu9sOlj.js b/internal/web/dist/assets/admin-DEu9sOlj.js new file mode 100644 index 0000000..aba8cec --- /dev/null +++ b/internal/web/dist/assets/admin-DEu9sOlj.js @@ -0,0 +1 @@ +import"./app-BaRSXv80.js";import{mountAdmin as m}from"./mount-D3UhzFzr.js";m(document.getElementById("app")); diff --git a/internal/web/dist/assets/admin-PxVR9VI8.js b/internal/web/dist/assets/admin-PxVR9VI8.js deleted file mode 100644 index 93dcb69..0000000 --- a/internal/web/dist/assets/admin-PxVR9VI8.js +++ /dev/null @@ -1 +0,0 @@ -import"./app-BaRSXv80.js";import{mountAdmin as m}from"./mount-zw3SIkYn.js";m(document.getElementById("app")); diff --git a/internal/web/dist/assets/index-CY0bHh3L.js b/internal/web/dist/assets/index-BoOKWXIu.js similarity index 99% rename from internal/web/dist/assets/index-CY0bHh3L.js rename to internal/web/dist/assets/index-BoOKWXIu.js index 50cabbd..9872a39 100644 --- a/internal/web/dist/assets/index-CY0bHh3L.js +++ b/internal/web/dist/assets/index-BoOKWXIu.js @@ -1,2 +1,2 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/mount-zw3SIkYn.js","assets/app-BaRSXv80.js","assets/app-Cfxw4Luj.css","assets/mount-MmpjwdB2.css"])))=>i.map(i=>d[i]); -import{e as Ie,u as je,d as re,p as Y,a as q,i as W,I as ee,t as z,b as N,c as w,f as Z,g as E,h as t,s as h,j as ae,k as te,l as se,m as T,n as v,o as u,q as Pe,r as fe,w as De,v as Te,x as ne,y as Ce,R as Re,z as b,A as D,B as qe,T as Le,C as Ne,N as Oe,D as Me,E as We,F as Be,G as Fe,H as ze,M as He,J as Ue,K as Ve,L as Ge,O as Xe,P as Ke,Q as Je,S as Qe,U as Ye,V as Ze,W as $e,X as et}from"./app-BaRSXv80.js";class be{#e=new WeakMap;#t;#n;static entries=new WeakMap;constructor(e){this.#n=e}observe(e,n){var r=this.#e.get(e)||new Set;return r.add(n),this.#e.set(e,r),this.#s().observe(e,this.#n),()=>{var l=this.#e.get(e);l.delete(n),l.size===0&&(this.#e.delete(e),this.#t.unobserve(e))}}#s(){return this.#t??(this.#t=new ResizeObserver(e=>{for(var n of e){be.entries.set(n.target,n);for(var r of this.#e.get(n.target)||[])r(n)}}))}}var tt=new be({box:"border-box"});function he(s,e,n){var r=tt.observe(s,()=>n(s[e]));Ie(()=>(je(()=>n(s[e])),r))}const nt="modulepreload",at=function(s){return"/"+s},ye={},st=function(e,n,r){let l=Promise.resolve();if(n&&n.length>0){let _=function(o){return Promise.all(o.map(i=>Promise.resolve(i).then(y=>({status:"fulfilled",value:y}),y=>({status:"rejected",reason:y}))))};document.getElementsByTagName("link");const c=document.querySelector("meta[property=csp-nonce]"),d=c?.nonce||c?.getAttribute("nonce");l=_(n.map(o=>{if(o=at(o),o in ye)return;ye[o]=!0;const i=o.endsWith(".css"),y=i?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${o}"]${y}`))return;const k=document.createElement("link");if(k.rel=i?"stylesheet":nt,i||(k.as="script"),k.crossOrigin="",k.href=o,d&&k.setAttribute("nonce",d),document.head.appendChild(k),i)return new Promise((m,I)=>{k.addEventListener("load",m),k.addEventListener("error",()=>I(new Error(`Unable to preload CSS for ${o}`)))})}))}function f(c){const d=new Event("vite:preloadError",{cancelable:!0});if(d.payload=c,window.dispatchEvent(d),!d.defaultPrevented)throw c}return l.then(c=>{for(const d of c||[])d.status==="rejected"&&f(d.reason);return e().catch(f)})};var rt=E('

                                                                        kg

                                                                        ',1),it=E('

                                                                        kg

                                                                        Poids indisponible

                                                                        ',1),lt=E('');function ot(s,e){Y(e,!0);const n=q(e,"taring",3,!1),r=v(()=>{if(e.linkBanner!=="")return e.linkBanner;const A=e.snapshot?.message??null;if(A!==null)return A.text;switch(e.snapshot?.state){case"product_armed":return"Posez votre produit";case"awaiting_stability":return"Pesée en cours…";case"printing":return"Étiquette en cours…";case"entering_tare":return"Tapez la tare en grammes";case"scale_lost":return"Le poids n’est plus disponible.";default:return"Touchez votre produit, l’étiquette sort"}}),l=v(()=>e.snapshot===null?"var(--waiting)":e.snapshot.state==="faulted"||e.snapshot.state==="scale_lost"?"var(--fault)":e.snapshot.state==="rejected"?"var(--warning)":e.snapshot.state==="succeeded"||e.snapshot.weight.latched?"var(--ready)":"var(--waiting)"),f=v(()=>e.showWeight&&e.snapshot!==null&&e.snapshot.weight.available&&!e.snapshot.weight.expired),c=v(()=>{const A=e.snapshot?.message;return A!=null?A.level:e.snapshot?.state==="faulted"||e.snapshot?.state==="scale_lost"?"error":e.snapshot?.state==="rejected"?"warn":"info"});var d=lt(),_=u(d);let o;var i=u(_);{var y=A=>{var O=rt(),G=Pe(O),X=u(G),K=h(G,2),F=u(K);z(()=>{T(X,e.snapshot.weight.net_text),T(F,`tare ${e.snapshot.weight.tare_g??""} g`)}),w(A,O)},k=A=>{var O=it();w(A,O)};W(i,A=>{t(f)&&e.snapshot?A(y):A(k,-1)})}var m=h(_,2);let I;var g=u(m),P=h(m,2);let B;var R=u(P);ee(R,{name:"tare",size:"1.4em"});var H=h(P,2);let V;z(()=>{ae(d,"data-state",e.snapshot?.state??"initializing"),o=te(_,"",o,{"border-left-color":t(l)}),I=se(m,1,"instruction svelte-4gizvm",null,I,{warn:t(c)==="warn",error:t(c)==="error"}),T(g,t(r)),B=se(P,1,"tare-key touch-target svelte-4gizvm",null,B,{active:n()}),V=te(H,"",V,{background:t(l)})}),N("click",P,function(...A){e.ontare?.apply(this,A)}),w(s,d),Z()}re(["click"]);var ct=E(''),ut=E('');function dt(s,e){Y(e,!0);var n=ut(),r=u(n);fe(r,21,()=>e.chips,d=>d.code,(d,_)=>{var o=ct();let i,y;var k=h(u(o),2),m=u(k),I=h(k,2),g=u(I);z(P=>{i=se(o,1,"chip touch-target svelte-atgty1",null,i,{active:t(_).code===e.active}),ae(o,"aria-pressed",t(_).code===e.active),y=te(o,"",y,P),T(m,t(_).label),T(g,t(_).count)},[()=>({"--chip-ink":Te(t(_).color),"--chip-wash":De(t(_).color)})]),N("click",o,()=>e.onselect(t(_).code)),w(d,o)});var l=h(r,2);let f;var c=u(l);ee(c,{name:"search",size:"1.75rem"}),z(()=>{f=se(l,1,"chip touch-target search-key svelte-atgty1",null,f,{active:e.searchFieldOpen}),ae(l,"aria-pressed",e.searchFieldOpen)}),N("click",l,function(...d){e.onopensearch?.apply(this,d)}),w(s,n),Z()}re(["click"]);var vt=E(''),gt=E('

                                                                        '),ht=E('

                                                                        ');function we(s,e){Y(e,!0);const n=q(e,"action",3,null);var r=ht(),l=u(r),f=u(l),c=u(f);ee(c,{name:"alert",size:"3rem"});var d=h(f,2),_=u(d),o=h(d,2),i=u(o),y=h(o,2);{var k=g=>{var P=vt(),B=u(P);z(()=>T(B,n().label)),N("click",P,function(...R){n().run?.apply(this,R)}),w(g,P)};W(y,g=>{n()!==null&&g(k)})}var m=h(y,2);{var I=g=>{var P=gt(),B=u(P);z(()=>T(B,e.code)),w(g,P)};W(m,g=>{e.code!==""&&g(I)})}z(()=>{T(_,e.title),T(i,e.detail)}),w(s,r),Z()}re(["click"]);var ft=E('
                                                                        '),pt=E('

                                                                        Chargement du catalogue…

                                                                        ',1),_t=E('

                                                                        '),mt=E('

                                                                        '),bt=E('
                                                                        '),yt=E('
                                                                        '),wt=E('
                                                                        ');function Et(s,e){Y(e,!0);const n=q(e,"gridColumns",3,0),r=q(e,"colors",19,()=>({})),l=q(e,"primaryCode",3,""),f=q(e,"tierAbbrev",19,()=>({})),c=q(e,"selectedID",3,null),d=q(e,"rejectedID",3,null),_=q(e,"busyID",3,null),o=q(e,"showPrices",3,!0),i=q(e,"loading",3,!1),y=q(e,"emptyMessage",3,"Aucun produit ne correspond."),k=q(e,"emptyHint",3,""),m=8,I=26,g="'Inter Variable', system-ui, sans-serif",P="'Inter Variable'",B=16;let R=D(0),H=D(0),V=D(!1);ne(()=>{const x=document.fonts;x!==void 0&&x.load(`700 ${Re}px ${P}`).catch(()=>[]).then(()=>{b(V,!0)})});const A=v(()=>(t(V),Be(g,700)));let O=D(null),G=D(0),X=D(0);const K=v(()=>n()>0?n():t(R)<=0||t(H)<=0?0:Math.max(1,Math.floor((t(R)+m)/(t(H)+m)))),F=v(()=>t(K)>0&&t(R)>0?(t(R)-(t(K)-1)*m)/t(K):0),p=v(()=>n()>0&&t(F)>0&&t(H)>0?t(F)/t(H):1),C=v(()=>We(t(p))),M=v(()=>n()>0?`grid-template-columns: ${Ne(n())}`:void 0),$=v(()=>t(F)>0?t(F)-I:0),ie=v(()=>t(G)>0?t(G):t($));ne(()=>{t(F),t(p),e.products.length;const x=t(O)?.querySelector(".name-box");b(G,x?.clientWidth??0,!0)});const oe=v(()=>{const x=new Map;if(t(A)===null||t(ie)<=0)return x;const L=t(X)>0?t(X):Oe;for(const j of e.products)x.set(j.id,Me(j.name,t(ie),t(A),L,t(C)));return x});var J=wt(),ce=u(J),le=h(ce,2),ue=h(le,2);{var pe=x=>{var L=pt(),j=Pe(L);fe(j,21,()=>({length:B}),qe,(a,S)=>{var U=ft();w(a,U)}),z(()=>te(j,t(M))),he(j,"clientWidth",a=>b(R,a)),w(x,L)},de=x=>{var L=mt(),j=u(L),a=u(j),S=h(j,2);{var U=Q=>{var ge=_t(),_e=u(ge);z(()=>T(_e,k())),w(Q,ge)};W(S,Q=>{k()!==""&&Q(U)})}z(()=>T(a,y())),w(x,L)},ve=x=>{var L=yt();fe(L,21,()=>e.products,j=>j.id,(j,a)=>{var S=bt(),U=u(S);{let Q=v(()=>t(oe).get(t(a).id)??t(C)),ge=v(()=>t(a).id===c()),_e=v(()=>t(a).id===d()),Se=v(()=>t(a).id===_());Le(U,{get product(){return t(a)},get nameSizePx(){return t(Q)},get categoryColor(){return r()[t(a).category_code]},get primaryCode(){return l()},get tierAbbrev(){return f()},get selected(){return t(ge)},get rejected(){return t(_e)},get busy(){return t(Se)},get showPrice(){return o()},get onpick(){return e.onpick}})}w(j,S)}),z(()=>{ae(L,"data-tile-count",e.products.length),te(L,t(M))}),he(L,"clientWidth",j=>b(R,j)),w(x,L)};W(ue,x=>{i()?x(pe):e.products.length===0?x(de,1):x(ve,-1)})}Ce(J,x=>b(O,x),()=>t(O)),z(()=>{ae(J,"data-tile-scale",t(p)),te(J,`--tile-scale: ${t(p)??""}`)}),he(ce,"clientHeight",x=>b(X,x)),he(le,"clientWidth",x=>b(H,x)),w(s,J),Z()}var kt=E(''),xt=E('
                                                                        ');function At(s,e){Y(e,!0);let n=D(null);ne(()=>{t(n)?.focus()});var r=xt(),l=u(r);ee(l,{name:"search",size:"1.75rem"});var f=h(l,2);Ce(f,i=>b(n,i),()=>t(n));var c=h(f,2);{var d=i=>{var y=kt();N("click",y,function(...k){e.onclose?.apply(this,k)}),w(i,y)};W(c,i=>{e.query!==""&&i(d)})}var _=h(c,2),o=u(_);ee(o,{name:"close",size:"1.5rem"}),z(()=>Fe(f,e.query)),N("input",f,i=>e.onquery(i.currentTarget.value)),N("keydown",f,i=>{i.key==="Escape"?(i.preventDefault(),e.onclose()):i.key==="Enter"&&(i.preventDefault(),e.onenter())}),N("click",_,function(...i){e.onclose?.apply(this,i)}),w(s,r),Z()}re(["input","keydown","click"]);function Pt(s){if(s==="")return"";const e=new Date(s);if(Number.isNaN(e.getTime()))return"";const n=l=>String(l).padStart(2,"0");return`${`${n(e.getDate())}/${n(e.getMonth()+1)}/${e.getFullYear()}`} ${n(e.getHours())}:${n(e.getMinutes())}:${n(e.getSeconds())}`}function Ct(s){return s.mode==="by_unit"?`${s.product_name} ${s.quantity} ${s.quantity>1?"unités":"unité"}`:`${s.product_name} ${s.net_text} kg`}var zt=E('Catalogue du '),St=E('Catalogue en attente'),It=E(' '),jt=E('aucune pour le moment'),Dt=E(' '),Tt=E(''),Rt=E('
                                                                        Dernière étiquette
                                                                        ');function qt(s,e){Y(e,!0);const n=q(e,"catalogAt",3,""),r=q(e,"productCount",3,0),l=q(e,"appVersion",3,""),f=v(()=>[r()>0?`${r()} produits pesables`:"",l()!==""?`application ${l()}`:""].filter(p=>p!=="").join(" · "));var c=Rt();let d;var _=u(c),o=u(_);{var i=p=>{var C=zt(),M=h(u(C)),$=u(M);z(()=>T($,n())),w(p,C)},y=p=>{var C=St();w(p,C)};W(o,p=>{n()!==""?p(i):p(y,-1)})}var k=h(o,2);{var m=p=>{var C=It(),M=u(C);z(()=>T(M,t(f))),w(p,C)};W(k,p=>{t(f)!==""&&p(m)})}var I=h(_,4),g=h(u(I),2);{var P=p=>{var C=jt();w(p,C)},B=p=>{var C=Dt(),M=u(C);z($=>T(M,$),[()=>Ct(e.label)]),w(p,C)};W(g,p=>{e.label===null?p(P):p(B,-1)})}var R=h(I,2);{var H=p=>{var C=Tt();z(()=>C.disabled=!e.available),N("click",C,function(...M){e.onreprint?.apply(this,M)}),w(p,C)};W(R,p=>{e.label!==null&&p(H)})}var V=h(R,2),A=u(V);let O;var G=h(A,2),X=u(G),K=h(V,2),F=u(K);ee(F,{name:"settings",size:"1.75rem"}),z(()=>{d=se(c,1,"bar svelte-161y12f",null,d,{live:e.label!==null}),O=se(A,1,"dot svelte-161y12f",null,O,{fault:!e.healthy}),ae(A,"aria-label",e.healthy?"Matériel disponible":"Matériel indisponible"),T(X,e.healthy?"Balance et imprimante disponibles":"Matériel indisponible")}),N("click",K,function(...p){e.onadmin?.apply(this,p)}),w(s,c),Z()}re(["click"]);var Lt=E(''),Nt=E('

                                                                        Tare g

                                                                        ');function Ot(s,e){Y(e,!0);const n=["1","2","3","4","5","6","7","8","9","0"];var r=Nt(),l=u(r),f=h(u(l),2),c=u(f),d=h(l,2);fe(d,20,()=>n,m=>m,(m,I)=>{var g=Lt(),P=u(g);z(()=>T(P,I)),N("click",g,()=>e.ondigit(I)),w(m,g)});var _=h(d,2),o=u(_),i=h(o,2),y=h(i,2),k=u(y);ee(k,{name:"check",size:"1.5rem"}),z(()=>T(c,e.grams===""?"0":e.grams)),N("click",o,function(...m){e.onclear?.apply(this,m)}),N("click",i,function(...m){e.oncancel?.apply(this,m)}),N("click",y,function(...m){e.onconfirm?.apply(this,m)}),w(s,r),Z()}re(["click"]);function Mt(s){return s===null||s.state==="initializing"?!1:s.scale.connected&&s.printer.health!=="faulted"}const Ee=1500,Wt=2e3,ke=5e3;function Bt(s,e){return e?s>=ke?{showWeight:!1,banner:"Poids indisponible",mustReconnect:!0}:s>=Wt?{showWeight:!1,banner:"Poids indisponible",mustReconnect:!1}:s>=Ee?{showWeight:!1,banner:"",mustReconnect:!1}:{showWeight:!0,banner:"",mustReconnect:!1}:{showWeight:s=ke}}const Ft=250,Ht={show_grid_prices:!0,idle_timeout_s:45,reprint_window_s:60,sound:!0,show_by_unit_products:!1,grid_columns:0,min_products_for_chip:He};class Ut{#e=D(null);get state(){return t(this.#e)}set state(e){b(this.#e,e,!0)}#t=D(null);get catalog(){return t(this.#t)}set catalog(e){b(this.#t,e,!0)}#n=D(ze({showWeight:!0,banner:"",mustReconnect:!1}));get link(){return t(this.#n)}set link(e){b(this.#n,e,!0)}#s=D("");get catalogError(){return t(this.#s)}set catalogError(e){b(this.#s,e,!0)}#r=null;#i=null;#l=Date.now();#a=!1;#o=!1;get presentation(){return this.catalog?.presentation??Ht}start(){this.#u(),this.#c(),this.#i=setInterval(()=>this.#v(),Ft)}stop(){this.#i!==null&&clearInterval(this.#i),this.#i=null,this.#r?.close(),this.#r=null,this.#a=!1}#c(){const e=new EventSource("/api/v1/stream");this.#r=e,e.onopen=()=>{this.#a=!0,this.#l=Date.now()},e.addEventListener("state",n=>this.#d(n)),e.onerror=()=>{this.#a=!1}}#d(e){this.#l=Date.now(),this.#a=!0;const n=JSON.parse(e.data),r=this.state;if(this.state=n,this.catalog===null)return;const l=n.catalog_count!==this.catalog.product_count,f=r!==null&&n.presentation_digest!==r.presentation_digest;(l||f)&&this.#u()}#v(){const e=Bt(Date.now()-this.#l,this.#a);this.link=e,e.mustReconnect&&(this.#r?.close(),this.#l=Date.now(),this.#c())}async#u(){if(!this.#o){this.#o=!0;try{this.catalog=await Ue(),this.catalogError=""}catch{this.catalog===null&&(this.catalogError="Catalogue indisponible. Prévenez un responsable.")}finally{this.#o=!1}}}}const xe="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function Ae(){let s=Date.now();const e=new Array(26);for(let r=9;r>=0;r--)e[r]=xe[s%32],s=Math.floor(s/32);const n=new Uint8Array(16);crypto.getRandomValues(n);for(let r=0;r<16;r++)e[10+r]=xe[n[r]%32];return e.join("")}var Vt=E('
                                                                        ');function Gt(s,e){Y(e,!0);const n=new Ut;let r=D(ze(Ve)),l=D(""),f=D(!1);const c=v(()=>t(l)!==""||t(f));let d=D(null),_=D(-1),o=D(null);const i=v(()=>n.catalog),y=v(()=>t(i)===null?[]:Qe(t(i))),k=v(()=>t(i)===null?[]:Ke(t(i))),m=v(()=>Xe(t(y),t(r),t(l))),I=v(()=>{const a={};for(const S of t(i)?.categories??[])a[S.code]=S.color;return a}),g=v(()=>n.state),P=v(()=>n.presentation),B=v(()=>Mt(t(g))),R=v(()=>{if(n.catalogError!=="")return{message:n.catalogError,hint:"Le poste réessaie tout seul."};if(t(y).length>0)return{message:"Aucun produit ne correspond.",hint:"Effacez des lettres ou changez de rayon."};if(t(i)!==null&&t(i).products.length>0)return{message:t(i).products.every(U=>U.mode==="by_unit")&&!t(P).show_by_unit_products?"Aucun produit à montrer : ce poste masque les produits vendus à l’unité, et le catalogue reçu ne contient que ceux-là.":"Aucun produit à montrer : les réglages d’affichage de ce poste masquent tout ce qu’il a reçu.",hint:"Le fichier est bien arrivé : prévenez un responsable, c’est un réglage de ce poste qui vide la grille."};const a=t(g)?.station??0;return{message:a>0?`Catalogue vide. En attente du fichier flv_${a}.csv.`:"Catalogue vide. En attente du fichier du poste.",hint:"Prévenez un responsable : aucun produit ne peut être pesé."}}),H=v(()=>t(g)?.state==="rejected"?t(g).product?.id??null:null),V=v(()=>t(g)?.state==="faulted"||t(g)?.state==="out_of_service"),A=["product_armed","weight_present","weight_stable","awaiting_stability","validating","printing"],O=v(()=>t(g)!==null&&A.includes(t(g).state)?t(g).product?.id??null:null),G=v(()=>Pt(t(i)?.updated_at??""));ne(()=>(n.start(),()=>n.stop())),ne(()=>{const a=n.state?.revision??-1;t(d)!==null&&a>t(_)&&(b(d,null),b(_,-1))});async function X(a){if(t(d)===null){b(d,a.id,!0),b(_,n.state?.revision??-1,!0);try{await Ge({product_id:a.id,tare_g:n.state?.weight.tare_g??0,units:1,manual_weight_g:0,seen_weight_g:n.state?.weight.gross_g??0,measurement_seq:n.state?.weight.seq??0,key:Ae()})}catch{b(d,null)}}}async function K(){const a=n.state?.reprint.job_id;a===void 0||a===""||await Je(a,Ae())}function F(){b(l,""),b(f,!1)}function p(){b(f,!0)}function C(){const a=t(m).length===1?t(m)[0]:void 0;a!==void 0&&X(a)}function M(a){if(!(a.metaKey||a.ctrlKey||a.altKey)&&!(a.target instanceof HTMLElement&&a.target.tagName==="INPUT")&&!(t(o)!==null||t(V))){if(a.key==="Escape"){a.preventDefault(),F();return}if(a.key==="Backspace"){a.preventDefault(),b(l,t(l).slice(0,-1),!0);return}if(a.key==="Enter"){C();return}a.key.length===1&&/[a-zA-Z0-9 ]/.test(a.key)&&(a.key===" "&&t(l)===""||(a.preventDefault(),b(l,t(l)+a.key)))}}ne(()=>(window.addEventListener("keydown",M),()=>window.removeEventListener("keydown",M)));async function $(){(await st(()=>import("./mount-zw3SIkYn.js"),__vite__mapDeps([0,1,2,3]))).mountAdmin(document.body)}var ie=Vt(),oe=u(ie);{let a=v(()=>t(o)!==null);ot(oe,{get snapshot(){return t(g)},get showWeight(){return n.link.showWeight},get linkBanner(){return n.link.banner},get taring(){return t(a)},ontare:()=>b(o,"")})}var J=h(oe,2);{var ce=a=>{Ot(a,{get grams(){return t(o)},ondigit:S=>b(o,(t(o)??"")+S),onclear:()=>b(o,""),oncancel:()=>b(o,null),onconfirm:()=>b(o,null)})};W(J,a=>{t(o)!==null&&a(ce)})}var le=h(J,2);{let a=v(()=>t(i)?.pricing.primary_code??""),S=v(()=>Object.fromEntries((t(i)?.pricing.tiers??[]).map(Q=>[Q.code,Q.abbrev]))),U=v(()=>t(i)===null&&n.catalogError==="");Et(le,{get products(){return t(m)},get colors(){return t(I)},get primaryCode(){return t(a)},get tierAbbrev(){return t(S)},get selectedID(){return t(O)},get rejectedID(){return t(H)},get busyID(){return t(d)},get showPrices(){return t(P).show_grid_prices},get gridColumns(){return t(P).grid_columns},get loading(){return t(U)},get emptyMessage(){return t(R).message},get emptyHint(){return t(R).hint},onpick:X})}var ue=h(le,2);{var pe=a=>{At(a,{get query(){return t(l)},onquery:S=>b(l,S,!0),onclose:F,onenter:C})};W(ue,a=>{t(c)&&a(pe)})}var de=h(ue,2);dt(de,{get chips(){return t(k)},get active(){return t(r)},get searchFieldOpen(){return t(c)},onselect:a=>b(r,a,!0),onopensearch:p});var ve=h(de,2);{let a=v(()=>t(g)?.last_label??null),S=v(()=>t(g)?.reprint.available??!1),U=v(()=>t(i)?.app_version??"");qt(ve,{get label(){return t(a)},get available(){return t(S)},get catalogAt(){return t(G)},get productCount(){return t(y).length},get appVersion(){return t(U)},get healthy(){return t(B)},onreprint:K,onadmin:$})}var x=h(ve,2);{var L=a=>{{let S=v(()=>t(g).message?.text??"Prévenez un responsable.");we(a,{title:"Poste indisponible",get detail(){return t(S)},get code(){return t(g).fault_code},action:{label:"J’ai compris",run:()=>{Ye()}}})}},j=a=>{we(a,{title:"Poste hors service",detail:"Ce poste ne peut pas peser. Prévenez un responsable.",get code(){return t(g).fault_code}})};W(x,a=>{t(g)?.state==="faulted"?a(L):t(g)?.state==="out_of_service"&&a(j,1)})}w(s,ie),Z()}const Xt=5,Kt="ResizeObserver loop";function Jt(){let s=!1,e=!1;const n=(c,d)=>{s||(s=!0,$e(c,d),Qt(),setTimeout(()=>location.reload(),Xt*1e3))},r=c=>{e||(e=!0,Ze(c))},l=c=>{if((c.error===void 0||c.error===null)&&c.message.startsWith(Kt)){r(c.message);return}n(c.message,c.error?.stack??"")},f=c=>{n(String(c.reason),c.reason?.stack??"")};return window.addEventListener("error",l),window.addEventListener("unhandledrejection",f),()=>{window.removeEventListener("error",l),window.removeEventListener("unhandledrejection",f)}}function Qt(){const s=document.createElement("div");s.className="fatal",s.append(me("h1","Une erreur est survenue"),me("p","L’écran va se recharger tout seul."),me("p","ERR-UI-01","code")),document.body.appendChild(s)}function me(s,e,n=""){const r=document.createElement(s);return r.textContent=e,n!==""&&(r.className=n),r}Jt();document.addEventListener("contextmenu",s=>{Yt(s.target)||s.preventDefault()});document.addEventListener("dragstart",s=>s.preventDefault());et(Gt,{target:document.getElementById("app")});function Yt(s){return s instanceof Element&&s.closest("[data-admin]")!==null} +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/mount-D3UhzFzr.js","assets/app-BaRSXv80.js","assets/app-Cfxw4Luj.css","assets/mount-MmpjwdB2.css"])))=>i.map(i=>d[i]); +import{e as Ie,u as je,d as re,p as Y,a as q,i as W,I as ee,t as z,b as N,c as w,f as Z,g as E,h as t,s as h,j as ae,k as te,l as se,m as T,n as v,o as u,q as Pe,r as fe,w as De,v as Te,x as ne,y as Ce,R as Re,z as b,A as D,B as qe,T as Le,C as Ne,N as Oe,D as Me,E as We,F as Be,G as Fe,H as ze,M as He,J as Ue,K as Ve,L as Ge,O as Xe,P as Ke,Q as Je,S as Qe,U as Ye,V as Ze,W as $e,X as et}from"./app-BaRSXv80.js";class be{#e=new WeakMap;#t;#n;static entries=new WeakMap;constructor(e){this.#n=e}observe(e,n){var r=this.#e.get(e)||new Set;return r.add(n),this.#e.set(e,r),this.#s().observe(e,this.#n),()=>{var l=this.#e.get(e);l.delete(n),l.size===0&&(this.#e.delete(e),this.#t.unobserve(e))}}#s(){return this.#t??(this.#t=new ResizeObserver(e=>{for(var n of e){be.entries.set(n.target,n);for(var r of this.#e.get(n.target)||[])r(n)}}))}}var tt=new be({box:"border-box"});function he(s,e,n){var r=tt.observe(s,()=>n(s[e]));Ie(()=>(je(()=>n(s[e])),r))}const nt="modulepreload",at=function(s){return"/"+s},ye={},st=function(e,n,r){let l=Promise.resolve();if(n&&n.length>0){let _=function(o){return Promise.all(o.map(i=>Promise.resolve(i).then(y=>({status:"fulfilled",value:y}),y=>({status:"rejected",reason:y}))))};document.getElementsByTagName("link");const c=document.querySelector("meta[property=csp-nonce]"),d=c?.nonce||c?.getAttribute("nonce");l=_(n.map(o=>{if(o=at(o),o in ye)return;ye[o]=!0;const i=o.endsWith(".css"),y=i?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${o}"]${y}`))return;const k=document.createElement("link");if(k.rel=i?"stylesheet":nt,i||(k.as="script"),k.crossOrigin="",k.href=o,d&&k.setAttribute("nonce",d),document.head.appendChild(k),i)return new Promise((m,I)=>{k.addEventListener("load",m),k.addEventListener("error",()=>I(new Error(`Unable to preload CSS for ${o}`)))})}))}function f(c){const d=new Event("vite:preloadError",{cancelable:!0});if(d.payload=c,window.dispatchEvent(d),!d.defaultPrevented)throw c}return l.then(c=>{for(const d of c||[])d.status==="rejected"&&f(d.reason);return e().catch(f)})};var rt=E('

                                                                        kg

                                                                        ',1),it=E('

                                                                        kg

                                                                        Poids indisponible

                                                                        ',1),lt=E('');function ot(s,e){Y(e,!0);const n=q(e,"taring",3,!1),r=v(()=>{if(e.linkBanner!=="")return e.linkBanner;const A=e.snapshot?.message??null;if(A!==null)return A.text;switch(e.snapshot?.state){case"product_armed":return"Posez votre produit";case"awaiting_stability":return"Pesée en cours…";case"printing":return"Étiquette en cours…";case"entering_tare":return"Tapez la tare en grammes";case"scale_lost":return"Le poids n’est plus disponible.";default:return"Touchez votre produit, l’étiquette sort"}}),l=v(()=>e.snapshot===null?"var(--waiting)":e.snapshot.state==="faulted"||e.snapshot.state==="scale_lost"?"var(--fault)":e.snapshot.state==="rejected"?"var(--warning)":e.snapshot.state==="succeeded"||e.snapshot.weight.latched?"var(--ready)":"var(--waiting)"),f=v(()=>e.showWeight&&e.snapshot!==null&&e.snapshot.weight.available&&!e.snapshot.weight.expired),c=v(()=>{const A=e.snapshot?.message;return A!=null?A.level:e.snapshot?.state==="faulted"||e.snapshot?.state==="scale_lost"?"error":e.snapshot?.state==="rejected"?"warn":"info"});var d=lt(),_=u(d);let o;var i=u(_);{var y=A=>{var O=rt(),G=Pe(O),X=u(G),K=h(G,2),F=u(K);z(()=>{T(X,e.snapshot.weight.net_text),T(F,`tare ${e.snapshot.weight.tare_g??""} g`)}),w(A,O)},k=A=>{var O=it();w(A,O)};W(i,A=>{t(f)&&e.snapshot?A(y):A(k,-1)})}var m=h(_,2);let I;var g=u(m),P=h(m,2);let B;var R=u(P);ee(R,{name:"tare",size:"1.4em"});var H=h(P,2);let V;z(()=>{ae(d,"data-state",e.snapshot?.state??"initializing"),o=te(_,"",o,{"border-left-color":t(l)}),I=se(m,1,"instruction svelte-4gizvm",null,I,{warn:t(c)==="warn",error:t(c)==="error"}),T(g,t(r)),B=se(P,1,"tare-key touch-target svelte-4gizvm",null,B,{active:n()}),V=te(H,"",V,{background:t(l)})}),N("click",P,function(...A){e.ontare?.apply(this,A)}),w(s,d),Z()}re(["click"]);var ct=E(''),ut=E('');function dt(s,e){Y(e,!0);var n=ut(),r=u(n);fe(r,21,()=>e.chips,d=>d.code,(d,_)=>{var o=ct();let i,y;var k=h(u(o),2),m=u(k),I=h(k,2),g=u(I);z(P=>{i=se(o,1,"chip touch-target svelte-atgty1",null,i,{active:t(_).code===e.active}),ae(o,"aria-pressed",t(_).code===e.active),y=te(o,"",y,P),T(m,t(_).label),T(g,t(_).count)},[()=>({"--chip-ink":Te(t(_).color),"--chip-wash":De(t(_).color)})]),N("click",o,()=>e.onselect(t(_).code)),w(d,o)});var l=h(r,2);let f;var c=u(l);ee(c,{name:"search",size:"1.75rem"}),z(()=>{f=se(l,1,"chip touch-target search-key svelte-atgty1",null,f,{active:e.searchFieldOpen}),ae(l,"aria-pressed",e.searchFieldOpen)}),N("click",l,function(...d){e.onopensearch?.apply(this,d)}),w(s,n),Z()}re(["click"]);var vt=E(''),gt=E('

                                                                        '),ht=E('

                                                                        ');function we(s,e){Y(e,!0);const n=q(e,"action",3,null);var r=ht(),l=u(r),f=u(l),c=u(f);ee(c,{name:"alert",size:"3rem"});var d=h(f,2),_=u(d),o=h(d,2),i=u(o),y=h(o,2);{var k=g=>{var P=vt(),B=u(P);z(()=>T(B,n().label)),N("click",P,function(...R){n().run?.apply(this,R)}),w(g,P)};W(y,g=>{n()!==null&&g(k)})}var m=h(y,2);{var I=g=>{var P=gt(),B=u(P);z(()=>T(B,e.code)),w(g,P)};W(m,g=>{e.code!==""&&g(I)})}z(()=>{T(_,e.title),T(i,e.detail)}),w(s,r),Z()}re(["click"]);var ft=E('
                                                                        '),pt=E('

                                                                        Chargement du catalogue…

                                                                        ',1),_t=E('

                                                                        '),mt=E('

                                                                        '),bt=E('
                                                                        '),yt=E('
                                                                        '),wt=E('
                                                                        ');function Et(s,e){Y(e,!0);const n=q(e,"gridColumns",3,0),r=q(e,"colors",19,()=>({})),l=q(e,"primaryCode",3,""),f=q(e,"tierAbbrev",19,()=>({})),c=q(e,"selectedID",3,null),d=q(e,"rejectedID",3,null),_=q(e,"busyID",3,null),o=q(e,"showPrices",3,!0),i=q(e,"loading",3,!1),y=q(e,"emptyMessage",3,"Aucun produit ne correspond."),k=q(e,"emptyHint",3,""),m=8,I=26,g="'Inter Variable', system-ui, sans-serif",P="'Inter Variable'",B=16;let R=D(0),H=D(0),V=D(!1);ne(()=>{const x=document.fonts;x!==void 0&&x.load(`700 ${Re}px ${P}`).catch(()=>[]).then(()=>{b(V,!0)})});const A=v(()=>(t(V),Be(g,700)));let O=D(null),G=D(0),X=D(0);const K=v(()=>n()>0?n():t(R)<=0||t(H)<=0?0:Math.max(1,Math.floor((t(R)+m)/(t(H)+m)))),F=v(()=>t(K)>0&&t(R)>0?(t(R)-(t(K)-1)*m)/t(K):0),p=v(()=>n()>0&&t(F)>0&&t(H)>0?t(F)/t(H):1),C=v(()=>We(t(p))),M=v(()=>n()>0?`grid-template-columns: ${Ne(n())}`:void 0),$=v(()=>t(F)>0?t(F)-I:0),ie=v(()=>t(G)>0?t(G):t($));ne(()=>{t(F),t(p),e.products.length;const x=t(O)?.querySelector(".name-box");b(G,x?.clientWidth??0,!0)});const oe=v(()=>{const x=new Map;if(t(A)===null||t(ie)<=0)return x;const L=t(X)>0?t(X):Oe;for(const j of e.products)x.set(j.id,Me(j.name,t(ie),t(A),L,t(C)));return x});var J=wt(),ce=u(J),le=h(ce,2),ue=h(le,2);{var pe=x=>{var L=pt(),j=Pe(L);fe(j,21,()=>({length:B}),qe,(a,S)=>{var U=ft();w(a,U)}),z(()=>te(j,t(M))),he(j,"clientWidth",a=>b(R,a)),w(x,L)},de=x=>{var L=mt(),j=u(L),a=u(j),S=h(j,2);{var U=Q=>{var ge=_t(),_e=u(ge);z(()=>T(_e,k())),w(Q,ge)};W(S,Q=>{k()!==""&&Q(U)})}z(()=>T(a,y())),w(x,L)},ve=x=>{var L=yt();fe(L,21,()=>e.products,j=>j.id,(j,a)=>{var S=bt(),U=u(S);{let Q=v(()=>t(oe).get(t(a).id)??t(C)),ge=v(()=>t(a).id===c()),_e=v(()=>t(a).id===d()),Se=v(()=>t(a).id===_());Le(U,{get product(){return t(a)},get nameSizePx(){return t(Q)},get categoryColor(){return r()[t(a).category_code]},get primaryCode(){return l()},get tierAbbrev(){return f()},get selected(){return t(ge)},get rejected(){return t(_e)},get busy(){return t(Se)},get showPrice(){return o()},get onpick(){return e.onpick}})}w(j,S)}),z(()=>{ae(L,"data-tile-count",e.products.length),te(L,t(M))}),he(L,"clientWidth",j=>b(R,j)),w(x,L)};W(ue,x=>{i()?x(pe):e.products.length===0?x(de,1):x(ve,-1)})}Ce(J,x=>b(O,x),()=>t(O)),z(()=>{ae(J,"data-tile-scale",t(p)),te(J,`--tile-scale: ${t(p)??""}`)}),he(ce,"clientHeight",x=>b(X,x)),he(le,"clientWidth",x=>b(H,x)),w(s,J),Z()}var kt=E(''),xt=E('
                                                                        ');function At(s,e){Y(e,!0);let n=D(null);ne(()=>{t(n)?.focus()});var r=xt(),l=u(r);ee(l,{name:"search",size:"1.75rem"});var f=h(l,2);Ce(f,i=>b(n,i),()=>t(n));var c=h(f,2);{var d=i=>{var y=kt();N("click",y,function(...k){e.onclose?.apply(this,k)}),w(i,y)};W(c,i=>{e.query!==""&&i(d)})}var _=h(c,2),o=u(_);ee(o,{name:"close",size:"1.5rem"}),z(()=>Fe(f,e.query)),N("input",f,i=>e.onquery(i.currentTarget.value)),N("keydown",f,i=>{i.key==="Escape"?(i.preventDefault(),e.onclose()):i.key==="Enter"&&(i.preventDefault(),e.onenter())}),N("click",_,function(...i){e.onclose?.apply(this,i)}),w(s,r),Z()}re(["input","keydown","click"]);function Pt(s){if(s==="")return"";const e=new Date(s);if(Number.isNaN(e.getTime()))return"";const n=l=>String(l).padStart(2,"0");return`${`${n(e.getDate())}/${n(e.getMonth()+1)}/${e.getFullYear()}`} ${n(e.getHours())}:${n(e.getMinutes())}:${n(e.getSeconds())}`}function Ct(s){return s.mode==="by_unit"?`${s.product_name} ${s.quantity} ${s.quantity>1?"unités":"unité"}`:`${s.product_name} ${s.net_text} kg`}var zt=E('Catalogue du '),St=E('Catalogue en attente'),It=E(' '),jt=E('aucune pour le moment'),Dt=E(' '),Tt=E(''),Rt=E('
                                                                        Dernière étiquette
                                                                        ');function qt(s,e){Y(e,!0);const n=q(e,"catalogAt",3,""),r=q(e,"productCount",3,0),l=q(e,"appVersion",3,""),f=v(()=>[r()>0?`${r()} produits pesables`:"",l()!==""?`application ${l()}`:""].filter(p=>p!=="").join(" · "));var c=Rt();let d;var _=u(c),o=u(_);{var i=p=>{var C=zt(),M=h(u(C)),$=u(M);z(()=>T($,n())),w(p,C)},y=p=>{var C=St();w(p,C)};W(o,p=>{n()!==""?p(i):p(y,-1)})}var k=h(o,2);{var m=p=>{var C=It(),M=u(C);z(()=>T(M,t(f))),w(p,C)};W(k,p=>{t(f)!==""&&p(m)})}var I=h(_,4),g=h(u(I),2);{var P=p=>{var C=jt();w(p,C)},B=p=>{var C=Dt(),M=u(C);z($=>T(M,$),[()=>Ct(e.label)]),w(p,C)};W(g,p=>{e.label===null?p(P):p(B,-1)})}var R=h(I,2);{var H=p=>{var C=Tt();z(()=>C.disabled=!e.available),N("click",C,function(...M){e.onreprint?.apply(this,M)}),w(p,C)};W(R,p=>{e.label!==null&&p(H)})}var V=h(R,2),A=u(V);let O;var G=h(A,2),X=u(G),K=h(V,2),F=u(K);ee(F,{name:"settings",size:"1.75rem"}),z(()=>{d=se(c,1,"bar svelte-161y12f",null,d,{live:e.label!==null}),O=se(A,1,"dot svelte-161y12f",null,O,{fault:!e.healthy}),ae(A,"aria-label",e.healthy?"Matériel disponible":"Matériel indisponible"),T(X,e.healthy?"Balance et imprimante disponibles":"Matériel indisponible")}),N("click",K,function(...p){e.onadmin?.apply(this,p)}),w(s,c),Z()}re(["click"]);var Lt=E(''),Nt=E('

                                                                        Tare g

                                                                        ');function Ot(s,e){Y(e,!0);const n=["1","2","3","4","5","6","7","8","9","0"];var r=Nt(),l=u(r),f=h(u(l),2),c=u(f),d=h(l,2);fe(d,20,()=>n,m=>m,(m,I)=>{var g=Lt(),P=u(g);z(()=>T(P,I)),N("click",g,()=>e.ondigit(I)),w(m,g)});var _=h(d,2),o=u(_),i=h(o,2),y=h(i,2),k=u(y);ee(k,{name:"check",size:"1.5rem"}),z(()=>T(c,e.grams===""?"0":e.grams)),N("click",o,function(...m){e.onclear?.apply(this,m)}),N("click",i,function(...m){e.oncancel?.apply(this,m)}),N("click",y,function(...m){e.onconfirm?.apply(this,m)}),w(s,r),Z()}re(["click"]);function Mt(s){return s===null||s.state==="initializing"?!1:s.scale.connected&&s.printer.health!=="faulted"}const Ee=1500,Wt=2e3,ke=5e3;function Bt(s,e){return e?s>=ke?{showWeight:!1,banner:"Poids indisponible",mustReconnect:!0}:s>=Wt?{showWeight:!1,banner:"Poids indisponible",mustReconnect:!1}:s>=Ee?{showWeight:!1,banner:"",mustReconnect:!1}:{showWeight:!0,banner:"",mustReconnect:!1}:{showWeight:s=ke}}const Ft=250,Ht={show_grid_prices:!0,idle_timeout_s:45,reprint_window_s:60,sound:!0,show_by_unit_products:!1,grid_columns:0,min_products_for_chip:He};class Ut{#e=D(null);get state(){return t(this.#e)}set state(e){b(this.#e,e,!0)}#t=D(null);get catalog(){return t(this.#t)}set catalog(e){b(this.#t,e,!0)}#n=D(ze({showWeight:!0,banner:"",mustReconnect:!1}));get link(){return t(this.#n)}set link(e){b(this.#n,e,!0)}#s=D("");get catalogError(){return t(this.#s)}set catalogError(e){b(this.#s,e,!0)}#r=null;#i=null;#l=Date.now();#a=!1;#o=!1;get presentation(){return this.catalog?.presentation??Ht}start(){this.#u(),this.#c(),this.#i=setInterval(()=>this.#v(),Ft)}stop(){this.#i!==null&&clearInterval(this.#i),this.#i=null,this.#r?.close(),this.#r=null,this.#a=!1}#c(){const e=new EventSource("/api/v1/stream");this.#r=e,e.onopen=()=>{this.#a=!0,this.#l=Date.now()},e.addEventListener("state",n=>this.#d(n)),e.onerror=()=>{this.#a=!1}}#d(e){this.#l=Date.now(),this.#a=!0;const n=JSON.parse(e.data),r=this.state;if(this.state=n,this.catalog===null)return;const l=n.catalog_count!==this.catalog.product_count,f=r!==null&&n.presentation_digest!==r.presentation_digest;(l||f)&&this.#u()}#v(){const e=Bt(Date.now()-this.#l,this.#a);this.link=e,e.mustReconnect&&(this.#r?.close(),this.#l=Date.now(),this.#c())}async#u(){if(!this.#o){this.#o=!0;try{this.catalog=await Ue(),this.catalogError=""}catch{this.catalog===null&&(this.catalogError="Catalogue indisponible. Prévenez un responsable.")}finally{this.#o=!1}}}}const xe="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function Ae(){let s=Date.now();const e=new Array(26);for(let r=9;r>=0;r--)e[r]=xe[s%32],s=Math.floor(s/32);const n=new Uint8Array(16);crypto.getRandomValues(n);for(let r=0;r<16;r++)e[10+r]=xe[n[r]%32];return e.join("")}var Vt=E('
                                                                        ');function Gt(s,e){Y(e,!0);const n=new Ut;let r=D(ze(Ve)),l=D(""),f=D(!1);const c=v(()=>t(l)!==""||t(f));let d=D(null),_=D(-1),o=D(null);const i=v(()=>n.catalog),y=v(()=>t(i)===null?[]:Qe(t(i))),k=v(()=>t(i)===null?[]:Ke(t(i))),m=v(()=>Xe(t(y),t(r),t(l))),I=v(()=>{const a={};for(const S of t(i)?.categories??[])a[S.code]=S.color;return a}),g=v(()=>n.state),P=v(()=>n.presentation),B=v(()=>Mt(t(g))),R=v(()=>{if(n.catalogError!=="")return{message:n.catalogError,hint:"Le poste réessaie tout seul."};if(t(y).length>0)return{message:"Aucun produit ne correspond.",hint:"Effacez des lettres ou changez de rayon."};if(t(i)!==null&&t(i).products.length>0)return{message:t(i).products.every(U=>U.mode==="by_unit")&&!t(P).show_by_unit_products?"Aucun produit à montrer : ce poste masque les produits vendus à l’unité, et le catalogue reçu ne contient que ceux-là.":"Aucun produit à montrer : les réglages d’affichage de ce poste masquent tout ce qu’il a reçu.",hint:"Le fichier est bien arrivé : prévenez un responsable, c’est un réglage de ce poste qui vide la grille."};const a=t(g)?.station??0;return{message:a>0?`Catalogue vide. En attente du fichier flv_${a}.csv.`:"Catalogue vide. En attente du fichier du poste.",hint:"Prévenez un responsable : aucun produit ne peut être pesé."}}),H=v(()=>t(g)?.state==="rejected"?t(g).product?.id??null:null),V=v(()=>t(g)?.state==="faulted"||t(g)?.state==="out_of_service"),A=["product_armed","weight_present","weight_stable","awaiting_stability","validating","printing"],O=v(()=>t(g)!==null&&A.includes(t(g).state)?t(g).product?.id??null:null),G=v(()=>Pt(t(i)?.updated_at??""));ne(()=>(n.start(),()=>n.stop())),ne(()=>{const a=n.state?.revision??-1;t(d)!==null&&a>t(_)&&(b(d,null),b(_,-1))});async function X(a){if(t(d)===null){b(d,a.id,!0),b(_,n.state?.revision??-1,!0);try{await Ge({product_id:a.id,tare_g:n.state?.weight.tare_g??0,units:1,manual_weight_g:0,seen_weight_g:n.state?.weight.gross_g??0,measurement_seq:n.state?.weight.seq??0,key:Ae()})}catch{b(d,null)}}}async function K(){const a=n.state?.reprint.job_id;a===void 0||a===""||await Je(a,Ae())}function F(){b(l,""),b(f,!1)}function p(){b(f,!0)}function C(){const a=t(m).length===1?t(m)[0]:void 0;a!==void 0&&X(a)}function M(a){if(!(a.metaKey||a.ctrlKey||a.altKey)&&!(a.target instanceof HTMLElement&&a.target.tagName==="INPUT")&&!(t(o)!==null||t(V))){if(a.key==="Escape"){a.preventDefault(),F();return}if(a.key==="Backspace"){a.preventDefault(),b(l,t(l).slice(0,-1),!0);return}if(a.key==="Enter"){C();return}a.key.length===1&&/[a-zA-Z0-9 ]/.test(a.key)&&(a.key===" "&&t(l)===""||(a.preventDefault(),b(l,t(l)+a.key)))}}ne(()=>(window.addEventListener("keydown",M),()=>window.removeEventListener("keydown",M)));async function $(){(await st(()=>import("./mount-D3UhzFzr.js"),__vite__mapDeps([0,1,2,3]))).mountAdmin(document.body)}var ie=Vt(),oe=u(ie);{let a=v(()=>t(o)!==null);ot(oe,{get snapshot(){return t(g)},get showWeight(){return n.link.showWeight},get linkBanner(){return n.link.banner},get taring(){return t(a)},ontare:()=>b(o,"")})}var J=h(oe,2);{var ce=a=>{Ot(a,{get grams(){return t(o)},ondigit:S=>b(o,(t(o)??"")+S),onclear:()=>b(o,""),oncancel:()=>b(o,null),onconfirm:()=>b(o,null)})};W(J,a=>{t(o)!==null&&a(ce)})}var le=h(J,2);{let a=v(()=>t(i)?.pricing.primary_code??""),S=v(()=>Object.fromEntries((t(i)?.pricing.tiers??[]).map(Q=>[Q.code,Q.abbrev]))),U=v(()=>t(i)===null&&n.catalogError==="");Et(le,{get products(){return t(m)},get colors(){return t(I)},get primaryCode(){return t(a)},get tierAbbrev(){return t(S)},get selectedID(){return t(O)},get rejectedID(){return t(H)},get busyID(){return t(d)},get showPrices(){return t(P).show_grid_prices},get gridColumns(){return t(P).grid_columns},get loading(){return t(U)},get emptyMessage(){return t(R).message},get emptyHint(){return t(R).hint},onpick:X})}var ue=h(le,2);{var pe=a=>{At(a,{get query(){return t(l)},onquery:S=>b(l,S,!0),onclose:F,onenter:C})};W(ue,a=>{t(c)&&a(pe)})}var de=h(ue,2);dt(de,{get chips(){return t(k)},get active(){return t(r)},get searchFieldOpen(){return t(c)},onselect:a=>b(r,a,!0),onopensearch:p});var ve=h(de,2);{let a=v(()=>t(g)?.last_label??null),S=v(()=>t(g)?.reprint.available??!1),U=v(()=>t(i)?.app_version??"");qt(ve,{get label(){return t(a)},get available(){return t(S)},get catalogAt(){return t(G)},get productCount(){return t(y).length},get appVersion(){return t(U)},get healthy(){return t(B)},onreprint:K,onadmin:$})}var x=h(ve,2);{var L=a=>{{let S=v(()=>t(g).message?.text??"Prévenez un responsable.");we(a,{title:"Poste indisponible",get detail(){return t(S)},get code(){return t(g).fault_code},action:{label:"J’ai compris",run:()=>{Ye()}}})}},j=a=>{we(a,{title:"Poste hors service",detail:"Ce poste ne peut pas peser. Prévenez un responsable.",get code(){return t(g).fault_code}})};W(x,a=>{t(g)?.state==="faulted"?a(L):t(g)?.state==="out_of_service"&&a(j,1)})}w(s,ie),Z()}const Xt=5,Kt="ResizeObserver loop";function Jt(){let s=!1,e=!1;const n=(c,d)=>{s||(s=!0,$e(c,d),Qt(),setTimeout(()=>location.reload(),Xt*1e3))},r=c=>{e||(e=!0,Ze(c))},l=c=>{if((c.error===void 0||c.error===null)&&c.message.startsWith(Kt)){r(c.message);return}n(c.message,c.error?.stack??"")},f=c=>{n(String(c.reason),c.reason?.stack??"")};return window.addEventListener("error",l),window.addEventListener("unhandledrejection",f),()=>{window.removeEventListener("error",l),window.removeEventListener("unhandledrejection",f)}}function Qt(){const s=document.createElement("div");s.className="fatal",s.append(me("h1","Une erreur est survenue"),me("p","L’écran va se recharger tout seul."),me("p","ERR-UI-01","code")),document.body.appendChild(s)}function me(s,e,n=""){const r=document.createElement(s);return r.textContent=e,n!==""&&(r.className=n),r}Jt();document.addEventListener("contextmenu",s=>{Yt(s.target)||s.preventDefault()});document.addEventListener("dragstart",s=>s.preventDefault());et(Gt,{target:document.getElementById("app")});function Yt(s){return s instanceof Element&&s.closest("[data-admin]")!==null} diff --git a/internal/web/dist/assets/mount-zw3SIkYn.js b/internal/web/dist/assets/mount-D3UhzFzr.js similarity index 94% rename from internal/web/dist/assets/mount-zw3SIkYn.js rename to internal/web/dist/assets/mount-D3UhzFzr.js index 64b38b9..3f6541e 100644 --- a/internal/web/dist/assets/mount-zw3SIkYn.js +++ b/internal/web/dist/assets/mount-D3UhzFzr.js @@ -1,4 +1,4 @@ -import{Y as Tr,Z as zr,_ as jr,$ as Ea,a0 as Vt,e as Rr,a1 as or,a2 as Or,a3 as Ar,a4 as Nr,u as ma,a5 as Ca,a6 as Ir,a7 as ur,x as wt,A as B,h as e,z as v,H as vt,d as ot,a as Be,s as n,i as _,t as f,l as Nt,j as be,m as o,b as Ce,c as l,o as s,g as c,p as Ue,I as Dr,f as Me,n as u,q as ae,a8 as Ia,r as ze,G as Gt,a9 as ea,aa as Tt,B as da,ab as lt,ac as Xt,ad as ka,C as Fr,J as cr,y as sa,T as Ur,ae as Ht,F as Mr,E as Wr,D as Br,O as Vr,K as Gr,X as Hr,af as Jr}from"./app-BaRSXv80.js";function dr(a){throw new Error("https://svelte.dev/e/lifecycle_outside_component")}function Kr(a,t,...r){var i=new jr(a);Tr(()=>{const d=t()??null;i.ensure(d,d&&(p=>d(p,...r)))},zr)}function Pa(a,t,r=!1){if(a.multiple){if(t==null)return;if(!Or(t))return Ar();for(var i of a.options)i.selected=t.includes(la(i));return}for(i of a.options){var d=la(i);if(Nr(d,t)){i.selected=!0;return}}(!r||t!==void 0)&&(a.selectedIndex=-1)}function vr(a){var t=new MutationObserver(()=>{"__value"in a&&Pa(a,a.__value)});t.observe(a,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["value"]}),or(()=>{t.disconnect()})}function $r(a,t,r=t){var i=new WeakSet,d=!0;Ea(a,"change",p=>{var y=p?"[selected]":":checked",z;if(a.multiple)z=[].map.call(a.querySelectorAll(y),la);else{var m=a.querySelector(y)??a.querySelector("option:not([disabled])");z=m&&la(m)}r(z),a.__value=z,Vt!==null&&i.add(Vt)}),Rr(()=>{var p=t();if(a===document.activeElement){var y=Vt;if(i.has(y))return}if(Pa(a,p,d),d&&p===void 0){var z=a.querySelector(":checked");z!==null&&(p=la(z),r(p))}a.__value=p,d=!1}),vr(a)}function la(a){return"__value"in a?a.__value:a.value}function ia(a,t,r=t){var i=new WeakSet;Ea(a,"input",async d=>{var p=d?a.defaultValue:a.value;if(p=ba(a)?wa(p):p,r(p),Vt!==null&&i.add(Vt),await Ir(),p!==(p=t())){var y=a.selectionStart,z=a.selectionEnd,m=a.value.length;if(a.value=p??"",z!==null){var j=a.value.length;y===z&&z===m&&j>m?(a.selectionStart=j,a.selectionEnd=j):(a.selectionStart=y,a.selectionEnd=Math.min(z,j))}}}),ma(t)==null&&a.value&&(r(ba(a)?wa(a.value):a.value),Vt!==null&&i.add(Vt)),Ca(()=>{var d=t();if(a===document.activeElement){var p=Vt;if(i.has(p))return}ba(a)&&d===wa(a.value)||a.type==="date"&&!d&&!a.value||d!==a.value&&(a.value=d??"")})}function Da(a,t,r=t){Ea(a,"change",i=>{var d=i?a.defaultChecked:a.checked;r(d)}),ma(t)==null&&r(a.checked),Ca(()=>{var i=t();a.checked=!!i})}function ba(a){var t=a.type;return t==="number"||t==="range"}function wa(a){return a===""?null:+a}function Fa(a,t,r,i,d){var p=()=>{i(r[a])};r.addEventListener(t,p),d?Ca(()=>{r[a]=d()}):p(),(r===document.body||r===window||r===document)&&or(()=>{r.removeEventListener(t,p)})}function Ta(a){ur===null&&dr(),wt(()=>{const t=ma(a);if(typeof t=="function")return t})}function Yr(a){ur===null&&dr(),Ta(()=>()=>ma(a))}const Xr="ERR-CFG-02";class Pt extends Error{constructor(t,r,i="",d=[]){super(r),this.status=t,this.code=i,this.faults=d,this.name="AdminError"}status;code;faults;get needsPassword(){return this.status===401}get needsFirstPassword(){return this.status===409&&this.code===Xr}get needsCredentials(){return this.needsPassword||this.needsFirstPassword}}function fa(a){return a instanceof Pt&&a.needsCredentials}function Qr(){return It("/admin/api/health")}function Zr(){return He("/admin/api/troubleshooting/reprint",{})}function en(){return He("/admin/api/troubleshooting/reload-catalog",{})}function tn(a){return He("/admin/api/troubleshooting/manual-entry",{on:a})}function an(){return He("/admin/api/troubleshooting/roll-changed",{})}function rn(a){return He("/admin/api/troubleshooting/fallback-printer",{on:a})}function nn(){return He("/admin/api/troubleshooting/test-scale",{})}function sn(){return He("/admin/api/troubleshooting/test-printer",{})}function ln(){return He("/admin/api/troubleshooting/test-label",{})}async function pr(a){const t=new FormData;t.append("file",a,a.name);const r=await fetch("/admin/api/catalog/import",{method:"POST",body:t});return Ra(r,"POST /admin/api/catalog/import")}const on="/admin/api/diagnostic.zip";function un(a){return He("/admin/api/session",{password:a})}async function cn(){await fetch("/admin/api/session",{method:"DELETE"})}function dn(a,t){return He("/admin/api/session/recovery",{code:a,password:t})}function za(){return It("/admin/api/config")}function vn(a){return ja("PUT","/admin/api/config",a)}function pn(){return He("/admin/api/config/reload",{})}function fn(){return He("/admin/api/restart",{})}function gn(){return He("/admin/api/reboot",{})}function mn(){return ja("DELETE","/admin/api/reboot",{})}function hn(){return He("/admin/api/config/confirm",{})}async function _n(){return(await It("/admin/api/config/versions")).versions}function bn(a){return He("/admin/api/config/restore",{version:a})}async function wn(){return(await It("/admin/api/ports")).ports}async function yn(){return(await It("/admin/api/printers")).printers}async function xn(){return(await He("/admin/api/printers/discover",{})).printers}function kn(a){return He("/admin/api/scale/detect",{port:a})}async function Ua(a,t){return(await He("/admin/api/scale/capture",{port:a,seconds:t})).frames}function fr(a){return He("/admin/api/printer/test?what="+a,{})}function qn(a,t,r,i){const d=new URLSearchParams({template:a,demo:t?"1":"0",dual:r?"1":"0"});return d.set("t",String(i)),"/admin/api/label/preview.png?"+d.toString()}async function Sn(a){const t=new URLSearchParams(a);return(await It("/admin/api/journal?"+t)).weighings}function Ln(a){return"/admin/api/journal/export.csv?"+new URLSearchParams(a).toString()}async function En(a){const t=new URLSearchParams(a);return(await It("/admin/api/technical?"+t)).entries}function Cn(a){const t=a===void 0?"":"?id="+String(a);return It("/admin/api/imports"+t)}function Pn(){return He("/admin/api/catalog/reload",{})}function Tn(){return He("/admin/api/catalog/forget-quarantine",{})}function Ma(a,t){return He("/admin/api/products/"+encodeURIComponent(a)+"/decision",t)}function zn(a){return He("/admin/api/replay",{frame:a})}async function It(a){const t=await fetch(a,{headers:{accept:"application/json"}});return Ra(t,"GET "+a)}function He(a,t){return ja("POST",a,t)}async function ja(a,t,r){const i=await fetch(t,{method:a,headers:{"content-type":"application/json"},body:JSON.stringify(r)});return Ra(i,a+" "+t)}async function Ra(a,t){const r=await a.text();if(!a.ok){const i=gr(r);throw new Pt(a.status,Rn(r,t,a.status)+jn(a),i?.code??"",i?.faults??[])}if(r!=="")return JSON.parse(r)}function jn(a){if(a.status!==429)return"";const t=Number(a.headers.get("Retry-After")??"");if(!Number.isFinite(t)||t<=0)return"";const r=Math.ceil(t/60);return r>1?` Réessayez dans ${String(r)} minutes.`:" Réessayez dans une minute."}function Rn(a,t,r){const i=gr(a);return i?.message!==void 0&&i.message!==""?i.message:`${t} a répondu ${String(r)}.`}function gr(a){try{return JSON.parse(a)}catch{return null}}function On(){return It("/admin/api/update")}function An(){return He("/admin/api/update/check",{})}function Nn(a){return He("/admin/api/update/apply",{version:a})}class In{constructor(t){this.admin=t}admin;#t=B(null);get config(){return e(this.#t)}set config(t){v(this.#t,t,!0)}#r=B("");get fingerprint(){return e(this.#r)}set fingerprint(t){v(this.#r,t,!0)}#a=B(vt([]));get retired(){return e(this.#a)}set retired(t){v(this.#a,t,!0)}#e=B(null);get pending(){return e(this.#e)}set pending(t){v(this.#e,t,!0)}#n=B(vt([]));get faults(){return e(this.#n)}set faults(t){v(this.#n,t,!0)}#l=B(!1);get dirty(){return e(this.#l)}set dirty(t){v(this.#l,t,!0)}async load(){const t=await this.admin.load(()=>za());t!==null&&(this.config=t.config,this.fingerprint=t.config_fingerprint,this.retired=t.retired_keys??[],this.pending=t.pending_confirmation,this.faults=[],this.dirty=!1)}value(t){let r=this.config;for(const i of t.split(".")){if(r===null||typeof r!="object")return;r=r[i]}return r}text(t){const r=this.value(t);return r==null?"":String(r)}number(t){const r=this.value(t);return typeof r=="number"?r:Number(r??0)}flag(t){return this.value(t)===!0}set(t,r){if(this.config===null)return;const i=t.split("."),d=i.pop();if(d===void 0)return;let p=this.config;for(const y of i){const z=p[y];(z===null||typeof z!="object")&&(p[y]={}),p=p[y]}p[d]=r,this.dirty=!0}unset(t){const r=t.split("."),i=r.pop();if(i===void 0||this.config===null)return!1;let d=this.config;for(const p of r){if(d===null||typeof d!="object")return!1;d=d[p]}return d===null||typeof d!="object"?!1:(delete d[i],this.dirty=!0,!0)}async save(){if(this.config===null)return!1;this.faults=[];const t=this.config;let r;try{r=await vn(t)}catch(i){if(fa(i))throw i;return this.admin.report(i),this.faults=Dn(this.admin),!1}return this.config=r.config,this.fingerprint=r.config_fingerprint,this.retired=r.retired_keys??[],this.pending=r.pending_confirmation,this.dirty=!1,!0}async confirm(){try{await hn()}catch(t){if(fa(t))throw t;this.admin.report(t);return}this.admin.notice="La configuration est confirmée.",this.pending=null,await this.admin.refresh()}dropRetired(t){if(t.includes("[")){this.admin.actionError=`« ${t} » est logée dans un tableau : cet écran ne sait pas la retirer de là. Modifiez le fichier de configuration lui-même.`;return}this.unset(t)&&(this.retired=this.retired.filter(r=>r!==t))}}function Dn(a){return a.lastFaults}const Fn={"station.number":"Numéro du poste","station.name":"Nom du poste","station.coop":"Nom de la coopérative","network.listen":"Adresse d’écoute","network.admin_on_lan":"Administration accessible depuis le réseau","ui.language":"Langue","ui.sound":"Son","ui.idle_timeout_s":"Retour à l’accueil après (secondes)","ui.reprint_window_s":"Réimpression possible pendant (secondes)","ui.show_grid_prices":"Afficher les prix sur les tuiles","ui.show_by_unit_products":"Afficher les produits vendus à l’unité","ui.grid_columns":"Colonnes de la grille","ui.min_products_for_chip":"Produits minimum pour afficher une catégorie","scale.type":"Protocole de la balance","scale.present":"Ce poste a une balance","scale.manual_entry_allowed":"Saisie manuelle autorisée","scale.degrade_after_s":"Passer en mode dégradé après (secondes)","scale.options.port":"Port série","scale.options.baud":"Vitesse (bauds)","scale.options.bits":"Bits de données","scale.options.parity":"Parité","scale.options.stop":"Bits d’arrêt","scale.options.backoff_min_ms":"Attente minimale avant réessai (ms)","scale.options.backoff_max_ms":"Attente maximale avant réessai (ms)","printer.type":"Pilote d’impression","printer.template":"Gabarit d’étiquette","printer.options.transport":"Transport","printer.options.queue":"File d’impression","printer.options.path":"Fichier de périphérique","printer.options.address":"Adresse réseau","printer.options.darkness":"Noircissement","printer.options.speed":"Vitesse d’impression","printer.options.offset_x":"Décalage horizontal (dots)","printer.options.offset_y":"Décalage vertical (dots)","printer.options.invert_bits":"Inverser les points","printer.options.copies":"Exemplaires","printer.options.roll_capacity":"Étiquettes par rouleau","pricing.amount_rounding":"Arrondi du montant","pricing.unit_price_rounding":"Arrondi du prix au kilo","pricing.primary_code":"Tarif principal","pricing.reference_code":"Tarif de référence","barcode.verify_reference_check_digit":"Vérifier la clé de contrôle","limits.empty_max_g":"Plateau considéré vide en dessous de (g)","limits.basket_check_enabled":"Vérifier la présence du panier","limits.basket_min_g":"Poids du panier, borne basse (g)","limits.basket_max_g":"Poids du panier, borne haute (g)","limits.min_weight_g":"Poids minimum accepté","limits.max_weight_g":"Poids maximum accepté","limits.max_tare_g":"Tare maximale","limits.min_units":"Unités minimum","limits.max_units":"Unités maximum","limits.max_amount_cents":"Montant maximum (centimes)","stability.mode":"Exigence de stabilité","stability.min_duration_ms":"Durée de stabilité (ms)","stability.tolerance_g":"Tolérance de stabilité (g)","stability.timeout_ms":"Délai d’attente de stabilité (ms)","stability.on_timeout":"Au bout du délai","stability.min_latch_rate":"Taux d’accroche minimal","stability.latch_rate_window_ms":"Fenêtre de mesure du taux (ms)","stability.expiry_floor_ms":"Péremption, plancher (ms)","stability.expiry_ceiling_ms":"Péremption, plafond (ms)","stability.expiry_factor":"Péremption, facteur","catalog.type":"Où le poste va chercher le catalogue","catalog.options.directory":"Répertoire surveillé","catalog.options.url":"Adresse du serveur","catalog.options.username":"Compte","catalog.options.password":"Mot de passe","catalog.options.separator":"Séparateur du CSV","catalog.options.poll_interval_s":"Vérifier toutes les (secondes)","catalog.options.stable_polls":"Vérifications avant lecture","catalog.options.max_file_size_mb":"Taille maximale du fichier (Mo)","catalog.options.max_image_size_kb":"Taille maximale d’une image (Ko)","catalog.options.min_readable_ratio":"Part minimale de lignes lisibles","catalog.options.max_weighable_drop":"Baisse maximale des produits pesables","catalog.options.max_archives":"Archives conservées","catalog.options.archive_days":"Archives conservées (jours)","catalog.options.failures_before_reject":"Échecs avant mise en quarantaine","catalog.images.source":"Origine des photos","catalog.images.path":"Répertoire des photos","catalog.fallback_category":"Rayon par défaut","journal.max_rows":"Pesées conservées","journal.max_days":"Pesées conservées (jours)","journal.max_technical":"Événements techniques conservés","admin.session_minutes":"Durée d’une session (minutes)","admin.attempts_per_minute":"Tentatives par minute","maintenance.weekly_integrity_check":"Contrôle d’intégrité hebdomadaire","maintenance.disk_alert_mb":"Alerte disque en dessous de (Mo)"};function qt(a){return Fn[a]??a}const Un={station:"l’identité du poste",network:"le réseau",ui:"l’écran client",scale:"la balance",printer:"l’imprimante",pricing:"les tarifs",barcode:"le code-barres",limits:"les garde-fous",stability:"la stabilité",catalog:"le catalogue",journal:"le journal",maintenance:"la maintenance"};function Mn(a){return Un[a]??a}const Wn={scale:"balance",printer:"imprimante",catalog:"catalogue",ui:"écran",config:"configuration",http:"réseau",system:"système"};function mr(a){return Wn[a]??"origine inconnue"}const qa="openscale.admin.preferences",hr="technical";class Bn{#t=B(vt(Vn()));get showTechnicalNames(){return e(this.#t)}set showTechnicalNames(t){v(this.#t,t,!0)}toggleTechnicalNames(){this.showTechnicalNames=!this.showTechnicalNames,Gn(this.showTechnicalNames)}}function Vn(){try{return globalThis.localStorage?.getItem(qa)===hr}catch{return!1}}function Gn(a){try{a?globalThis.localStorage?.setItem(qa,hr):globalThis.localStorage?.removeItem(qa)}catch{}}const St=new Bn,Hn=3e3,Jn=["hardware","label","rules","catalog","journal","station","update"];function Wa(a){return Jn.includes(a)}function Kn(a){return a instanceof Pt&&a.needsCredentials}class $n{constructor(t=Hn){this.periodMs=t}periodMs;#t=B(null);get health(){return e(this.#t)}set health(t){v(this.#t,t,!0)}#r=B("dashboard");get page(){return e(this.#r)}set page(t){v(this.#r,t,!0)}#a=B(!1);get expert(){return e(this.#a)}set expert(t){v(this.#a,t,!0)}#e=B("");get linkError(){return e(this.#e)}set linkError(t){v(this.#e,t,!0)}#n=B("");get actionError(){return e(this.#n)}set actionError(t){v(this.#n,t,!0)}#l=B(!1);get needsFirstPassword(){return e(this.#l)}set needsFirstPassword(t){v(this.#l,t,!0)}#d=B("");get notice(){return e(this.#d)}set notice(t){v(this.#d,t,!0)}#v=B(!1);get busy(){return e(this.#v)}set busy(t){v(this.#v,t,!0)}#p=B(vt([]));get lastFaults(){return e(this.#p)}set lastFaults(t){v(this.#p,t,!0)}#f=B(null);get pending(){return e(this.#f)}set pending(t){v(this.#f,t,!0)}#i=null;#o=null;start(){this.refresh(),this.#i=setInterval(()=>{this.refresh()},this.periodMs)}stop(){this.#i!==null&&clearInterval(this.#i),this.#i=null}async refresh(){try{this.health=await Qr(),this.linkError=""}catch(t){this.linkError=Ba(t)}}#u(){this.busy=!0,this.notice="",this.actionError="",this.needsFirstPassword=!1}#s(t){this.actionError=Ba(t),this.needsFirstPassword=t instanceof Pt&&t.needsFirstPassword,this.#m(t)}async run(t){this.#u();let r=null;try{r=await t(),this.notice=r.message}catch(i){this.#s(i)}finally{this.busy=!1}return await this.refresh(),r}async login(t){this.#u();try{return await un(t),this.expert=!0,this.notice="Session d’administration ouverte.",!0}catch(r){return this.expert=!1,this.#s(r),!1}finally{this.busy=!1}}async recover(t,r){this.#u();try{const i=await dn(t,r);return this.expert=!0,this.notice=i.warning??"Le mot de passe est remplacé et la session est ouverte.",!0}catch(i){return this.#s(i),!1}finally{this.busy=!1}}async protect(t){try{return await t()}catch(r){if(!Kn(r))return this.#s(r),null;if(!await this.#g(r))return null;try{return await t()}catch(i){return this.#s(i),null}}}#g(t){return this.pending={kind:t.needsFirstPassword?"first-password":"password",message:t.message},new Promise(r=>{this.#o=r})}async answerPassword(t){await this.login(t)&&this.#c(!0)}async answerRecovery(t,r){await this.recover(t,r)&&this.#c(!0)}cancelPassword(){this.#c(!1)}#c(t){this.pending=null,this.notice="";const r=this.#o;this.#o=null,r?.(t)}async logout(){await cn(),this.expert=!1,this.page="dashboard",this.notice="Session d’administration fermée."}open(t){this.actionError="",this.notice="",this.needsFirstPassword=!1,this.page=t}async load(t){try{const r=await t();return this.actionError="",this.lastFaults=[],r}catch(r){return this.lastFaults=r instanceof Pt?r.faults:[],this.#s(r),null}}report(t){this.lastFaults=t instanceof Pt?t.faults:[],this.#s(t)}#m(t){t instanceof Pt&&t.needsPassword&&(this.expert=!1)}}function Ba(a){return a instanceof Pt?a.message:a instanceof Error?"Le poste n’a pas répondu : "+a.message:"Le poste n’a pas répondu."}var Yn=c('clé'),Xn=c('');function Te(a,t){const r=Be(t,"kind",3,"read"),i=Be(t,"busy",3,!1),d=Be(t,"disabled",3,!1),p=Be(t,"protected",3,!1),y=Be(t,"act",3,void 0);var z=Xn();let m;var j=s(z),S=n(j);{var A=L=>{var W=Yn();l(L,W)};_(S,L=>{p()&&L(A)})}f(()=>{m=Nt(z,1,`act ${r()??""}`,"svelte-5tpq0o",m,{"touch-target":r()==="destructive",busy:i()}),be(z,"data-kind",r()),be(z,"data-act",y()),z.disabled=d()||i(),o(j,`${(i()?"En cours…":t.label)??""} `)}),Ce("click",z,function(...L){t.onrun?.apply(this,L)}),l(a,z)}ot(["click"]);var Qn=c(`

                                                                        Le code de secours a été tiré à l’installation de ce poste et imprimé +import{Y as Tr,Z as zr,_ as jr,$ as Ea,a0 as Vt,e as Rr,a1 as or,a2 as Or,a3 as Ar,a4 as Nr,u as ma,a5 as Ca,a6 as Ir,a7 as ur,x as wt,A as B,h as e,z as v,H as vt,d as ot,a as Be,s as n,i as _,t as f,l as Nt,j as be,m as o,b as Ce,c as l,o as s,g as c,p as Ue,I as Dr,f as Me,n as u,q as ae,a8 as Ia,r as ze,G as Gt,a9 as ea,aa as Tt,B as da,ab as lt,ac as Xt,ad as ka,C as Fr,J as cr,y as sa,T as Ur,ae as Ht,F as Mr,E as Wr,D as Br,O as Vr,K as Gr,X as Hr,af as Jr}from"./app-BaRSXv80.js";function dr(a){throw new Error("https://svelte.dev/e/lifecycle_outside_component")}function Kr(a,t,...r){var i=new jr(a);Tr(()=>{const d=t()??null;i.ensure(d,d&&(p=>d(p,...r)))},zr)}function Pa(a,t,r=!1){if(a.multiple){if(t==null)return;if(!Or(t))return Ar();for(var i of a.options)i.selected=t.includes(la(i));return}for(i of a.options){var d=la(i);if(Nr(d,t)){i.selected=!0;return}}(!r||t!==void 0)&&(a.selectedIndex=-1)}function vr(a){var t=new MutationObserver(()=>{"__value"in a&&Pa(a,a.__value)});t.observe(a,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["value"]}),or(()=>{t.disconnect()})}function $r(a,t,r=t){var i=new WeakSet,d=!0;Ea(a,"change",p=>{var y=p?"[selected]":":checked",z;if(a.multiple)z=[].map.call(a.querySelectorAll(y),la);else{var m=a.querySelector(y)??a.querySelector("option:not([disabled])");z=m&&la(m)}r(z),a.__value=z,Vt!==null&&i.add(Vt)}),Rr(()=>{var p=t();if(a===document.activeElement){var y=Vt;if(i.has(y))return}if(Pa(a,p,d),d&&p===void 0){var z=a.querySelector(":checked");z!==null&&(p=la(z),r(p))}a.__value=p,d=!1}),vr(a)}function la(a){return"__value"in a?a.__value:a.value}function ia(a,t,r=t){var i=new WeakSet;Ea(a,"input",async d=>{var p=d?a.defaultValue:a.value;if(p=ba(a)?wa(p):p,r(p),Vt!==null&&i.add(Vt),await Ir(),p!==(p=t())){var y=a.selectionStart,z=a.selectionEnd,m=a.value.length;if(a.value=p??"",z!==null){var j=a.value.length;y===z&&z===m&&j>m?(a.selectionStart=j,a.selectionEnd=j):(a.selectionStart=y,a.selectionEnd=Math.min(z,j))}}}),ma(t)==null&&a.value&&(r(ba(a)?wa(a.value):a.value),Vt!==null&&i.add(Vt)),Ca(()=>{var d=t();if(a===document.activeElement){var p=Vt;if(i.has(p))return}ba(a)&&d===wa(a.value)||a.type==="date"&&!d&&!a.value||d!==a.value&&(a.value=d??"")})}function Da(a,t,r=t){Ea(a,"change",i=>{var d=i?a.defaultChecked:a.checked;r(d)}),ma(t)==null&&r(a.checked),Ca(()=>{var i=t();a.checked=!!i})}function ba(a){var t=a.type;return t==="number"||t==="range"}function wa(a){return a===""?null:+a}function Fa(a,t,r,i,d){var p=()=>{i(r[a])};r.addEventListener(t,p),d?Ca(()=>{r[a]=d()}):p(),(r===document.body||r===window||r===document)&&or(()=>{r.removeEventListener(t,p)})}function Ta(a){ur===null&&dr(),wt(()=>{const t=ma(a);if(typeof t=="function")return t})}function Yr(a){ur===null&&dr(),Ta(()=>()=>ma(a))}const Xr="ERR-CFG-02";class Pt extends Error{constructor(t,r,i="",d=[]){super(r),this.status=t,this.code=i,this.faults=d,this.name="AdminError"}status;code;faults;get needsPassword(){return this.status===401}get needsFirstPassword(){return this.status===409&&this.code===Xr}get needsCredentials(){return this.needsPassword||this.needsFirstPassword}}function fa(a){return a instanceof Pt&&a.needsCredentials}function Qr(){return It("/admin/api/health")}function Zr(){return He("/admin/api/troubleshooting/reprint",{})}function en(){return He("/admin/api/troubleshooting/reload-catalog",{})}function tn(a){return He("/admin/api/troubleshooting/manual-entry",{on:a})}function an(){return He("/admin/api/troubleshooting/roll-changed",{})}function rn(a){return He("/admin/api/troubleshooting/fallback-printer",{on:a})}function nn(){return He("/admin/api/troubleshooting/test-scale",{})}function sn(){return He("/admin/api/troubleshooting/test-printer",{})}function ln(){return He("/admin/api/troubleshooting/test-label",{})}async function pr(a){const t=new FormData;t.append("file",a,a.name);const r=await fetch("/admin/api/catalog/import",{method:"POST",body:t});return Ra(r,"POST /admin/api/catalog/import")}const on="/admin/api/diagnostic.zip";function un(a){return He("/admin/api/session",{password:a})}async function cn(){await fetch("/admin/api/session",{method:"DELETE"})}function dn(a,t){return He("/admin/api/session/recovery",{code:a,password:t})}function za(){return It("/admin/api/config")}function vn(a){return ja("PUT","/admin/api/config",a)}function pn(){return He("/admin/api/config/reload",{})}function fn(){return He("/admin/api/restart",{})}function gn(){return He("/admin/api/reboot",{})}function mn(){return ja("DELETE","/admin/api/reboot",{})}function hn(){return He("/admin/api/config/confirm",{})}async function _n(){return(await It("/admin/api/config/versions")).versions}function bn(a){return He("/admin/api/config/restore",{version:a})}async function wn(){return(await It("/admin/api/ports")).ports}async function yn(){return(await It("/admin/api/printers")).printers}async function xn(){return(await He("/admin/api/printers/discover",{})).printers}function kn(a){return He("/admin/api/scale/detect",{port:a})}async function Ua(a,t){return(await He("/admin/api/scale/capture",{port:a,seconds:t})).frames}function fr(a){return He("/admin/api/printer/test?what="+a,{})}function qn(a,t,r,i){const d=new URLSearchParams({template:a,demo:t?"1":"0",dual:r?"1":"0"});return d.set("t",String(i)),"/admin/api/label/preview.png?"+d.toString()}async function Sn(a){const t=new URLSearchParams(a);return(await It("/admin/api/journal?"+t)).weighings}function Ln(a){return"/admin/api/journal/export.csv?"+new URLSearchParams(a).toString()}async function En(a){const t=new URLSearchParams(a);return(await It("/admin/api/technical?"+t)).entries}function Cn(a){const t=a===void 0?"":"?id="+String(a);return It("/admin/api/imports"+t)}function Pn(){return He("/admin/api/catalog/reload",{})}function Tn(){return He("/admin/api/catalog/forget-quarantine",{})}function Ma(a,t){return He("/admin/api/products/"+encodeURIComponent(a)+"/decision",t)}function zn(a){return He("/admin/api/replay",{frame:a})}async function It(a){const t=await fetch(a,{headers:{accept:"application/json"}});return Ra(t,"GET "+a)}function He(a,t){return ja("POST",a,t)}async function ja(a,t,r){const i=await fetch(t,{method:a,headers:{"content-type":"application/json"},body:JSON.stringify(r)});return Ra(i,a+" "+t)}async function Ra(a,t){const r=await a.text();if(!a.ok){const i=gr(r);throw new Pt(a.status,Rn(r,t,a.status)+jn(a),i?.code??"",i?.faults??[])}if(r!=="")return JSON.parse(r)}function jn(a){if(a.status!==429)return"";const t=Number(a.headers.get("Retry-After")??"");if(!Number.isFinite(t)||t<=0)return"";const r=Math.ceil(t/60);return r>1?` Réessayez dans ${String(r)} minutes.`:" Réessayez dans une minute."}function Rn(a,t,r){const i=gr(a);return i?.message!==void 0&&i.message!==""?i.message:`${t} a répondu ${String(r)}.`}function gr(a){try{return JSON.parse(a)}catch{return null}}function On(){return It("/admin/api/update")}function An(){return He("/admin/api/update/check",{})}function Nn(a){return He("/admin/api/update/apply",{version:a})}class In{constructor(t){this.admin=t}admin;#t=B(null);get config(){return e(this.#t)}set config(t){v(this.#t,t,!0)}#r=B("");get fingerprint(){return e(this.#r)}set fingerprint(t){v(this.#r,t,!0)}#a=B(vt([]));get retired(){return e(this.#a)}set retired(t){v(this.#a,t,!0)}#e=B(null);get pending(){return e(this.#e)}set pending(t){v(this.#e,t,!0)}#n=B(vt([]));get faults(){return e(this.#n)}set faults(t){v(this.#n,t,!0)}#l=B(!1);get dirty(){return e(this.#l)}set dirty(t){v(this.#l,t,!0)}async load(){const t=await this.admin.load(()=>za());t!==null&&(this.config=t.config,this.fingerprint=t.config_fingerprint,this.retired=t.retired_keys??[],this.pending=t.pending_confirmation,this.faults=[],this.dirty=!1)}value(t){let r=this.config;for(const i of t.split(".")){if(r===null||typeof r!="object")return;r=r[i]}return r}text(t){const r=this.value(t);return r==null?"":String(r)}number(t){const r=this.value(t);return typeof r=="number"?r:Number(r??0)}flag(t){return this.value(t)===!0}set(t,r){if(this.config===null)return;const i=t.split("."),d=i.pop();if(d===void 0)return;let p=this.config;for(const y of i){const z=p[y];(z===null||typeof z!="object")&&(p[y]={}),p=p[y]}p[d]=r,this.dirty=!0}unset(t){const r=t.split("."),i=r.pop();if(i===void 0||this.config===null)return!1;let d=this.config;for(const p of r){if(d===null||typeof d!="object")return!1;d=d[p]}return d===null||typeof d!="object"?!1:(delete d[i],this.dirty=!0,!0)}async save(){if(this.config===null)return!1;this.faults=[];const t=this.config;let r;try{r=await vn(t)}catch(i){if(fa(i))throw i;return this.admin.report(i),this.faults=Dn(this.admin),!1}return this.config=r.config,this.fingerprint=r.config_fingerprint,this.retired=r.retired_keys??[],this.pending=r.pending_confirmation,this.dirty=!1,!0}async confirm(){try{await hn()}catch(t){if(fa(t))throw t;this.admin.report(t);return}this.admin.notice="La configuration est confirmée.",this.pending=null,await this.admin.refresh()}dropRetired(t){if(t.includes("[")){this.admin.actionError=`« ${t} » est logée dans un tableau : cet écran ne sait pas la retirer de là. Modifiez le fichier de configuration lui-même.`;return}this.unset(t)&&(this.retired=this.retired.filter(r=>r!==t))}}function Dn(a){return a.lastFaults}const Fn={"station.number":"Numéro du poste","station.name":"Nom du poste","station.coop":"Nom de la coopérative","network.listen":"Adresse d’écoute","network.admin_on_lan":"Administration accessible depuis le réseau","ui.language":"Langue","ui.sound":"Son","ui.idle_timeout_s":"Retour à l’accueil après (secondes)","ui.reprint_window_s":"Réimpression possible pendant (secondes)","ui.show_grid_prices":"Afficher les prix sur les tuiles","ui.show_by_unit_products":"Afficher les produits vendus à l’unité","ui.grid_columns":"Colonnes de la grille","ui.min_products_for_chip":"Tuiles minimum pour donner sa puce à une catégorie","scale.type":"Protocole de la balance","scale.present":"Ce poste a une balance","scale.manual_entry_allowed":"Saisie manuelle autorisée","scale.degrade_after_s":"Passer en mode dégradé après (secondes)","scale.options.port":"Port série","scale.options.baud":"Vitesse (bauds)","scale.options.bits":"Bits de données","scale.options.parity":"Parité","scale.options.stop":"Bits d’arrêt","scale.options.backoff_min_ms":"Attente minimale avant réessai (ms)","scale.options.backoff_max_ms":"Attente maximale avant réessai (ms)","printer.type":"Pilote d’impression","printer.template":"Gabarit d’étiquette","printer.options.transport":"Transport","printer.options.queue":"File d’impression","printer.options.path":"Fichier de périphérique","printer.options.address":"Adresse réseau","printer.options.darkness":"Noircissement","printer.options.speed":"Vitesse d’impression","printer.options.offset_x":"Décalage horizontal (dots)","printer.options.offset_y":"Décalage vertical (dots)","printer.options.invert_bits":"Inverser les points","printer.options.copies":"Exemplaires","printer.options.roll_capacity":"Étiquettes par rouleau","pricing.amount_rounding":"Arrondi du montant","pricing.unit_price_rounding":"Arrondi du prix au kilo","pricing.primary_code":"Tarif principal","pricing.reference_code":"Tarif de référence","barcode.verify_reference_check_digit":"Vérifier la clé de contrôle","limits.empty_max_g":"Plateau considéré vide en dessous de (g)","limits.basket_check_enabled":"Vérifier la présence du panier","limits.basket_min_g":"Poids du panier, borne basse (g)","limits.basket_max_g":"Poids du panier, borne haute (g)","limits.min_weight_g":"Poids minimum accepté","limits.max_weight_g":"Poids maximum accepté","limits.max_tare_g":"Tare maximale","limits.min_units":"Unités minimum","limits.max_units":"Unités maximum","limits.max_amount_cents":"Montant maximum (centimes)","stability.mode":"Exigence de stabilité","stability.min_duration_ms":"Durée de stabilité (ms)","stability.tolerance_g":"Tolérance de stabilité (g)","stability.timeout_ms":"Délai d’attente de stabilité (ms)","stability.on_timeout":"Au bout du délai","stability.min_latch_rate":"Taux d’accroche minimal","stability.latch_rate_window_ms":"Fenêtre de mesure du taux (ms)","stability.expiry_floor_ms":"Péremption, plancher (ms)","stability.expiry_ceiling_ms":"Péremption, plafond (ms)","stability.expiry_factor":"Péremption, facteur","catalog.type":"Où le poste va chercher le catalogue","catalog.options.directory":"Répertoire surveillé","catalog.options.url":"Adresse du serveur","catalog.options.username":"Compte","catalog.options.password":"Mot de passe","catalog.options.separator":"Séparateur du CSV","catalog.options.poll_interval_s":"Vérifier toutes les (secondes)","catalog.options.stable_polls":"Vérifications avant lecture","catalog.options.max_file_size_mb":"Taille maximale du fichier (Mo)","catalog.options.max_image_size_kb":"Taille maximale d’une image (Ko)","catalog.options.min_readable_ratio":"Part minimale de lignes lisibles","catalog.options.max_weighable_drop":"Baisse maximale des produits pesables","catalog.options.max_archives":"Archives conservées","catalog.options.archive_days":"Archives conservées (jours)","catalog.options.failures_before_reject":"Échecs avant mise en quarantaine","catalog.images.source":"Origine des photos","catalog.images.path":"Répertoire des photos","catalog.fallback_category":"Rayon par défaut","journal.max_rows":"Pesées conservées","journal.max_days":"Pesées conservées (jours)","journal.max_technical":"Événements techniques conservés","admin.session_minutes":"Durée d’une session (minutes)","admin.attempts_per_minute":"Tentatives par minute","maintenance.weekly_integrity_check":"Contrôle d’intégrité hebdomadaire","maintenance.disk_alert_mb":"Alerte disque en dessous de (Mo)"};function qt(a){return Fn[a]??a}const Un={station:"l’identité du poste",network:"le réseau",ui:"l’écran client",scale:"la balance",printer:"l’imprimante",pricing:"les tarifs",barcode:"le code-barres",limits:"les garde-fous",stability:"la stabilité",catalog:"le catalogue",journal:"le journal",maintenance:"la maintenance"};function Mn(a){return Un[a]??a}const Wn={scale:"balance",printer:"imprimante",catalog:"catalogue",ui:"écran",config:"configuration",http:"réseau",system:"système"};function mr(a){return Wn[a]??"origine inconnue"}const qa="openscale.admin.preferences",hr="technical";class Bn{#t=B(vt(Vn()));get showTechnicalNames(){return e(this.#t)}set showTechnicalNames(t){v(this.#t,t,!0)}toggleTechnicalNames(){this.showTechnicalNames=!this.showTechnicalNames,Gn(this.showTechnicalNames)}}function Vn(){try{return globalThis.localStorage?.getItem(qa)===hr}catch{return!1}}function Gn(a){try{a?globalThis.localStorage?.setItem(qa,hr):globalThis.localStorage?.removeItem(qa)}catch{}}const St=new Bn,Hn=3e3,Jn=["hardware","label","rules","catalog","journal","station","update"];function Wa(a){return Jn.includes(a)}function Kn(a){return a instanceof Pt&&a.needsCredentials}class $n{constructor(t=Hn){this.periodMs=t}periodMs;#t=B(null);get health(){return e(this.#t)}set health(t){v(this.#t,t,!0)}#r=B("dashboard");get page(){return e(this.#r)}set page(t){v(this.#r,t,!0)}#a=B(!1);get expert(){return e(this.#a)}set expert(t){v(this.#a,t,!0)}#e=B("");get linkError(){return e(this.#e)}set linkError(t){v(this.#e,t,!0)}#n=B("");get actionError(){return e(this.#n)}set actionError(t){v(this.#n,t,!0)}#l=B(!1);get needsFirstPassword(){return e(this.#l)}set needsFirstPassword(t){v(this.#l,t,!0)}#d=B("");get notice(){return e(this.#d)}set notice(t){v(this.#d,t,!0)}#v=B(!1);get busy(){return e(this.#v)}set busy(t){v(this.#v,t,!0)}#p=B(vt([]));get lastFaults(){return e(this.#p)}set lastFaults(t){v(this.#p,t,!0)}#f=B(null);get pending(){return e(this.#f)}set pending(t){v(this.#f,t,!0)}#i=null;#o=null;start(){this.refresh(),this.#i=setInterval(()=>{this.refresh()},this.periodMs)}stop(){this.#i!==null&&clearInterval(this.#i),this.#i=null}async refresh(){try{this.health=await Qr(),this.linkError=""}catch(t){this.linkError=Ba(t)}}#u(){this.busy=!0,this.notice="",this.actionError="",this.needsFirstPassword=!1}#s(t){this.actionError=Ba(t),this.needsFirstPassword=t instanceof Pt&&t.needsFirstPassword,this.#m(t)}async run(t){this.#u();let r=null;try{r=await t(),this.notice=r.message}catch(i){this.#s(i)}finally{this.busy=!1}return await this.refresh(),r}async login(t){this.#u();try{return await un(t),this.expert=!0,this.notice="Session d’administration ouverte.",!0}catch(r){return this.expert=!1,this.#s(r),!1}finally{this.busy=!1}}async recover(t,r){this.#u();try{const i=await dn(t,r);return this.expert=!0,this.notice=i.warning??"Le mot de passe est remplacé et la session est ouverte.",!0}catch(i){return this.#s(i),!1}finally{this.busy=!1}}async protect(t){try{return await t()}catch(r){if(!Kn(r))return this.#s(r),null;if(!await this.#g(r))return null;try{return await t()}catch(i){return this.#s(i),null}}}#g(t){return this.pending={kind:t.needsFirstPassword?"first-password":"password",message:t.message},new Promise(r=>{this.#o=r})}async answerPassword(t){await this.login(t)&&this.#c(!0)}async answerRecovery(t,r){await this.recover(t,r)&&this.#c(!0)}cancelPassword(){this.#c(!1)}#c(t){this.pending=null,this.notice="";const r=this.#o;this.#o=null,r?.(t)}async logout(){await cn(),this.expert=!1,this.page="dashboard",this.notice="Session d’administration fermée."}open(t){this.actionError="",this.notice="",this.needsFirstPassword=!1,this.page=t}async load(t){try{const r=await t();return this.actionError="",this.lastFaults=[],r}catch(r){return this.lastFaults=r instanceof Pt?r.faults:[],this.#s(r),null}}report(t){this.lastFaults=t instanceof Pt?t.faults:[],this.#s(t)}#m(t){t instanceof Pt&&t.needsPassword&&(this.expert=!1)}}function Ba(a){return a instanceof Pt?a.message:a instanceof Error?"Le poste n’a pas répondu : "+a.message:"Le poste n’a pas répondu."}var Yn=c('clé'),Xn=c('');function Te(a,t){const r=Be(t,"kind",3,"read"),i=Be(t,"busy",3,!1),d=Be(t,"disabled",3,!1),p=Be(t,"protected",3,!1),y=Be(t,"act",3,void 0);var z=Xn();let m;var j=s(z),S=n(j);{var A=L=>{var W=Yn();l(L,W)};_(S,L=>{p()&&L(A)})}f(()=>{m=Nt(z,1,`act ${r()??""}`,"svelte-5tpq0o",m,{"touch-target":r()==="destructive",busy:i()}),be(z,"data-kind",r()),be(z,"data-act",y()),z.disabled=d()||i(),o(j,`${(i()?"En cours…":t.label)??""} `)}),Ce("click",z,function(...L){t.onrun?.apply(this,L)}),l(a,z)}ot(["click"]);var Qn=c(`

                                                                        Le code de secours a été tiré à l’installation de ce poste et imprimé sur sa fiche, rangée dans le classeur du magasin. Il n’existe nulle part ailleurs.

                                                                        `,1),Zn=c(`

                                                                        Oublié ? Le code de secours de la fiche d’installation en repose un. Un responsable peut aussi le faire en ligne de commande, avec openscale config password.

                                                                        `,1),es=c('

                                                                        '),ts=c('');function as(a,t){Ue(t,!0);const r=u(()=>t.admin.pending?.kind==="first-password");let i=B(""),d=B("");const p=8,y=u(()=>e(r)?e(d).trim().length>0&&e(i).length>=p:e(i).length>0);async function z(){!e(y)||t.admin.busy||(e(r)?await t.admin.answerRecovery(e(d),e(i)):await t.admin.answerPassword(e(i)),v(i,""),v(d,""))}var m=ts(),j=s(m),S=s(j),A=s(S),L=s(A);Dr(L,{name:"settings",size:"1.5rem"});var W=n(A,2),O=s(W),C=n(S,2),K=s(C),U=n(C,2);{var E=T=>{var w=Qn(),k=n(ae(w),2),R=n(s(k),2);Ia(R);var F=n(k,2),h=s(F);h.textContent="Nouveau mot de passe — 8 caractères au moins";var G=n(h,2);Ce("keydown",R,Y=>Y.key==="Enter"&&void z()),ia(R,()=>e(d),Y=>v(d,Y)),Ce("keydown",G,Y=>Y.key==="Enter"&&void z()),ia(G,()=>e(i),Y=>v(i,Y)),l(T,w)},x=T=>{var w=Zn(),k=ae(w),R=n(s(k),2);Ia(R),Ce("keydown",R,F=>F.key==="Enter"&&void z()),ia(R,()=>e(i),F=>v(i,F)),l(T,w)};_(U,T=>{e(r)?T(E):T(x,-1)})}var g=n(U,2);{var D=T=>{var w=es(),k=s(w);f(()=>o(k,t.admin.actionError)),l(T,w)};_(g,T=>{t.admin.actionError!==""&&T(D)})}var H=n(g,2),V=s(H);Te(V,{label:"Annuler",onrun:()=>t.admin.cancelPassword()});var $=n(V,2);{let T=u(()=>e(r)?"Poser ce mot de passe":"Continuer"),w=u(()=>!e(y)||t.admin.busy);Te($,{kind:"write",get label(){return e(T)},get disabled(){return e(w)},onrun:()=>{z()}})}f(()=>{o(O,e(r)?"Ce poste n’a pas encore de mot de passe":"Mot de passe d’administration"),o(K,t.admin.pending?.message??"")}),l(a,m),Me()}ot(["keydown"]);var rs=c(' '),ns=c(""),ss=c(''),ls=c(''),is=c(""),os=c(""),us=c('

                                                                        '),cs=c(" "),ds=c('

                                                                        '),vs=c('
                                                                        ');function at(a,t){Ue(t,!0);const r=Be(t,"hint",3,""),i=Be(t,"kind",3,"text"),d=Be(t,"disabled",3,!1),p=Be(t,"fault",3,""),y=Be(t,"allowed",19,()=>[]),z=Be(t,"choices",19,()=>[]),m=u(()=>`field-${t.path.replace(/\./gu,"-")}`);var j=vs();let S;var A=s(j),L=s(A),W=s(L),O=n(L,2);{var C=T=>{var w=rs(),k=s(w);f(()=>o(k,t.path)),l(T,w)};_(O,T=>{St.showTechnicalNames&&T(C)})}var K=n(A,2);{var U=T=>{var w=ss();ze(w,21,z,R=>R.value,(R,F)=>{var h=ns(),G=s(h),Y={};f(()=>{o(G,e(F).label),Y!==(Y=e(F).value)&&(h.value=(h.__value=e(F).value)??"")}),l(R,h)});var k;vr(w),f(()=>{be(w,"id",e(m)),w.disabled=d(),k!==(k=t.value)&&(w.value=(w.__value=t.value)??"",Pa(w,t.value))}),Ce("change",w,R=>t.onchange(R.currentTarget.value)),l(T,w)},E=T=>{var w=ls();f(()=>{be(w,"id",e(m)),be(w,"type",i()),Gt(w,t.value),w.disabled=d(),be(w,"list",y().length>0?e(m)+"-allowed":void 0)}),Ce("input",w,k=>t.onchange(k.currentTarget.value)),l(T,w)};_(K,T=>{z().length>0?T(U):T(E,-1)})}var x=n(K,2);{var g=T=>{var w=os();ze(w,20,y,k=>k,(k,R)=>{var F=is(),h={};f(()=>{h!==(h=R)&&(F.value=(F.__value=R)??"")}),l(k,F)}),f(()=>be(w,"id",e(m)+"-allowed")),l(T,w)};_(x,T=>{y().length>0&&T(g)})}var D=n(x,2);{var H=T=>{var w=us(),k=s(w);f(()=>o(k,r())),l(T,w)};_(D,T=>{r()!==""&&T(H)})}var V=n(D,2);{var $=T=>{var w=ds(),k=s(w),R=n(k);{var F=h=>{var G=cs(),Y=s(G);f(ne=>o(Y,`Valeurs acceptées : ${ne??""}.`),[()=>y().join(", ")]),l(h,G)};_(R,h=>{y().length>0&&h(F)})}f(()=>o(k,`${p()??""} `)),l(T,w)};_(V,T=>{p()!==""&&T($)})}f(()=>{S=Nt(j,1,"field svelte-1oilv5s",null,S,{refused:p()!==""}),be(A,"for",e(m)),o(W,t.label)}),l(a,j),Me()}ot(["change","input"]);var ps=c('

                                                                        '),fs=c('

                                                                        ');function Oe(a,t){const r=Be(t,"note",3,"");var i=fs(),d=s(i),p=s(d),y=n(d,2);{var z=j=>{var S=ps(),A=s(S);f(()=>o(A,r())),l(j,S)};_(y,j=>{r()!==""&&j(z)})}var m=n(y,2);Kr(m,()=>t.children),f(()=>o(p,t.title)),l(a,i)}function $e(a,t){return a.faults.find(r=>r.field===t)?.message??""}function Qt(a,t){return a.faults.find(r=>r.field===t)?.allowed??[]}var gs=c('

                                                                        Lecture des réglages du poste…

                                                                        '),ms=c(`

                                                                        Le poste y cherche le fichier , et le supprime une fois lu : c’est ce qui dit au producteur que la livraison est prise.

                                                                        `,1),hs=c(`

                                                                        Sur un serveur WebDAV, le dépôt d’un fichier CSV depuis cet écran n’est plus @@ -7,7 +7,7 @@ import{Y as Tr,Z as zr,_ as jr,$ as Ea,a0 as Vt,e as Rr,a1 as or,a2 as Or,a3 as chercher aucun catalogue.

                                                                        `),bs=c('
                                                                        ',1);function ws(a,t){Ue(t,!0);const r={local_drop:["catalog.options.directory"],webdav:["catalog.options.url","catalog.options.username","catalog.options.password"]},i=u(()=>t.draft.text("catalog.type"));function d(p){t.draft.set("catalog.type",p);for(const[y,z]of Object.entries(r))if(y!==p)for(const m of z)t.draft.unset(m)}Oe(a,{title:"Où le poste va chercher le catalogue",children:(p,y)=>{var z=bs(),m=ae(z),j=s(m),S=s(j),A=n(j,2),L=s(A),W=n(m,2);{var O=E=>{var x=gs();l(E,x)},C=E=>{var x=ms(),g=ae(x);{let $=u(()=>qt("catalog.options.directory")),T=u(()=>t.draft.text("catalog.options.directory")),w=u(()=>$e(t.draft,"catalog.options.directory"));at(g,{get label(){return e($)},path:"catalog.options.directory",get value(){return e(T)},hint:"Laissez vide pour le répertoire du poste, celui que le service crée lui-même. Un répertoire nommé ici doit exister : le poste ne le crée pas.",get fault(){return e(w)},onchange:k=>t.draft.set("catalog.options.directory",k)})}var D=n(g,2),H=n(s(D)),V=s(H);f(()=>o(V,`flv_${t.station??""}.csv`)),l(E,x)},K=E=>{var x=hs(),g=ae(x);{let V=u(()=>qt("catalog.options.url")),$=u(()=>t.draft.text("catalog.options.url")),T=u(()=>$e(t.draft,"catalog.options.url"));at(g,{get label(){return e(V)},path:"catalog.options.url",get value(){return e($)},get fault(){return e(T)},onchange:w=>t.draft.set("catalog.options.url",w)})}var D=n(g,2);{let V=u(()=>qt("catalog.options.username")),$=u(()=>t.draft.text("catalog.options.username")),T=u(()=>$e(t.draft,"catalog.options.username"));at(D,{get label(){return e(V)},path:"catalog.options.username",get value(){return e($)},get fault(){return e(T)},onchange:w=>t.draft.set("catalog.options.username",w)})}var H=n(D,2);{let V=u(()=>qt("catalog.options.password")),$=u(()=>$e(t.draft,"catalog.options.password"));at(H,{get label(){return e(V)},path:"catalog.options.password",kind:"password",value:"",hint:"Laissez vide : le mot de passe actuel est conservé.",get fault(){return e($)},onchange:T=>t.draft.set("catalog.options.password",T)})}l(E,x)},U=E=>{var x=_s();l(E,x)};_(W,E=>{t.draft.config===null?E(O):e(i)==="local_drop"?E(C,1):e(i)==="webdav"?E(K,2):E(U,-1)})}f(()=>{ea(S,e(i)==="local_drop"),ea(L,e(i)==="webdav")}),Ce("change",S,()=>d("local_drop")),Ce("change",L,()=>d("webdav")),l(p,z)},$$slots:{default:!0}}),Me()}ot(["change"]);const Va=1e3;function va(a){const t=Oa(a);return t===null?"":[Zt(t.getDate()),Zt(t.getMonth()+1),String(t.getFullYear())].join("/")}function Lt(a){const t=Oa(a);return t===null?"":`${va(a)} à ${Zt(t.getHours())}:${Zt(t.getMinutes())}`}function ys(a){const t=Oa(a);return t===null?"":[Zt(t.getHours()),Zt(t.getMinutes()),Zt(t.getSeconds())].join(":")}function Sa(a){const t=a/1e6;return t<1e3?`${Math.round(t)} Mo`:`${_r(t/1e3)} Go`}function se(a){const t=String(Math.abs(Math.trunc(a))),r=[];for(let i=t.length;i>0;i-=3)r.unshift(t.slice(Math.max(0,i-3),i));return(a<0?"-":"")+r.join(" ")}function ua(a){return a0}function qs(a){const t=a.motive!=="";return{canWithdraw:t||!pa(!1,a.waiverInForce),canOfferAgain:t||!pa(!0,a.waiverInForce),canSaveWaiver:ks(a.typedWaiver)&&(t||!pa(a.offeredInForce,a.typedWaiver)),canDropWaiver:t||!pa(a.offeredInForce,null)}}function br(a){const t=[];return a.offered||t.push("retiré de la grille"),a.min_weight_g!==null&&t.push(`peut peser à partir de ${se(a.min_weight_g)} g`),t.length===0?"aucune restriction":t.join(" · ")}function ca(a,t,r,i){const d=t>1?i:r;return a>=t?`${se(t)} ${d}.`:`${se(a)} lignes affichées sur ${se(t)} ${d}.`}function Ss(a,t){return t>a?`${se(a)} produits affichés sur ${se(t)} trouvés — précisez votre recherche.`:`${se(t)} ${t>1?"produits trouvés":"produit trouvé"}.`}function Ls(a){return`${se(a)} ${a>1?"imports affichés":"import affiché"} : le poste n’en publie jamais plus de vingt.`}function ya(a,t,r,i){const d=`${se(r)} ${r>1?t:a}`;return r<=i?d+".":`${d} — seules les ${se(i)} premières lignes sont affichées.`}function Es(a,t){const r=`${se(a)} ${a>1?"produits retirés":"produit retiré"}`;return t===""?r+".":`${r} depuis l’import du ${Lt(t)}.`}var Cs=c('

                                                                        Aucune décision locale : la grille est celle du fichier.

                                                                        '),Ps=c('
                                                                      • '),Ts=c('

                                                                          ',1);function zs(a,t){Ue(t,!0);const r=20,i=u(()=>t.decisions.slice(0,r));Oe(a,{title:"Décisions en vigueur",note:"Ce qu’un humain a décidé, produit par produit. C’est ici que se reprend un produit retiré de la grille.",children:(d,p)=>{var y=Tt(),z=ae(y);{var m=S=>{var A=Cs();l(S,A)},j=S=>{var A=Ts(),L=ae(A),W=s(L),O=n(L,2),C=s(O);ze(C,21,()=>e(i),K=>K.product_id,(K,U)=>{var E=Ps(),x=s(E),g=n(x,2),D=s(g),H=n(g,2),V=s(H),$=n(H,2),T=s($),w=n($,2),k=s(w),R=n(w,2),F=s(R);f((h,G,Y)=>{o(D,h),o(V,e(U).product_id),o(T,G),o(k,e(U).reason),o(F,Y)},[()=>t.nameOf(e(U).product_id),()=>br(e(U)),()=>va(e(U).decided_at)]),Ce("click",x,()=>t.onchoose(e(U).product_id)),l(K,E)}),f(K=>o(W,K),[()=>ca(e(i).length,t.decisions.length,"décision en vigueur","décisions en vigueur")]),l(S,A)};_(z,S=>{t.decisions.length===0?S(m):S(j,-1)})}l(d,y)},$$slots:{default:!0}}),Me()}ot(["click"]);function js(a){return a==="loading"?"Lecture des signalements du dernier import…":"Les signalements du dernier import n’ont pas pu être lus : cet écran ne sait pas ce qu’ils disent."}function Rs(a){return a==="loading"?"Lecture de l’historique des imports…":"L’historique des imports n’a pas pu être lu : cet écran ne sait pas ce qu’il contient."}function Aa(a,t,r){return a!==void 0?a:t==="loading"?"Lecture du nom…":t==="unread"?r:"Produit absent du catalogue en service"}var Ga=c('

                                                                          '),Os=c(' '),As=c('
                                                                        • '),Ns=c('

                                                                            ',1);function xa(a,t){Ue(t,!0);const r=Be(t,"remedy",3,""),i=50,d=u(()=>t.findings.slice(0,i)),p=u(()=>t.findings.length>e(d).length);Oe(a,{get title(){return t.title},get note(){return t.note},children:(y,z)=>{var m=Tt(),j=ae(m);{var S=W=>{var O=Ga(),C=s(O);f(K=>{be(O,"data-unread",t.list),o(C,K)},[()=>js(t.state)]),l(W,O)},A=W=>{var O=Ga(),C=s(O);f(()=>o(C,t.none)),l(W,O)},L=W=>{var O=Ns(),C=ae(O),K=s(C),U=n(K);{var E=D=>{var H=lt();f(()=>o(H,r())),l(D,H)};_(U,D=>{e(p)&&r()!==""&&D(E)})}var x=n(C,2),g=s(x);ze(g,21,()=>e(d),da,(D,H)=>{var V=As(),$=s(V),T=s($),w=n($,2);{var k=M=>{var b=Os(),N=s(b);f(()=>o(N,e(H).product_name)),l(M,b)};_(w,M=>{e(H).product_name!==""&&M(k)})}var R=n(w,2),F=s(R),h=n(R,2),G=s(h),Y=n(h,2),ne=s(Y);f(M=>{o(T,`ligne ${M??""}`),o(F,e(H).product_id),o(G,e(H).value),o(ne,e(H).message)},[()=>se(e(H).csv_line)]),l(D,V)}),f(D=>{be(C,"data-tally",t.list),o(K,`${D??""} `),be(x,"data-rows",t.list)},[()=>ca(e(d).length,t.findings.length,t.singular,t.plural)]),l(W,O)};_(j,W=>{t.state!=="read"?W(S):t.findings.length===0?W(A,1):W(L,-1)})}l(y,m)},$$slots:{default:!0}}),Me()}function wr(a,t){const r=a.rows_read_count,i=a.images_decoded_count,d=r-i,p=[{count:_t(a.weighable_count),label:"pesables",note:`(${_t(i)} avec photo, ${_t(d)} sans)`,link:""},{count:_t(a.not_weighable_count),label:"non pesables",note:Bs(t),link:""}];if(a.anomalies_count>0&&p.push({count:_t(a.anomalies_count),label:"anomalies",note:"à corriger dans Odoo",link:Ha(a.anomalies_count)}),a.unit_mismatches_count>0){const y=a.unit_mismatches_count;p.push({count:"+ "+_t(y),label:y===1?"unité divergente":"unités divergentes",note:"pesable, unité à corriger",link:Ha(y)})}return{headline:`Catalogue du ${va(a.occurred_at)} — ${_t(r)} produits reçus`,lines:p,oneLine:Is(a)}}function Is(a){const t=[`${_t(a.rows_read_count)} reçus`,`${_t(a.weighable_count)} pesables`,`${_t(a.not_weighable_count)} non pesables`,`${_t(a.anomalies_count)} anomalies`],r=a.unit_mismatches_count;return r>0&&t.push(`${_t(r)} ${r===1?"unité divergente":"unités divergentes"}`),t.join(" · ")}const Ds={applied:"appliqué",unchanged:"identique au précédent",rejected:"refusé",failed:"échec"},Fs={local_drop:"dépôt local",webdav:"WebDAV",manual:"déposé sur l’écran"};function ha(a){return Ds[a]??"résultat inconnu"}function yr(a){return Fs[a]??"source inconnue"}function Us(a,t){const r=[a.file_name===""?"Le dernier fichier":a.file_name,ha(a.result)],i=Lt(a.occurred_at);i!==""&&r.push("le "+i),r.push("via "+yr(a.source));const d=a.reason===""?"":" "+a.reason;return`${r.join(" ")} — ${wr(a,t).oneLine}.${d}`}function Ms(a){return a===""?"Aucun nouvel import enregistré à cet instant, et ce poste ne publie pas ce qu’il surveille.":"Aucun nouvel import enregistré à cet instant. Le poste surveille "+a+" : le fichier n’y était pas, ou il n’a pas encore fini d’arriver."}function Ws(a){return"Ce poste n’a pas de journal : il ne pourra rien dire de l’issue de cette relecture."+(a===""?"":" Le poste surveille "+a+".")}function Ha(a){return a===1?"voir la ligne":`voir les ${_t(a)} lignes`}function Bs(a){return a.map(t=>`${Vs(t)} (${_t(t.count)})`).join(", ")}function Vs(a){switch(a.code){case"PREPACKAGED_PRODUCT":return"préemballés";case"INTERNAL_CODE_NOT_WEIGHABLE":return a.value===""?"code interne":"code interne "+a.value;case"NO_BARCODE":return"sans code-barres";default:return a.code}}function _t(a){return String(a)}var Gs=c('

                                                                            '),Hs=c('

                                                                            Aucun import dans l’historique.

                                                                            '),Js=c(' '),Ks=c('

                                                                            QuandFichierSourceRésultatMotifLuesPesablesNon pesablesAnomaliesRetirés
                                                                            ',1);function $s(a,t){Ue(t,!0);function r(i){const d=ha(i.result);return i.code===""?d:`${d} (${i.code})`}Oe(a,{title:"Vingt derniers imports",children:(i,d)=>{var p=Tt(),y=ae(p);{var z=S=>{var A=Gs(),L=s(A);f(W=>o(L,W),[()=>Rs(t.state)]),l(S,A)},m=S=>{var A=Hs();l(S,A)},j=S=>{var A=Ks(),L=ae(A),W=s(L),O=n(L,2),C=s(O),K=n(s(C));ze(K,21,()=>t.imports,U=>U.id,(U,E)=>{var x=Js(),g=s(x),D=s(g),H=n(g),V=s(H),$=n(H),T=s($),w=n($),k=s(w),R=n(w),F=s(R),h=n(R),G=s(h),Y=n(h),ne=s(Y),M=n(Y),b=s(M),N=n(M),X=s(N),de=n(N),he=s(de);f((fe,oe,I,Q,re,ee,ve,ye)=>{o(D,fe),o(V,e(E).file_name),o(T,oe),o(k,I),o(F,e(E).reason),o(G,Q),o(ne,re),o(b,ee),o(X,ve),o(he,ye)},[()=>Lt(e(E).occurred_at),()=>yr(e(E).source),()=>r(e(E)),()=>se(e(E).rows_read_count),()=>se(e(E).weighable_count),()=>se(e(E).not_weighable_count),()=>se(e(E).anomalies_count),()=>se(e(E).products_withdrawn_count)]),l(U,x)}),f(U=>o(W,U),[()=>Ls(t.imports.length)]),l(S,A)};_(y,S=>{t.state!=="read"?S(z):t.imports.length===0?S(m,1):S(j,-1)})}l(i,p)},$$slots:{default:!0}}),Me()}var Ys=c(' '),Xs=c(''),Qs=c('
                                                                            '),Zs=c('

                                                                            ');function xr(a,t){Ue(t,!0);const r=u(()=>wr(t.record,t.motives));var i=Zs(),d=s(i),p=s(d),y=n(d,2);ze(y,21,()=>e(r).lines,j=>j.label,(j,S)=>{var A=Qs(),L=s(A),W=s(L),O=n(L,2),C=s(O),K=s(C),U=n(C,2);{var E=D=>{var H=Ys(),V=s(H);f(()=>o(V,e(S).note)),l(D,H)};_(U,D=>{e(S).note!==""&&D(E)})}var x=n(U,2);{var g=D=>{var H=Xs(),V=s(H);f(()=>o(V,e(S).link)),Ce("click",H,()=>t.onshowrows(e(S).label==="anomalies"?"anomalies":"units")),l(D,H)};_(x,D=>{e(S).link!==""&&t.onshowrows!==void 0&&D(g)})}f(()=>{be(A,"data-row",e(S).label),o(W,e(S).count),o(K,e(S).label)}),l(j,A)});var z=n(y,2),m=s(z);f(()=>{o(p,e(r).headline),o(m,e(r).oneLine)}),l(a,i),Me()}ot(["click"]);var el=c(" "),tl=c(' '),al=c('');function Na(a,t){Ue(t,!0);const r=Be(t,"hint",3,"");var i=al(),d=s(i),p=n(d,2),y=s(p),z=s(y),m=n(y,2);{var j=L=>{var W=el(),O=s(W);f(()=>o(O,t.path)),l(L,W)};_(m,L=>{St.showTechnicalNames&&L(j)})}var S=n(m,2);{var A=L=>{var W=tl(),O=s(W);f(()=>o(O,r())),l(L,W)};_(S,L=>{r()!==""&&L(A)})}f(L=>{be(i,"data-flag",t.path),be(i,"data-on",L),ea(d,t.on),o(z,t.label)},[()=>String(t.on)]),Ce("change",d,L=>t.onchange(L.currentTarget.checked)),l(a,i),Me()}ot(["change"]);function Ja(a){const t=`${se(a.columns)} ${a.columns>1?"colonnes":"colonne"}`,r=`${se(a.rows)} ${a.rows>1?"rangées":"rangée"}`;return`${t} × ${r}`}function rl(a){const t=a.layout,r=[];if(a.columns===Xt)return r.push(t===null?"Automatique : la grille suit la largeur de l’écran. Un écran plus large en montre davantage sans qu’on y revienne.":`Automatique : ${Ja(t)} sur cet écran. Un écran plus large en montrera davantage sans qu’on y revienne.`),r;if(t===null)return r.push(`${se(a.columns)} ${a.columns>1?"colonnes":"colonne"} sur tous les écrans. Cet écran ne sait pas dire combien de rangées cela fait ici.`),r;const i=t.columns*t.rows;if(r.push(`${Ja(t)} — ${se(i)} ${i>1?"tuiles":"tuile"} d’un coup, sur cet écran (${String(a.viewport.width)} × ${String(a.viewport.height)}).`),a.tileCount>0){const p=Math.ceil(a.tileCount/i);r.push(a.tileCount>1?`Les ${se(a.tileCount)} tuiles de la grille tiennent en ${se(p)} ${p>1?"écrans":"écran"}.`:"La seule tuile de la grille tient en un écran.")}const d=a.floor;if(d!==null&&d.names>0){const p=d.names>1;r.push(`${se(d.names)} ${p?"noms":"nom"} sur ${se(a.tileCount)} ${p?"atteignent":"atteint"} le plancher de ${se(ka)} px : `+(d.rows>1?`leurs ${se(d.rows)} rangées peuvent être plus hautes que les autres.`:"leur rangée peut être plus haute que les autres."))}return r}function nl(a,t,r){if(a==="loading")return"Lecture du catalogue en service…";if(a==="unread")return"Le catalogue en service n’a pas pu être lu : cet écran ne sait pas combien de produits se vendent à l’unité.";if(t===0)return"Aucun produit vendu à l’unité dans le catalogue en service.";const i=t>1,d=i?"produits vendus à l’unité sont":"produit vendu à l’unité est",p=r?`${i?"montrés":"montré"} dans la grille de ce poste`:`${i?"masqués":"masqué"} sur ce poste`;return`${se(t)} ${d} ${p}.`}function sl(a){return["localhost","127.0.0.1"].includes(a)?"":"Cet écran n’est pas celui du poste : ce compte vaut pour l’écran que vous lisez."}const ll=-1,il=10;class kr{#t=B("");get sentence(){return e(this.#t)}set sentence(t){v(this.#t,t,!0)}#r=0;#a="";#e=0;begin(t){this.sentence="",this.#r=t.last_import_id,this.#a=t.watched,this.#e=il}forget(){this.sentence="",this.#e=0}observe(t){if(this.#e===0)return;if(t.counters.journal_rows_count===ll){this.#n(Ws(this.#a));return}const r=t.catalog;if(r!==null&&r.id!==this.#r){this.#n(Us(r,t.catalog_motives));return}this.#e-=1,this.#e===0&&(this.sentence=Ms(this.#a))}#n(t){this.sentence=t,this.#e=0}}var ol=c('

                                                                            Aucun import enregistré sur ce poste.

                                                                            '),ul=c('

                                                                            '),cl=c(`

                                                                            « Oublier la quarantaine » fait relire un fichier que le poste avait écarté : c’est le seul geste de cette page qui puisse remettre en service un catalogue refusé.

                                                                            `,1),Ka=c(""),dl=c(''),vl=c('

                                                                            '),pl=c('

                                                                            '),fl=c('
                                                                            '),gl=c(`

                                                                            Un produit masqué reste vendable : la caisse lit toujours son code-barres, et une étiquette déjà imprimée reste valable. Ce réglage ne fait que retirer sa tuile.

                                                                            Colonnes de la grille

                                                                            Se tromper ne coûte rien d’autre que de revenir ici : le réglage ne change ni le - fichier reçu, ni les étiquettes déjà imprimées.

                                                                            En dessous de ce nombre, la catégorie n’a pas de puce dans la barre du bas. Ses + fichier reçu, ni les étiquettes déjà imprimées.

                                                                            En dessous de ce nombre, la catégorie n’a pas de puce dans la barre du bas. Ses produits restent dans « Tout » et à la recherche : ce réglage ne retire aucune tuile. Pour ne plus montrer une catégorie du tout, c’est sa case « visible » qu’il faut décocher.

                                                                            `,1),ml=c('

                                                                            '),hl=c(`

                                                                            Déposez ici le fichier clé

                                                                            Ce dépôt remplace toute la grille par le fichier apporté : il change ce que le poste diff --git a/internal/web/dist/index.html b/internal/web/dist/index.html index 38ff0c1..1f104c9 100644 --- a/internal/web/dist/index.html +++ b/internal/web/dist/index.html @@ -6,7 +6,7 @@ Pesée - + From fe83c4c252673b6ad023c6387177c087993ae4b9 Mon Sep 17 00:00:00 2001 From: "Fabien F." Date: Tue, 4 Aug 2026 14:04:12 +0200 Subject: [PATCH 27/27] =?UTF-8?q?style(domain):=20r=C3=A9aligner=20la=20ta?= =?UTF-8?q?ble=20de=20l'empreinte,=20que=20deux=20cl=C3=A9s=20longues=20on?= =?UTF-8?q?t=20d=C3=A9salign=C3=A9e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `golangci-lint` refuse le fichier sur gofmt. Les deux entrées ajoutées à TestFingerprintChangesWhenASharedValueChanges — le libellé d'une catégorie et le seuil de puce — portent des clés plus longues que les précédentes, et gofmt réaligne alors tout le littéral. La faute est une lacune de vérification et pas une faute d'écriture : la vérification locale a lancé `make vet`, quand la CI lance `make lint`, qui ajoute golangci-lint et donc gofmt. `make vet` ne voit pas ce format. --- internal/domain/fingerprint_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/domain/fingerprint_test.go b/internal/domain/fingerprint_test.go index 90ba275..af6ff07 100644 --- a/internal/domain/fingerprint_test.go +++ b/internal/domain/fingerprint_test.go @@ -59,16 +59,16 @@ func TestFingerprintIgnoresWhatDiffersFromStationToStation(t *testing.T) { func TestFingerprintChangesWhenASharedValueChanges(t *testing.T) { reference := loadDelivered(t) for name, mutate := range map[string]func(*Config){ - "une remise de tarif": func(c *Config) { c.Pricing.Tiers[0].Discount = 200 }, - "un seuil de panier": func(c *Config) { c.Limits.BasketMin = -300 }, - "le gabarit": func(c *Config) { c.Printer.Template = "weighing_neutral_single" }, - "une catégorie": func(c *Config) { c.Catalog.Categories[0].Visible = false }, + "une remise de tarif": func(c *Config) { c.Pricing.Tiers[0].Discount = 200 }, + "un seuil de panier": func(c *Config) { c.Limits.BasketMin = -300 }, + "le gabarit": func(c *Config) { c.Printer.Template = "weighing_neutral_single" }, + "une catégorie": func(c *Config) { c.Catalog.Categories[0].Visible = false }, // The WORDING and not only the visibility: internal/web keys its serialized // catalog on this digest, and the labels of the grid are read from the // configuration (§10.2 bis). A renamed shelf that left the eight characters // alone would be served from the cache with its old name until the next import. "le libellé d'une catégorie": func(c *Config) { c.Catalog.Categories[0].Label = "Fruits d'ici" }, - "la rétention du journal": func(c *Config) { c.Journal.MaxDays = 30 }, + "la rétention du journal": func(c *Config) { c.Journal.MaxDays = 30 }, // Two stations that disagree here do not show the same grid: one offers fifteen // tiles the other does not have, and the eight characters have to say so. "les produits à l'unité montrés": func(c *Config) { c.UI.ShowByUnitProducts = true },