diff --git a/docs/02-architecture.md b/docs/02-architecture.md index 6d8d354..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é) @@ -2658,6 +2658,22 @@ Surcharges : `--config`, `--data`, `OPENSCALE_CONFIG`, `OPENSCALE_DATA`. **Aucun // the screen for the ONE value written. // Control 49 refuses everything else, and // says what zero means while refusing. + "min_products_for_chip": 5, // HOW MANY weighable tiles a category + // needs before the grid gives it a filter + // CHIP. Default 5, floor 1, NO CEILING, + // applied to EACH category separately, on + // its own count -- there is no setting on + // the NUMBER of categories. Under the + // threshold a category loses its CHIP and + // never its tiles: they stay in « Tout » + // and stay searchable; categories[].visible + // is the only setting that removes products + // from a screen (ADR-059, which amends + // ADR-024 without reversing it). A file + // written before this key, the shipped one + // included, decodes to the default, exactly + // as it already does for grid_columns. + // Control 50 refuses only the floor. // tile_size REMOVED: la densité s'adapte en continu à l'écran (clamp CSS, // ADR-035). Le contrôle 20 REFUSE désormais cette clé — un exemple qui la // porterait encore ferait recopier une configuration que le poste rejette. @@ -2842,7 +2858,7 @@ Surcharges : `--config`, `--data`, `OPENSCALE_CONFIG`, `OPENSCALE_DATA`. **Aucun > > **Le défaut `false` s'applique en silence aux fichiers existants.** La lecture du document n'applique aucun défaut : un fichier écrit avant cette clé se relit comme « masqués ». C'est ce qui est demandé, mais un poste mis à jour perd quinze tuiles sans un message. D'où deux exigences tenues : les fichiers livrés **écrivent la clé** plutôt que de compter sur le zéro du langage — `config-lacagette.json` à `false`, `config-demo.json` à `true`, `NeutralProfile` à `false` —, et la page **Catalogue** annonce combien de produits sont concernés sur le catalogue en service (§14.4). La clé traverse `Export(false)`, donc l'**empreinte de configuration** que les postes d'un parc comparent à l'œil : ajouter cette clé change les huit caractères de tous les postes, même à valeur identique (§11.5, ADR-041). -### 11.3 Validation — 49 numéros, 47 contrôles, tous remontés d'un coup +### 11.3 Validation — un numéro n'est jamais réemployé, tous les contrôles remontés d'un coup ```go // Fault is a single configuration error, named by the field that carries it. @@ -2864,9 +2880,9 @@ func (c *Config) Validate(reg Registries) []Fault > **Aucune clé d'option n'est nommée dans cette fonction** (ADR-044). Le noyau ne connaît ni `scale.options.port` ni `printer.options.copies` : il applique le **schéma que le driver choisi déclare**, et c'est tout. Deux contrôles l'ont appris à leurs dépens, et leurs numéros portent la cicatrice — voir plus bas. -1–2 `station.number ∈ [1,99]`, `network.listen` parseable · 3–5 types de balance/imprimante/source connus, **liste des valeurs disponibles dans `Fault.Values`** — pour la balance, **exactement les deux protocoles du registre** (§9.3) ; pour l'imprimante, **exactement les descripteurs enregistrés**, c'est-à-dire `raster` (défaut) et `preview`, `sbpl` restant un encodeur et non un driver (§1.1-3, §8.1, §8.2) · 6–9 options de driver validées par leur schéma · 10–13 au moins un tarif, **le tarif désigné par `reference_code` ne porte pas de remise** (c'est le prix du catalogue, pas un réglage), codes uniques, **`discount_percent ∈ [0 %, 100 %]`** au dixième de point — *le dénominateur constant d'ADR-034 a supprimé la panne que cette ligne retenait : un `coef_den` non positif atteignait `RoundingPolicy.Divide` et tuait la goroutine du Hub* · 14–16 `primary_code`, `reference_code`, chaque `secondary_codes` appartiennent à la grille · 17–18 **le plan de numérotation interne s'auto-contrôle au démarrage** (§6.2, ADR-028) : chaque préfixe déclaré fait exactement 4 chiffres, et `4 + RefWidth + PayloadWidth + 1 = 13` — un plan incohérent **arrête le processus au démarrage**, jamais à l'impression · 19 aucun préfixe déclaré deux fois dans le plan · 20 **une configuration qui porte encore `weight_decimals`, `units_field_width`, `weight_prefix`, `unit_prefix`, `content`, `rules_by_prefix`, `coef_num`, `coef_den` ou `ui.tile_size` est refusée**, avec un message qui renvoie au plan compilé pour les six premières, à `discount_percent` (ADR-034) pour les deux suivantes et à `ui.grid_columns` (ADR-057) pour la dernière : après mise à jour, un poste ne doit surtout pas croire que son ancien réglage de largeur, son ancien coefficient ou son ancien palier de densité s'applique encore. **`domain.Migrate` (§11.6, ADR-058) s'exécute avant ce contrôle sur tout ce qu'un poste LIT** — démarrage, `doctor`, `config validate` — de sorte qu'au moins une partie de ces neuf clés en disparaît avant d'y arriver, quand la conversion est possible : `ui.tile_size` toujours, `coef_num`/`coef_den` quand la fraction est exacte. Ce que ce contrôle voit encore est donc, sur ce chemin, ce que la migration n'a pas su trancher — ou ce qu'un document tapé directement dans `PUT /admin/api/config` porte, chemin que la migration ne touche pas · 21 **`template.media.dots_per_mm` unique source de résolution** (mineur-3) · 22 `basket_min_g ≤ basket_max_g ≤ 0` · 23 `min_weight_g < max_weight_g ≤ 99999` · 24 `min_units ≤ max_units ≤ 99` · 25 `max_amount_cents ≤ 99999` · 26 `timeout_ms > min_duration_ms` · 27 `expiry_floor_ms ≥ 1000` et `< expiry_ceiling_ms` · 28 `stability.mode` et `on_timeout` dans la liste · 29 **`template` existe ET `Template.ValidateOn()` passe SUR LA TÊTE QUE LE DRIVER DÉCLARE** (les 9 règles et l'accord de pas, §7.5, ADR-045) — géométrie recomposée avec les décalages de l'exploitant · 30 `journal.max_rows ≥ 100` · 31 `admin.password_hash` non vide et bien formé · 32 `catalog.fallback_category ∈ categories` · 33 codes de catégorie uniques · 34 `min_readable_ratio ∈ [0,1]` · 35 couleurs `#RRGGBB` · 36 `poll_interval_s ≥ 1` · **37 — numéro laissé en trou** : `copies` n'est plus un contrôle en propre, sa borne est déclarée par le driver qui possède la clé et appliquée par le contrôle 7 (ADR-044) · 38 **`offset_x/y` recomposé avec la géométrie du gabarit ET la marge que CETTE tête laisse** (mineur-2, ADR-045) · 39 **pas d'hôte HTTP(S) derrière un chemin de dépôt** (important-11) · 40 `max_weighable_drop ∈ [0, 0.5]` · 41 `roll_capacity ≥ 50` · 42 **transport série interdit pour l'imprimante** (16 ko/étiquette, §8.3) · 43 **tout prix porté par un fichier de configuration livré (`config-lacagette.json`, produits de démonstration, `flv_demo.csv`) vérifie `0 ≤ prix ≤ 999 999` centimes** — 3ᵉ et dernière imposition de `MaxUnitPrice` (§6.1), avec le DDL (§12.3) et la règle de prix de §10.3. *Depuis §11.5 c'est un contrôle de configuration **ordinaire**, appliqué à un fichier comme tous les autres ; il validait auparavant des valeurs compilées, c'est-à-dire du code source* · 44 `catalog.images.source` dans la liste, et `path` lisible **depuis le contexte du service** quand la source est `image_directory` · 45 `max_image_size_kb ∈ [16, 4096]` **et** `max_image_size_kb × 1024 ≤ max_file_size_mb × 1 048 576` — une image ne peut pas être autorisée à dépasser le fichier qui la contient (§10.7) · 46 **sur `local_drop`, un `catalog.options.directory` non vide est *inscriptible* depuis le contexte du service** — le fichier témoin y est créé **puis supprimé**, parce que l'acquittement d'un import *est* une suppression (ADR-004, ADR-038) —, et ce n'est pas le répertoire d'archives du poste, qui relirait ses propres copies en boucle ; vide, c'est le répertoire du poste et il n'y a rien à sonder · **47 — numéro laissé en trou**, comme le 37 : il refusait `directory` sur `webdav`, ce que le contrôle 9 refuse déjà pour **toute** source, présente ou à venir — une clé que la source choisie ne déclare pas ; sa phrase a déménagé dans le 9, qui **nomme** désormais la source qui porte la clé (ADR-052) · 48 **`update.repository` est un couple `propriétaire/projet`, jamais une URL** (ADR-040) : c'est le seul champ du fichier qui désigne d'où viendra du code privilégié, et l'hôte est compilé dans le binaire · 49 **`ui.grid_columns` vaut `0` — automatique — ou un nombre de colonnes de 3 à 12** (ADR-057), et le refus porte **l'intervalle *et* le sens de `0`** dans `Fault.Values` : les deux valeurs sont de natures différentes et une seule est un nombre de colonnes. Quelqu'un qui écrit `1` demande une grille plus dense ; un refus qui ne nommerait que l'intervalle lui ferait lire « 1 est hors de [3, 12] » sans jamais lui apprendre que la grille qu'il avait s'écrit `0` — ce qui, dans un fichier, ressemble exactement à « aucune colonne ». +1–2 `station.number ∈ [1,99]`, `network.listen` parseable · 3–5 types de balance/imprimante/source connus, **liste des valeurs disponibles dans `Fault.Values`** — pour la balance, **exactement les deux protocoles du registre** (§9.3) ; pour l'imprimante, **exactement les descripteurs enregistrés**, c'est-à-dire `raster` (défaut) et `preview`, `sbpl` restant un encodeur et non un driver (§1.1-3, §8.1, §8.2) · 6–9 options de driver validées par leur schéma · 10–13 au moins un tarif, **le tarif désigné par `reference_code` ne porte pas de remise** (c'est le prix du catalogue, pas un réglage), codes uniques, **`discount_percent ∈ [0 %, 100 %]`** au dixième de point — *le dénominateur constant d'ADR-034 a supprimé la panne que cette ligne retenait : un `coef_den` non positif atteignait `RoundingPolicy.Divide` et tuait la goroutine du Hub* · 14–16 `primary_code`, `reference_code`, chaque `secondary_codes` appartiennent à la grille · 17–18 **le plan de numérotation interne s'auto-contrôle au démarrage** (§6.2, ADR-028) : chaque préfixe déclaré fait exactement 4 chiffres, et `4 + RefWidth + PayloadWidth + 1 = 13` — un plan incohérent **arrête le processus au démarrage**, jamais à l'impression · 19 aucun préfixe déclaré deux fois dans le plan · 20 **une configuration qui porte encore `weight_decimals`, `units_field_width`, `weight_prefix`, `unit_prefix`, `content`, `rules_by_prefix`, `coef_num`, `coef_den` ou `ui.tile_size` est refusée**, avec un message qui renvoie au plan compilé pour les six premières, à `discount_percent` (ADR-034) pour les deux suivantes et à `ui.grid_columns` (ADR-057) pour la dernière : après mise à jour, un poste ne doit surtout pas croire que son ancien réglage de largeur, son ancien coefficient ou son ancien palier de densité s'applique encore. **`domain.Migrate` (§11.6, ADR-058) s'exécute avant ce contrôle sur tout ce qu'un poste LIT** — démarrage, `doctor`, `config validate` — de sorte qu'au moins une partie de ces neuf clés en disparaît avant d'y arriver, quand la conversion est possible : `ui.tile_size` toujours, `coef_num`/`coef_den` quand la fraction est exacte. Ce que ce contrôle voit encore est donc, sur ce chemin, ce que la migration n'a pas su trancher — ou ce qu'un document tapé directement dans `PUT /admin/api/config` porte, chemin que la migration ne touche pas · 21 **`template.media.dots_per_mm` unique source de résolution** (mineur-3) · 22 `basket_min_g ≤ basket_max_g ≤ 0` · 23 `min_weight_g < max_weight_g ≤ 99999` · 24 `min_units ≤ max_units ≤ 99` · 25 `max_amount_cents ≤ 99999` · 26 `timeout_ms > min_duration_ms` · 27 `expiry_floor_ms ≥ 1000` et `< expiry_ceiling_ms` · 28 `stability.mode` et `on_timeout` dans la liste · 29 **`template` existe ET `Template.ValidateOn()` passe SUR LA TÊTE QUE LE DRIVER DÉCLARE** (les 9 règles et l'accord de pas, §7.5, ADR-045) — géométrie recomposée avec les décalages de l'exploitant · 30 `journal.max_rows ≥ 100` · 31 `admin.password_hash` non vide et bien formé · 32 `catalog.fallback_category ∈ categories` · 33 codes de catégorie uniques · 34 `min_readable_ratio ∈ [0,1]` · 35 couleurs `#RRGGBB` · 36 `poll_interval_s ≥ 1` · **37 — numéro laissé en trou** : `copies` n'est plus un contrôle en propre, sa borne est déclarée par le driver qui possède la clé et appliquée par le contrôle 7 (ADR-044) · 38 **`offset_x/y` recomposé avec la géométrie du gabarit ET la marge que CETTE tête laisse** (mineur-2, ADR-045) · 39 **pas d'hôte HTTP(S) derrière un chemin de dépôt** (important-11) · 40 `max_weighable_drop ∈ [0, 0.5]` · 41 `roll_capacity ≥ 50` · 42 **transport série interdit pour l'imprimante** (16 ko/étiquette, §8.3) · 43 **tout prix porté par un fichier de configuration livré (`config-lacagette.json`, produits de démonstration, `flv_demo.csv`) vérifie `0 ≤ prix ≤ 999 999` centimes** — 3ᵉ et dernière imposition de `MaxUnitPrice` (§6.1), avec le DDL (§12.3) et la règle de prix de §10.3. *Depuis §11.5 c'est un contrôle de configuration **ordinaire**, appliqué à un fichier comme tous les autres ; il validait auparavant des valeurs compilées, c'est-à-dire du code source* · 44 `catalog.images.source` dans la liste, et `path` lisible **depuis le contexte du service** quand la source est `image_directory` · 45 `max_image_size_kb ∈ [16, 4096]` **et** `max_image_size_kb × 1024 ≤ max_file_size_mb × 1 048 576` — une image ne peut pas être autorisée à dépasser le fichier qui la contient (§10.7) · 46 **sur `local_drop`, un `catalog.options.directory` non vide est *inscriptible* depuis le contexte du service** — le fichier témoin y est créé **puis supprimé**, parce que l'acquittement d'un import *est* une suppression (ADR-004, ADR-038) —, et ce n'est pas le répertoire d'archives du poste, qui relirait ses propres copies en boucle ; vide, c'est le répertoire du poste et il n'y a rien à sonder · **47 — numéro laissé en trou**, comme le 37 : il refusait `directory` sur `webdav`, ce que le contrôle 9 refuse déjà pour **toute** source, présente ou à venir — une clé que la source choisie ne déclare pas ; sa phrase a déménagé dans le 9, qui **nomme** désormais la source qui porte la clé (ADR-052) · 48 **`update.repository` est un couple `propriétaire/projet`, jamais une URL** (ADR-040) : c'est le seul champ du fichier qui désigne d'où viendra du code privilégié, et l'hôte est compilé dans le binaire · 49 **`ui.grid_columns` vaut `0` — automatique — ou un nombre de colonnes de 3 à 12** (ADR-057), et le refus porte **l'intervalle *et* le sens de `0`** dans `Fault.Values` : les deux valeurs sont de natures différentes et une seule est un nombre de colonnes. Quelqu'un qui écrit `1` demande une grille plus dense ; un refus qui ne nommerait que l'intervalle lui ferait lire « 1 est hors de [3, 12] » sans jamais lui apprendre que la grille qu'il avait s'écrit `0` — ce qui, dans un fichier, ressemble exactement à « aucune colonne » · **50** `ui.min_products_for_chip` vaut au moins **1**, **sans plafond** (ADR-059) : sous 1, une catégorie qui n'a pas la moindre tuile obtiendrait quand même sa puce, ce qui n'a pas de lecture légitime — c'est le plancher que ce contrôle refuse. Un plafond protégerait contre un seuil supérieur au plus gros rayon d'une coopérative, qui laisse la barre avec « Tout » seul ; ce cas n'a pas de refus, parce qu'il se répare en revenant sur le champ, et qu'aucun couple de bornes n'est vrai de tous les catalogues (§14.3-2). -> **Un numéro est en trou, un autre a maigri, et c'est le même mouvement** (ADR-044). Le **37** portait `copies ∈ [1,10]`. Il nommait une clé d'un driver que le noyau ne voit pas, et il était l'**une des trois bornes** posées sur un même chiffre : cette règle et le schéma d'options disaient `[1, 10]`, pendant que `raster.Settings.Validate` acceptait les six chiffres du champ ``. Ce n'était pas une incohérence mais **deux notions** — ce qu'un *travail d'impression* demande, et ce qu'une *configuration* autorise —, et le désaccord ne se voyait qu'en lisant les trois. Le **3**, lui, exigeait littéralement la clé `scale.options.port` de tout poste dont `scale.present` était levé, quel que soit son `scale.type` : un driver atteint par une **adresse** — TCP, USB — était refusé avant d'être interrogé, sur une clé que son propre schéma ne porte pas, et en ajouter un aurait demandé d'éditer `Config.Validate`. Le 3 subsiste, réduit à ce qu'il sait faire — *ce protocole existe-t-il dans ce binaire ?* — et le 37 **n'est pas renuméroté** : §11.3 désigne ses contrôles par leur numéro, ici, dans les tests et dans le code, et renuméroter ferait mentir toutes les citations d'un coup. **Un troisième numéro les a rejoints depuis** : le **47**, retiré par ADR-052 pour la même raison que le 37 — il refusait sur `webdav` une clé que le contrôle 9 refuse déjà pour toute source qui ne la déclare pas. **Il y a donc 49 numéros pour 47 contrôles appliqués**, le 49ᵉ étant celui qu'ADR-057 ajoute. *(Ce compte était faux ici jusqu'au 01/08/2026 : il n'avait pas suivi la suppression du 47.)* +> **Un numéro est en trou, un autre a maigri, et c'est le même mouvement** (ADR-044). Le **37** portait `copies ∈ [1,10]`. Il nommait une clé d'un driver que le noyau ne voit pas, et il était l'**une des trois bornes** posées sur un même chiffre : cette règle et le schéma d'options disaient `[1, 10]`, pendant que `raster.Settings.Validate` acceptait les six chiffres du champ ``. Ce n'était pas une incohérence mais **deux notions** — ce qu'un *travail d'impression* demande, et ce qu'une *configuration* autorise —, et le désaccord ne se voyait qu'en lisant les trois. Le **3**, lui, exigeait littéralement la clé `scale.options.port` de tout poste dont `scale.present` était levé, quel que soit son `scale.type` : un driver atteint par une **adresse** — TCP, USB — était refusé avant d'être interrogé, sur une clé que son propre schéma ne porte pas, et en ajouter un aurait demandé d'éditer `Config.Validate`. Le 3 subsiste, réduit à ce qu'il sait faire — *ce protocole existe-t-il dans ce binaire ?* — et le 37 **n'est pas renuméroté** : §11.3 désigne ses contrôles par leur numéro, ici, dans les tests et dans le code, et renuméroter ferait mentir toutes les citations d'un coup. **Un troisième numéro les a rejoints depuis** : le **47**, retiré par ADR-052 pour la même raison que le 37 — il refusait sur `webdav` une clé que le contrôle 9 refuse déjà pour toute source qui ne la déclare pas. **Le numéro d'un contrôle retiré n'est donc jamais repris, et jamais donné à un contrôle nouveau** : la liste énumérée ci-dessus est ce qui fait foi sur ce qu'un poste vérifie réellement, un contrôle à la fois — pas un total recopié à cet endroit, qui a déjà menti pour avoir oublié une suppression et redirait la même chose au premier ajout suivant. > > **Ce qui est abandonné est nommé.** Sur un registre **vide** — `openscale config validate` lancé sur un portable —, le contrôle par schéma ne s'exécute pas du tout : la borne de `copies` n'est alors pas appliquée à la validation. Elle l'est là où elle décide de quelque chose, à la construction du driver. Une borne qu'un paquet d'imprimante est seul à pouvoir énoncer vaut mieux qu'une borne que le noyau répète. @@ -4119,13 +4135,13 @@ Contrastes **AAA** (≥ 7:1) sur tout texte ≥ 24 px, **AA** partout ailleurs 1. **Une seule grille, une densité continue par défaut et surchargeable en colonnes.** `repeat(auto-fill, minmax(var(--tile-min), 1fr))` où `--tile-min` vaut `clamp(15rem, 19vw, 22rem)` (ADR-035), tuile ≥ 72 px de haut (§14.2). Mesuré dans le navigateur sur le catalogue réel : **5 colonnes de 371 px sur 1920 × 1080**, 5 de 261 px à 1366, 7 de 354 px à 2560, **10 de 374 px sur un 4K**. Sur l'écran de référence, en double tarif et prix affichés — les réglages du poste installé —, cela fait **2 rangées, soit 10 tuiles d'un coup et 34 écrans** de défilement pour les 331 pesables — et d'autant moins qu'un filtre est actif (la plus grosse catégorie, `A`, en compte **126 pesables** pour 140 lignes reçues). *(« Une dizaine d'écrans », écrit ici jusqu'au 01/08/2026, comptait des rangées et non des écrans : la campagne au navigateur en relève 34.)* **Les trois conditions comptent autant que le nombre de colonnes** : masquer les prix ou retirer le second palier de tarif ajoute une rangée entière à densité égale, et c'est pourquoi aucun tableau de cette page ne fait référence — l'aperçu de §14.4 mesure une vraie tuile dans les réglages réels du poste (ADR-057). **Et c'est ici que le plafond de 120 produits par catégorie de l'ancienne application cesse d'être une anecdote de conception : il est franchi aujourd'hui, en production.** Chaque écran de catégorie est une copie de `FormulaireSquelette`, qui porte exactement **120 contrôles `Image0…Image119`** (§14.2) ; la boucle de remplissage (`FormulaireCalcul.cls` l. 552-664) parcourt **tout** le jeu d'enregistrements — `… WHERE Categorie.Intitule = "Autres" AND Visible=True ORDER BY NomProduit` — et cherche pour chacun le contrôle `"Image" & i` dans un `Select Case` **sans branche par défaut** : passé `i = 119`, aucun `Case` ne correspond, rien n'est écrit, rien n'est journalisé, et la boucle continue jusqu'à `EOF`. **Sur `flv.csv`, 126 produits « Autres » franchissent le contrôle d'intégrité de l'ancienne application pour 120 emplacements : les six derniers de l'ordre alphabétique ne s'affichent sur aucune balance aujourd'hui, sans un message ni une ligne de journal.** L'écart monterait à **20** si `ProduitIndisponibleSurErreur` repassait à `"N"` — le masquage des 14 codes à zone de réservation occupée de cette catégorie (§10.3) dissimule une partie du dépassement. Le défaut est **daté** : il est né le jour où « Autres » a franchi 120 produits, quelque part entre le catalogue de 2022 (`A = 1`) et celui de 2026 (`A = 140`). **Cette architecture n'a aucune limite de ce genre** — la grille est une liste, pas un gabarit d'emplacements ; aucun nombre de tuiles n'apparaît en configuration (§11.2), et `GET /api/v1/catalog` sert le catalogue **entier** (§14.5). C'est le bénéfice le plus directement chiffrable de la réécriture — **six produits vendables qui redeviennent visibles**, sans une ligne de code écrite pour eux — et un écart à annoncer à la mise en service (§18, L9). La densité reste **continue par défaut** : elle suit l'écran, `clamp()` bornant ce que `vw` produirait d'illisible aux deux extrêmes (ADR-035, qui remplace les trois valeurs d'ADR-031). Les deux bornes restent les mêmes contraintes physiques qu'avant — cible ≥ 72 px, nom de **69 caractères** lisible à 60–80 cm —, et `auto-fill` absorbe le reste. **Elle devient surchargeable en nombre de colonnes** (`ui.grid_columns`, ADR-057) : à `0`, qui est le défaut et ce que fait un poste dont personne n'a rien réglé, la déclaration de grille est celle ci-dessus **mot pour mot** ; de `3` à `12`, c'est `repeat(N, minmax(0, 1fr))` et **ce nombre de colonnes sur n'importe quel écran**, le reste de la tuile suivant par un facteur déduit de la largeur de colonne obtenue. **Les deux contraintes physiques ne bougent pas pour autant : elles cessent d'être un interdit et deviennent ce que l'écran d'administration ANNONCE** (§14.4) — combien de tuiles la grille montrera d'un coup, en combien d'écrans, et combien de noms descendent au plancher —, avant l'enregistrement et non après. C'est la différence de fond avec ADR-031 : ce n'est plus l'exploitant qui choisit une taille à l'aveugle, c'est la mise en page qui lui répond sur son propre écran. *(Le réglage `confort`/`dense` de la première version était la bascule « grandes/petites vignettes » de l'existant, adossée aux tranches 0-24 / 25-47 / … / 100-120 de `Systeme_Dimensions` — que CSS grid rend caduques. Les trois valeurs mesurées d'ADR-031 n'étaient pas ce réglage-là non plus, mais elles demandaient encore à un exploitant de choisir : `clamp()` répond à sa place.)* -2. **Les catégories sont des filtres, pas quatre écrans.** L'ancienne application posait quatre boutons en dur — `BoutonFruits`, `BoutonLegumes`, `BoutonVrac`, `BoutonAutres` — parce que quatre formulaires étaient préconstruits au démarrage, et ouvrait `FormulaireLegumes` en dur au lancement. La répartition réelle interdit cette parité, **et elle 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**. En 2022, « Autres » menait à **un seul produit** — un quart de barre de navigation pour une tuile ; en 2026, c'est la catégorie la plus peuplée. Aucune barre en dur ne survit à ça. Donc : **la vue au repos est « Tout »**, 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), l'effectif **pesable** est écrit sur la puce — jamais le nombre de lignes reçues, qui compte des préemballés sans tuile —, l'ordre et la couleur viennent de la configuration **et non d'un classement figé dans le code** (c'est précisément ce que l'inversion 2022 → 2026 rend nécessaire), et une catégorie peut être masquée sur **ce** poste (`categories[].visible` — le poste « fruits » n'a pas à montrer le vrac : c'est une vraie décision de magasin). Il n'y a **plus de catégorie ouverte par défaut** à configurer : « Tout » est toujours juste, et c'est un toucher de moins dans tous les cas. *(La « sous-catégorie en puces dès qu'une catégorie en compte plus de 3 » est supprimée : le CSV a sept colonnes et aucune ne porte de sous-catégorie — le mécanisme ne se serait jamais déclenché. C'était la transposition d'une fonctionnalité que l'ancienne application n'avait elle-même jamais implémentée.)* +2. **Les catégories sont des filtres, pas quatre écrans.** L'ancienne application posait quatre boutons en dur — `BoutonFruits`, `BoutonLegumes`, `BoutonVrac`, `BoutonAutres` — parce que quatre formulaires étaient préconstruits au démarrage, et ouvrait `FormulaireLegumes` en dur au lancement. La répartition réelle interdit cette parité, **et elle 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**. En 2022, « Autres » menait à **un seul produit** — un quart de barre de navigation pour une tuile ; en 2026, c'est la catégorie la plus peuplée. Aucune barre en dur ne survit à ça. Donc : **la vue au repos est « Tout »**, une puce par catégorie **peuplée** (seuil **configuré**, `ui.min_products_for_chip`, 5 par défaut ; en deçà, la catégorie reste dans « Tout » et ses produits restent atteignables par la recherche), l'effectif **pesable** est écrit sur la puce — jamais le nombre de lignes reçues, qui compte des préemballés sans tuile —, l'ordre et la couleur viennent de la configuration **et non d'un classement figé dans le code** (c'est précisément ce que l'inversion 2022 → 2026 rend nécessaire), et une catégorie peut être masquée sur **ce** poste (`categories[].visible` — le poste « fruits » n'a pas à montrer le vrac : c'est une vraie décision de magasin). Il n'y a **plus de catégorie ouverte par défaut** à configurer : « Tout » est toujours juste, et c'est un toucher de moins dans tous les cas. *(La « sous-catégorie en puces dès qu'une catégorie en compte plus de 3 » est supprimée : le CSV a sept colonnes et aucune ne porte de sous-catégorie — le mécanisme ne se serait jamais déclenché. C'était la transposition d'une fonctionnalité que l'ancienne application n'avait elle-même jamais implémentée.)* 3. **Recherche : un filtre en place, jamais une vue.** Le clavier est un **panneau ancré au tiers bas de l'écran** ; la grille reste visible au-dessus et **se réduit lettre après lettre**, insensible aux accents. Pas de bouton « OK », pas de validation, pas d'écran de résultats, **pas de plafond de résultats** — sur 331 entrées, deux ou trois lettres suffisent. Disposition **réduite** : les 26 lettres, l'espace et le retour arrière, à la cible tactile de 72 px ; ni touches accentuées (la normalisation s'en charge), ni symboles — aucun nom des deux fichiers réels ne contient de point-virgule ni de guillemet, et les 10 apostrophes n'ont jamais besoin d'être tapées. Refermer le panneau **est** l'effacement. Le catalogue complet (~60 ko JSON pour 355 produits, images exclues, §12.3) est **dans le navigateur** : filtrage et tri instantanés, **et fonctionnels pendant un redémarrage du service**. *(L'existant remplaçait la grille par un `FormulaireClavier` AZERTY plein écran puis par un `FormulaireProduitsClavier` distinct, avec refus au-delà de 50 résultats. Une surcouche plein écran cache exactement ce qu'elle filtre — la première version de ce document promettait des « résultats en direct dès la 1ʳᵉ lettre » tout en rangeant la recherche parmi les surcouches qui occupent tout l'écran : les deux ne pouvaient pas être vrais ensemble.)* 4. **Pas de rail alphabétique A–Z.** Il visait une liste trop longue pour être parcourue ; sur 331 entrées déjà triées, filtrables par catégorie et cherchables au clavier, il ajoute une cible de 26 zones étroites — donc fragiles au doigt — pour économiser un geste de défilement. Le défilement inertiel et la recherche couvrent le besoin. **S'il manque, `weighings.duration_ms` le dira** : c'est un ajout d'une demi-journée, réversible, qu'on ne fait pas avant d'avoir la mesure. -> **La vue au repos est la grille complète des produits pesables *que ce poste montre*.** Deux masquages, et deux seulement, la réduisent : une **catégorie** que ce poste ne sert pas (`categories[].visible`, point 2) et les **produits vendus à l'unité** quand `ui.show_by_unit_products` est faux (§11.2, ADR-041). Les deux sont des choix de **poste**, appliqués dans le navigateur, et jamais des décisions d'import : `GET /api/v1/catalog` sert toujours **tout ce qui a une tuile**, et les puces comptent ce que la grille montre réellement. La maquette ci-dessus est celle d'un poste au réglage par défaut : sur `flv.csv`, **316 tuiles pour 331 pesables**, les quinze produits à l'unité répartis en 5 fruits, 8 légumes et 2 vrac — aucun rayon ne passe sous le seuil de la puce, donc aucune catégorie ne disparaît. Ce n'est **ni un plafond ni une perte** : l'ancienne application, elle, perdait six produits vendables par dépassement silencieux de ses 120 emplacements, sans un message ni une ligne de journal. Ici l'écart est annoncé, sur la page Catalogue, et une case le défait. +> **La vue au repos est la grille complète des produits pesables *que ce poste montre*.** Deux masquages, et deux seulement, la réduisent : une **catégorie** que ce poste ne sert pas (`categories[].visible`, point 2) et les **produits vendus à l'unité** quand `ui.show_by_unit_products` est faux (§11.2, ADR-041). Les deux sont des choix de **poste**, appliqués dans le navigateur, et jamais des décisions d'import : `GET /api/v1/catalog` sert toujours **tout ce qui a une tuile**, et les puces comptent ce que la grille montre réellement. La maquette ci-dessus est celle d'un poste au réglage par défaut : sur `flv.csv`, **316 tuiles pour 331 pesables**, les quinze produits à l'unité répartis en 5 fruits, 8 légumes et 2 vrac — aucun rayon ne passe sous le seuil de la puce (`ui.min_products_for_chip`, 5 par défaut), donc aucune catégorie ne disparaît. Ce n'est **ni un plafond ni une perte** : l'ancienne application, elle, perdait six produits vendables par dépassement silencieux de ses 120 emplacements, sans un message ni une ligne de journal. Ici l'écart est annoncé, sur la page Catalogue, et une case le défait. > **La divergence de normalisation, fermée par la machine.** Le serveur envoie le nom désaccentué **calculé au moment de servir le catalogue** par `domain.Normalize` — jamais stocké, une seule source de vérité, le `name` (§12.3). Le navigateur ne normalise que la **requête**, avec une fonction de 12 lignes. Un fichier `web/testdata/normalization.json` (120 paires : « Œufs », « Poêlée », « ÉPINARD », « CÜRCÜMA », « Ail violet »…) est consommé **par le test Go et par le test Vitest**. Si les deux implémentations divergent, la CI casse. **Les chaînes réelles des fichiers authentiques y entrent** : « ♥ LENTILLES VERTES 10Kg » (le cœur U+2665 doit être ignoré à la recherche, sinon **127 produits sur 355** deviennent introuvables au clavier), « Œuf chocolat lait cœur lacté 2 kg » (la **ligature `Œ` se cherche par `OE`**, sinon le produit est inatteignable au clavier réduit), « Figue baglama calibre n°3 BIO » (le `°` et la **double espace**), « Chou Frisé Kale SAF », « FLOCONS D'AVOINE GROS 5KG » et « ♥AA-LA TOMME DES CROQUANTS AFFINE A LA LIQUEUR DE NOIX DU PERIGORD-MV ». @@ -4223,7 +4239,7 @@ Contrastes **AAA** (≥ 7:1) sur tout texte ≥ 24 px, **AA** partout ailleurs | Matériel | Balance : **protocole** (deux entrées, §9.3), **liste déroulante des ports détectés avec description USB**, **« Détecter automatiquement »** (ouvre le port 3 s et y essaie le **décodeur de chaque protocole qui déclare pouvoir être reconnu sur un port série**, un décodeur neuf par candidat ; annonce « COM8 : 12 trame(s) valide(s) en 3s — GRAM XFOC RS, GRAM XFOC + » et propose **le driver qui a reconnu les trames**, jamais la première entrée du registre — **c'est la détection qui répond à « y a-t-il une balance ? », pas l'exploitant**, ADR-046), case **« ce poste n'a pas de balance »** (`scale.present`, qui éteint le feu au lieu de le laisser rouge), **visualiseur des 20 dernières trames brutes en direct** (hexa + décodé, **toujours actif** : ce n'est plus un réglage ; le **découpage est celui du décodeur du protocole**, `Decoder.FrameEnd`, et jamais une recherche de `CR`/`LF` faite sur place — ADR-047), **cadence médiane**. Imprimante : driver, **transport en liste déroulante** (les transports que le binaire déclare, §8.4), **un seul champ d'appareil, celui de la clé que le transport choisi lit** — file, nœud d'impression ou adresse réseau —, **« Lister les files »**, **« Rechercher l'imprimante »** (les destinations que le transport choisi ne peut pas ouvrir ne sont pas proposées, et l'écart est annoncé), statut, **les auto-tests que le driver déclare honorer** (§8.6, ADR-049), trame de statut brute en hexa. | | Étiquette | Menu de gabarit, **aperçu PNG rafraîchi à chaque frappe** (identique à l'impression, A2), décalage X/Y en dots avec flèches ±1 **bornées par la géométrie**, noircissement, vitesse, exemplaires, **bandeau chiffré de `Diagnose()`** avec la mention « troncature volontaire (ADR-003) », éditeur tabulaire du gabarit. | | Règles | Grille de tarifs (code/libellé/abrégé/**remise en %**/ordre), **la ligne du tarif de référence en lecture seule pour son prix et modifiable pour ses mots**, les deux arrondis **avec l'explication de l'écart au centime**, les 14 garde-fous avec **leur seuil et leur message modifiables, leur sévérité en lecture seule** (§6.4), **aperçu en direct** (« à 8 g, ce produit serait refusé »), code-barres. **La liste des dérogations de poids minimum actives** — un produit nommé par ligne, avec son `min_weight_g`, sa date et son auteur —, en lecture ici et **modifiable depuis l'onglet Catalogue**, où se trouve le produit (§10.6). | -| Catalogue | **Où le poste va chercher le catalogue** — répertoire local ou serveur WebDAV, avec le répertoire surveillé, ou l'adresse et le compte (§10.1, ADR-038) ; le mot de passe s'y saisit **en écriture seule**, laissé vide il ne bouge pas, et choisir WebDAV avertit **au moment du choix** que le dépôt d'un CSV depuis l'écran n'y existe plus · Dernier import et son **inventaire** · **liste des anomalies et des unités divergentes, chacune avec le nom du produit, son numéro de ligne du CSV, son motif en français et la valeur fautive** · **liste des non-pesables** (préemballés, sans code-barres), nommés eux aussi, présentée comme un **inventaire neutre** et jamais comme une liste d'erreurs · produits **retirés** depuis l'import précédent (§10.9) · historique des 20 derniers imports · **glisser-déposer d'un CSV (A4)** · **interrupteur « Ne plus proposer ce produit »** · **« Autoriser ce produit à peser moins de 10 g »** (dérogation par produit, §10.6) · « Oublier la quarantaine » · état de la source · **le panneau « Ce que la grille montre »** : l'interrupteur des produits vendus à l'unité et le **choix des colonnes de la grille**, avec la phrase chiffrée qui dit ce que le brouillon donnerait sur cet écran (ci-dessous, ADR-057). | +| Catalogue | **Où le poste va chercher le catalogue** — répertoire local ou serveur WebDAV, avec le répertoire surveillé, ou l'adresse et le compte (§10.1, ADR-038) ; le mot de passe s'y saisit **en écriture seule**, laissé vide il ne bouge pas, et choisir WebDAV avertit **au moment du choix** que le dépôt d'un CSV depuis l'écran n'y existe plus · Dernier import et son **inventaire** · **liste des anomalies et des unités divergentes, chacune avec le nom du produit, son numéro de ligne du CSV, son motif en français et la valeur fautive** · **liste des non-pesables** (préemballés, sans code-barres), nommés eux aussi, présentée comme un **inventaire neutre** et jamais comme une liste d'erreurs · produits **retirés** depuis l'import précédent (§10.9) · historique des 20 derniers imports · **glisser-déposer d'un CSV (A4)** · **interrupteur « Ne plus proposer ce produit »** · **« Autoriser ce produit à peser moins de 10 g »** (dérogation par produit, §10.6) · « Oublier la quarantaine » · état de la source · **le panneau « Ce que la grille montre »** : l'interrupteur des produits vendus à l'unité, le **choix des colonnes de la grille** — avec la phrase chiffrée qui dit ce que le brouillon donnerait sur cet écran (ci-dessous, ADR-057) — et **le seuil de puce** (`ui.min_products_for_chip`, champ nombre, plancher 1, sans plafond, ADR-059), sans cette phrase chiffrée : rien n'annonce sur cet écran, avant l'enregistrement, si une catégorie va perdre sa puce. | | Journal | 200 dernières pesées, filtres, export CSV, détail **avec la trame brute** et bouton « Rejouer cette trame ». Journal technique en dessous. | | Poste | Numéro, nom, coop, empreinte, export/import avec **aperçu du diff champ par champ**, 5 versions restaurables, version du binaire, chemins, espace disque. **Rubrique Maintenance** en bas de page : *relire le fichier de configuration* (sans coupure) · *redémarrer le poste* (~5 s) · *redémarrer l'ordinateur* (rouge, confirmation puis 30 s annulables). *(Aucun bloc de configuration n'exige toujours de redémarrage — §11.4, ADR-027 : ces trois gestes sont des réparations, pas des conséquences d'un réglage.)* | @@ -4250,6 +4266,8 @@ Contrastes **AAA** (≥ 7:1) sur tout texte ≥ 24 px, **AA** partout ailleurs > > **L'honnêteté du « sur cet écran », et elle est gratuite.** `admin_on_lan` permet d'ouvrir cette page depuis un portable. Quand l'adresse n'est ni `localhost` ni `127.0.0.1`, la phrase ajoute : *« Cet écran n'est pas celui du poste : ce compte vaut pour l'écran que vous lisez. »* Zéro donnée en plus, zéro route en plus, et le cas « faux en silence » est **nommé** au lieu d'être subi. C'est aussi pourquoi une erreur de réglage n'est pas grave ici : elle se voit sur le poste, et elle se répare en revenant sur cette page — ce que la dernière ligne du panneau dit en toutes lettres. +> **Le seuil de puce se règle dans le même panneau, sous le choix des colonnes** (`ui.min_products_for_chip`, ADR-059) : un simple champ nombre, plancher `1`, sans plafond. **Il n'a pas l'aperçu chiffré des colonnes** : aucune phrase n'annonce, avant l'enregistrement, si le nombre qu'on vient de taper va faire perdre sa puce à une catégorie du catalogue en service — la note du champ dit seulement ce que le réglage fait en général, jamais ce qu'il donnerait sur ce poste. C'est la conséquence assumée d'ADR-059 : se tromper se répare en revenant sur le champ, mais rien ne le signale avant. + **Premier démarrage** : assistant en 5 étapes (mot de passe → poste → balance → imprimante → catalogue). Tant que le catalogue est vide, la grille client affiche *« Catalogue vide. En attente du fichier `flv_.csv` depuis 4 min »* — **le nom est dérivé de `station.number`**, jamais écrit en dur dans un message — **avec le chemin surveillé en clair** et un renvoi vers l'import manuel. **Authentification** : `POST /admin/api/session` → **argon2id** (`x/crypto/argon2`) → cookie `HttpOnly; SameSite=Strict; Path=/admin`, session 30 min en mémoire, **5 tentatives/minute par IP** puis verrouillage 5 min. Vérification de `Origin`/`Host` sur toute méthode non-`GET` (CSRF et DNS-rebinding contre `127.0.0.1`). **Aucune porte dérobée** — le littéral `"admin"` de l'existant disparaît. @@ -5140,7 +5158,7 @@ rétrécir le dessin ; la question reste ouverte. **Décision.** On distingue les **saisies** des **comptes rendus**. Les saisies (tare, quantité) sont rares, locales et non modales : la tare vit dans le bandeau haut avec un pavé ancré dessous, la quantité dans la tuile elle-même sur appui maintenu — et un produit à l'unité s'imprime **au premier toucher, pour 1 unité**. La recherche est un **filtre en place** : clavier ancré au tiers bas, grille visible au-dessus. Les comptes rendus ne sont **jamais** modaux : `Succeeded` devient un accusé de bandeau, `Rejected` un message de bandeau plus un liseré sur la tuile touchée. Le plein écran est réservé à ce qui empêche de servir : `Faulted`, `OutOfService`, `ERR-CFG-01`. Le retour au repos est piloté par le **signal physique** (sac retiré → `Idle`) ; `idle_timeout_s` ne vide plus qu'une saisie abandonnée. `success_delay_ms`, `reject_delay_ms` et `switch_delay_s` deviennent des constantes du code. **Conséquences.** (a) La qualité centrale de l'existant — **un toucher = une étiquette, sans confirmation** — est préservée **y compris pour les 15 produits à l'unité**, qui exigeaient trois gestes et la disparition de la grille. (b) Une incrustation de succès plein écran **ajoutait** un écran que l'ancienne application n'avait pas : elle est supprimée, le vrai retour d'information est l'étiquette qui sort. (c) Un refus n'oblige plus le client à fermer quoi que ce soit pour corriger. (d) La machine à états perd un état et un événement (`EnteringUnits`, `UnitsConfirmed`) : le produit cartésien testé tombe de 224 à 208 couples. (e) On perd la possibilité de saisir une quantité en aveugle depuis un clavier plein écran ; c'est le but — sur ce volume, la réponse est « 1 » dans l'immense majorité des cas, et la quantité multiple reste accessible en un appui maintenu. (f) **Le plein écran laisse passer une chose, et une seule** : la touche « Réglages » de la barre basse. Un poste neuf démarre en `OutOfService` ; couvrir cette touche le rendait inconfigurable par tout chemin prévu, puisqu'ADR-032 n'en laisse aucun autre. Le voile garde toute la fenêtre, « Réimprimer » comprise (§14.3). -### ADR-024 — Une seule grille, des filtres dérivés de la donnée, pas quatre écrans +### ADR-024 — Une seule grille, des filtres dérivés de la donnée, pas quatre écrans *(amendé, sans être renversé, par ADR-059)* **Contexte.** L'écran client de l'existant était **quatre formulaires préconstruits au démarrage** (`FormulaireFruits`, `FormulaireLegumes`, `FormulaireVrac`, `FormulaireAutres`), d'où quatre boutons en dur, un `SourceObject = "FormulaireLegumes"` écrit en dur au lancement, quatre cases `Categorie…Visible`, et une taille de tuile pilotée par les tranches de `Systeme_Dimensions`. La première version de cette architecture a conservé la barre de quatre onglets équipondérés, un `default_category`, un réglage de densité et un rail A–Z — plus une exigence chiffrée à « 340 produits », héritée de la table `Produits` de l'ancienne base. **Décision.** Une **seule grille**, celle des produits pesables, triée, ouverte par défaut sur « Tout ». Les catégories deviennent des **puces de filtre** dont le rendu dérive de la donnée : une catégorie n'obtient sa puce qu'au-delà de 5 produits pesables sur ce poste, avec son effectif écrit dessus. Une **seule densité**, déduite de la cible tactile (≥ 72 px) et de la lisibilité d'un nom de 49 caractères. **Pas de rail alphabétique**, pas de `ui.open_category`, pas de `ui.grid_density`, pas d'horloge. L'exigence est reformulée en termes d'utilisateur et **mesurée** : *n'importe quel produit atteint en moins de 4 secondes*, via `weighings.duration_ms`. **Conséquences.** (a) La répartition réelle — **V = 58, L = 84, A = 1, F = 10** — rendait la barre de quatre onglets absurde : un quart de la navigation menait à **un** produit. (b) 128 px de barre d'onglets et un rail vertical libérés au profit de la grille. (c) Un toucher de moins dans tous les cas où le client ne connaît pas la catégorie de son produit. (d) On perd la mémoire d'une catégorie préférée par poste ; `categories[].visible` couvre le seul cas qui le justifiait (un poste « fruits » qui ne montre pas le vrac), et c'est une décision de magasin, pas un défaut d'affichage. (e) Le rail A–Z et un regroupement plus fin restent des ajouts d'une demi-journée, **à décider sur la mesure** — pas avant. (f) **Le plafond de 120 produits par catégorie s'en va avec les formulaires qui le portaient**, et ce n'est pas une précaution théorique : les 120 contrôles `Image0…Image119` du squelette Access sont dépassés **aujourd'hui** par les 126 pesables de « Autres », dont les six derniers sont perdus en silence (§14.3). Une grille qui est une liste n'a pas de plafond à franchir ; c'est le gain de la réécriture le plus facile à vérifier — et le seul qui se compte en produits vendables. @@ -5887,6 +5905,18 @@ La conversion de `coef_num`/`coef_den` est **exacte ou n'est pas.** `discount_pe **La conséquence tenue par un test, pas par une relecture.** `TestEveryRetiredKeyHasADeclaredVerdict` échoue si une clé de `retiredKeys` (§11.3, contrôle 20) n'a pas de verdict déclaré dans `retiredVerdicts` (§11.6). C'est ce test, et non une convention à se souvenir, qui empêche qu'une dixième clé retirée demain rejoue le 01/08/2026 : qui retire une clé doit répondre, dans le même fichier, à *que devient un document qui la porte encore*. +### ADR-059 — Le seuil de puce devient un réglage, `ui.min_products_for_chip` + +**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 qui donne sa puce de filtre à une catégorie était une constante du front, `MIN_PRODUCTS_FOR_CHIP`, dont le commentaire disait « CONSTANTE DU CODE, pas un réglage (ADR-025) ». ADR-024 a mesuré qu'un seuil doit exister — en 2022, `flv_1.csv` donnait `A = 1` : « Autres » menait à un seul produit, un quart de barre de navigation pour une tuile — mais aucune mesure n'y dit pourquoi cinq plutôt que trois ou huit. Le catalogue en service inverse la répartition qui a justifié ce seuil : `flv.csv` donne `A = 140, V = 118, L = 68, F = 29`, avec 126 tuiles pesables pour la seule catégorie « Autres ». Un nombre figé dans le code ne peut convenir ni aux deux formes à la fois, ni à celle d'une autre coopérative. + +**Décision.** `ui.min_products_for_chip`, un entier, **défaut 5**, **plancher 1**, **pas de plafond**, appliqué à **chaque catégorie séparément, sur son propre effectif de tuiles sur ce poste**. Il n'existe nulle part de réglage sur le *nombre* de catégories : le seuil ne fait que retenir ou retirer une puce, une catégorie à la fois. Le contrôle 50 refuse une valeur sous 1 ; un document qui ne porte pas la clé — le fichier livré compris — se relit au défaut, exactement comme `ui.grid_columns` (ADR-057) : `Config.UnmarshalJSON` corrige le zéro, et le profil neutre écrit `5` en toutes lettres. + +**Pourquoi ce réglage a le droit d'exister.** Même argument qu'`ui.grid_columns` (ADR-057) : « à partir de combien de produits une catégorie mérite-t-elle sa puce » dépend de la forme du catalogue d'une coopérative, une forme qu'aucune mesure d'écran ne tranche et qui s'inverse d'un export à l'autre pour la même coopérative. ADR-025 en autorise donc un réglage, et **ADR-024 n'est pas renversé** : ce qu'il a établi — qu'un seuil doit exister — reste vrai ; ce qui change est que le nombre qu'il avait posé devient la décision d'un exploitant plutôt qu'une valeur mesurée. + +**Conséquences.** (a) **Le comportement livré ne bouge pas** : le fichier livré se tait sur cette clé, comme il se tait déjà sur `grid_columns`, et §11.2 dit pourquoi ce silence est sûr — un `Config` décodé sans la clé porte le défaut, jamais un zéro qui donnerait sa puce à une catégorie sans tuile. (b) **Le seuil retire une puce et jamais une tuile** : `categories[].visible` reste le seul mécanisme qui retire des produits d'un écran, et une catégorie sous le seuil garde toutes ses tuiles dans « Tout » et à la recherche. (c) **Le champ entre dans `presentationDigest` par réflexion** (§11.4) : un écran client voisin applique le nouveau seuil par le flux d'état, sans redémarrage. (d) **Conséquence assumée, et pas seulement possible** : sans plafond, un seuil supérieur au plus gros rayon d'une coopérative laisse la barre de catégories avec « Tout » seul, et rien à l'écran ne le signale avant l'enregistrement. C'est le prix d'« un seul paramètre » plutôt qu'un plafond dérivé du catalogue en service ; il est réversible en revenant sur le champ, exactement comme un mauvais nombre de colonnes l'est pour ADR-057. + --- ## 21. Inconnues à lever sur site 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 new file mode 100644 index 0000000..f881fcf --- /dev/null +++ b/docs/superpowers/specs/2026-08-04-seuil-de-puce-reglable-design.md @@ -0,0 +1,256 @@ +# 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. + +### 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 + +`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 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. +// +// 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` | **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 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 +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. diff --git a/internal/domain/config.go b/internal/domain/config.go index fc119cd..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). @@ -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/fingerprint_test.go b/internal/domain/fingerprint_test.go index de0b504..af6ff07 100644 --- a/internal/domain/fingerprint_test.go +++ b/internal/domain/fingerprint_test.go @@ -59,11 +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 }, - "la rétention du journal": func(c *Config) { c.Journal.MaxDays = 30 }, + "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 }, // 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 }, @@ -71,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) 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{ diff --git a/internal/domain/validate.go b/internal/domain/validate.go index bae4617..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. @@ -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_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_order_test.go b/internal/domain/validate_order_test.go index 0f6d958..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 -// fields at once and walks the whole numbering from control 1 to control 49. 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 champs cassés, du contrôle 1 au contrôle 49", + 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 @@ -74,6 +75,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 +105,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..36e70ba 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 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 +// 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..dbbbb87 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 @@ -466,3 +466,54 @@ 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 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 +// (§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, 0} { + 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: 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 { + t.Fatalf("le silence du fichier livré est traité comme une faute : %s", fault.Message) + } +} diff --git a/internal/web/catalog.go b/internal/web/catalog.go index cd30ee7..506c4ea 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, } } @@ -306,7 +312,24 @@ 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 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, Visible: c.Visible, ProductCount: counts[c.Code], diff --git a/internal/web/catalog_test.go b/internal/web/catalog_test.go index 3f4223a..e268a2d 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 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{} + 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) } @@ -234,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 }, @@ -347,6 +358,90 @@ 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 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 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, + 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) + } +} + +// 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/dist/admin.html b/internal/web/dist/admin.html index 9ea47b5..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-BJRgB0wR.js b/internal/web/dist/assets/admin-BJRgB0wR.js deleted file mode 100644 index 2e9fc94..0000000 --- a/internal/web/dist/assets/admin-BJRgB0wR.js +++ /dev/null @@ -1 +0,0 @@ -import"./app-CFgWi82J.js";import{mountAdmin as m}from"./mount-DmM_mBVD.js";m(document.getElementById("app")); 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/app-CFgWi82J.js b/internal/web/dist/assets/app-BaRSXv80.js similarity index 88% rename from internal/web/dist/assets/app-CFgWi82J.js rename to internal/web/dist/assets/app-BaRSXv80.js index f585b62..0577464 100644 --- a/internal/web/dist/assets/app-CFgWi82J.js +++ b/internal/web/dist/assets/app-BaRSXv80.js @@ -1,2 +1,2 @@ (function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))r(i);new MutationObserver(i=>{for(const s of i)if(s.type==="childList")for(const f of s.addedNodes)f.tagName==="LINK"&&f.rel==="modulepreload"&&r(f)}).observe(document,{childList:!0,subtree:!0});function n(i){const s={};return i.integrity&&(s.integrity=i.integrity),i.referrerPolicy&&(s.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?s.credentials="include":i.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function r(i){if(i.ep)return;i.ep=!0;const s=n(i);fetch(i.href,s)}})();const wn=!1;var mn=Array.isArray,wr=Array.prototype.indexOf,ut=Array.prototype.includes,pt=Array.from,mr=Object.defineProperty,Ce=Object.getOwnPropertyDescriptor,br=Object.getOwnPropertyDescriptors,Er=Object.prototype,yr=Array.prototype,bn=Object.getPrototypeOf,rn=Object.isExtensible;const Sr=()=>{};function Tr(e){for(var t=0;t{e=r,t=i});return{promise:n,resolve:e,reject:t}}const C=2,Pe=4,gt=8,yn=1<<24,X=16,G=32,oe=64,kt=128,B=512,N=1024,M=2048,W=4096,R=8192,H=16384,je=32768,Ct=1<<25,Re=65536,ct=1<<17,Ar=1<<18,He=1<<19,Mr=1<<20,$=1<<25,ye=65536,dt=1<<21,xe=1<<22,ve=1<<23,we=Symbol("$state"),Or=Symbol("legacy props"),Nr=Symbol(""),Sn=Symbol("attributes"),xt=Symbol("class"),Pt=Symbol("style"),Rt=Symbol("text"),ft=Symbol("form reset"),Qe=new class extends Error{name="StaleReactionError";message="The reaction that called `getAbortSignal()` was re-run or destroyed"},kr=!!globalThis.document?.contentType&&globalThis.document.contentType.includes("xml");function Cr(){throw new Error("https://svelte.dev/e/async_derived_orphan")}function xr(e,t,n){throw new Error("https://svelte.dev/e/each_key_duplicate")}function Pr(e){throw new Error("https://svelte.dev/e/effect_in_teardown")}function Rr(){throw new Error("https://svelte.dev/e/effect_in_unowned_derived")}function Ir(e){throw new Error("https://svelte.dev/e/effect_orphan")}function Lr(){throw new Error("https://svelte.dev/e/effect_update_depth_exceeded")}function Dr(e){throw new Error("https://svelte.dev/e/props_invalid_value")}function Fr(){throw new Error("https://svelte.dev/e/state_descriptors_fixed")}function jr(){throw new Error("https://svelte.dev/e/state_prototype_fixed")}function Hr(){throw new Error("https://svelte.dev/e/state_unsafe_mutation")}function zr(){throw new Error("https://svelte.dev/e/svelte_boundary_reset_onerror")}const Ur=1,qr=2,Tn=4,Br=8,Gr=16,Vr=1,Yr=4,Xr=8,Kr=16,Zr=1,Wr=2,O=Symbol("uninitialized"),Jr="http://www.w3.org/1999/xhtml";function Qr(){console.warn("https://svelte.dev/e/derived_inert")}function Is(){console.warn("https://svelte.dev/e/select_multiple_invalid_value")}function $r(){console.warn("https://svelte.dev/e/svelte_boundary_reset_noop")}function An(e){return e===this.v}function ei(e,t){return e!=e?t==t:e!==t||e!==null&&typeof e=="object"||typeof e=="function"}function Mn(e){return!ei(e,this.v)}let ti=!1,I=null;function Ie(e){I=e}function On(e,t=!1,n){I={p:I,i:!1,c:null,e:null,s:e,x:null,r:w,l:null}}function Nn(e){var t=I,n=t.e;if(n!==null){t.e=null;for(var r of n)Kn(r)}return t.i=!0,I=t.p,{}}function kn(){return!0}let pe=[];function Cn(){var e=pe;pe=[],Tr(e)}function ee(e){if(pe.length===0&&!Xe){var t=pe;queueMicrotask(()=>{t===pe&&Cn()})}pe.push(e)}function ni(){for(;pe.length>0;)Cn()}function xn(e){var t=w;if(t===null)return m.f|=ve,e;if((t.f&je)===0&&(t.f&Pe)===0)throw e;he(e,t)}function he(e,t){if(!(t!==null&&(t.f&H)!==0)){for(;t!==null;){if((t.f&kt)!==0){if((t.f&je)===0)throw e;try{t.b.error(e);return}catch(n){e=n}}t=t.parent}throw e}}const ri=-7169;function A(e,t){e.f=e.f&ri|t}function qt(e){(e.f&B)!==0||e.deps===null?A(e,N):A(e,W)}function Pn(e){if(e!==null)for(const t of e)(t.f&C)===0||(t.f&ye)===0||(t.f^=ye,Pn(t.deps))}function Rn(e,t,n){(e.f&M)!==0?t.add(e):(e.f&W)!==0&&n.add(e),Pn(e.deps),A(e,N)}let nt=!1;function ii(e){var t=nt;try{return nt=!1,[e(),nt]}finally{nt=t}}function Ls(e,t){{const n=document.body;e.autofocus=!0,ee(()=>{document.activeElement===n&&e.focus()})}}let sn=!1;function si(){sn||(sn=!0,document.addEventListener("reset",e=>{Promise.resolve().then(()=>{if(!e.defaultPrevented)for(const t of e.target.elements)t[ft]?.()})},{capture:!0}))}function ze(e){var t=m,n=w;V(null),ne(null);try{return e()}finally{V(t),ne(n)}}function Ds(e,t,n,r=n){e.addEventListener(t,()=>ze(n));const i=e[ft];i?e[ft]=()=>{i(),r(!0)}:e[ft]=()=>r(!0),si()}function fi(e){let t=0,n=Se(0),r;return()=>{Yt()&&(T(n),Zn(()=>(t===0&&(r=Wt(()=>e(()=>Ke(n)))),t+=1,()=>{ee(()=>{t-=1,t===0&&(r?.(),r=void 0,Ke(n))})})))}}var ai=Re|He;function li(e,t,n,r){new oi(e,t,n,r)}class oi{parent;is_pending=!1;transform_error;#t;#f=null;#e;#l;#r;#s=null;#n=null;#a=null;#i=null;#_=0;#o=0;#u=!1;#d=new Set;#p=new Set;#c=null;#w=fi(()=>(this.#c=Se(this.#_),()=>{this.#c=null}));constructor(t,n,r,i){this.#t=t,this.#e=n,this.#l=s=>{var f=w;f.b=this,f.f|=kt,r(s)},this.parent=w.b,this.transform_error=i??this.parent?.transform_error??(s=>s),this.#r=Xt(()=>{this.#h()},ai)}#g(){try{this.#s=q(()=>this.#l(this.#t))}catch(t){this.error(t)}}#E(t){const n=this.#e.failed,{reset:r,invoke_onerror:i}=this.#m(t);ee(i),n&&(this.#a=q(()=>{n(this.#t,()=>t,()=>r)}))}#m(t){var n=!1,r=!1;const i=()=>{if(n){$r();return}n=!0,r&&zr(),this.#a!==null&&be(this.#a,()=>{this.#a=null}),this.#v(()=>{this.#h()})};return{reset:i,invoke_onerror:()=>{try{r=!0,this.#e.onerror?.(t,i),r=!1}catch(f){he(f,this.#r&&this.#r.parent)}}}}#y(){const t=this.#e.pending;t&&(this.is_pending=!0,this.#n=q(()=>t(this.#t)),ee(()=>{var n=this.#i=document.createDocumentFragment(),r=le();n.append(r),this.#s=this.#v(()=>q(()=>this.#l(r))),this.#o===0&&(this.#t.before(n),this.#i=null,be(this.#n,()=>{this.#n=null}),this.#b(b))}))}#h(){try{if(this.is_pending=this.has_pending_snippet(),this.#o=0,this.#_=0,this.#s=q(()=>{this.#l(this.#t)}),this.#o>0){var t=this.#i=document.createDocumentFragment();Zt(this.#s,t);const n=this.#e.pending;this.#n=q(()=>n(this.#t))}else this.#b(b)}catch(n){this.error(n)}}#b(t){this.is_pending=!1,t.transfer_effects(this.#d,this.#p)}defer_effect(t){Rn(t,this.#d,this.#p)}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!this.#e.pending}#v(t){var n=w,r=m,i=I;ne(this.#r),V(this.#r),Ie(this.#r.ctx);try{return _e.ensure(),t()}catch(s){return xn(s),null}finally{ne(n),V(r),Ie(i)}}#S(t,n){if(!this.has_pending_snippet()){this.parent&&this.parent.#S(t,n);return}this.#o+=t,this.#o===0&&(this.#b(n),this.#n&&be(this.#n,()=>{this.#n=null}),this.#i&&(this.#t.before(this.#i),this.#i=null))}update_pending_count(t,n){this.#S(t,n),this.#_+=t,!(!this.#c||this.#u)&&(this.#u=!0,ee(()=>{this.#u=!1,this.#c&&Le(this.#c,this.#_)}))}get_effect_pending(){return this.#w(),T(this.#c)}error(t){if(!this.#e.onerror&&!this.#e.failed)throw t;b?.is_fork?(this.#s&&b.skip_effect(this.#s),this.#n&&b.skip_effect(this.#n),this.#a&&b.skip_effect(this.#a),b.oncommit(()=>{this.#T(t)})):this.#T(t)}#T(t){this.#s&&(D(this.#s),this.#s=null),this.#n&&(D(this.#n),this.#n=null),this.#a&&(D(this.#a),this.#a=null);let n=this.#e.failed;const r=i=>{const{reset:s,invoke_onerror:f}=this.#m(i);f(),n&&(this.#a=this.#v(()=>{try{return q(()=>{var l=w;l.b=this,l.f|=kt,n(this.#t,()=>i,()=>s)})}catch(l){return he(l,this.#r.parent),null}}))};ee(()=>{var i;try{i=this.transform_error(t)}catch(s){he(s,this.#r&&this.#r.parent);return}i!==null&&typeof i=="object"&&typeof i.then=="function"?i.then(r,s=>he(s,this.#r&&this.#r.parent)):r(i)})}}function ui(e,t,n,r){const i=Ze;var s=e.filter(h=>!h.settled),f=t.map(i);if(n.length===0&&s.length===0){r(f);return}var l=w,a=ci(),o=s.length===1?s[0].promise:s.length>1?Promise.all(s.map(h=>h.promise)):null;function c(h){if((l.f&H)===0){a();try{r([...f,...h])}catch(v){he(v,l)}ht()}}var d=In();if(n.length===0){o.then(()=>c([])).finally(d);return}function u(){Promise.all(n.map(h=>di(h))).then(c).catch(h=>he(h,l)).finally(d)}o?o.then(()=>{a(),u(),ht()}):u()}function ci(){var e=w,t=m,n=I,r=b;return function(s=!0){ne(e),V(t),Ie(n),s&&(e.f&H)===0&&(r?.activate(),r?.apply())}}function ht(e=!0){ne(null),V(null),Ie(null),e&&b?.deactivate()}function In(){var e=w,t=e.b,n=b,r=!!t?.is_rendered();return t?.update_pending_count(1,n),n.increment(r,e),()=>{t?.update_pending_count(-1,n),n.decrement(r,e)}}function Ze(e){var t=C|M;return w!==null&&(w.f|=He),{ctx:I,deps:null,effects:null,equals:An,f:t,fn:e,reactions:null,rv:0,v:O,wv:0,parent:w,ac:null}}const Be=Symbol("obsolete");function di(e,t,n){let r=w;r===null&&Cr();var i=void 0,s=Se(O),f=!m,l=new Set;return Oi(()=>{var a=w,o=En();i=o.promise;try{Promise.resolve(e()).then(o.resolve,h=>{h!==Qe&&o.reject(h)}).finally(ht)}catch(h){o.reject(h),ht()}var c=b;if(f){if((a.f&je)!==0)var d=In();if(r.b?.is_rendered())c.async_deriveds.get(a)?.reject(Be);else for(const h of l.values())h.reject(Be);l.add(o),c.async_deriveds.set(a,o)}const u=(h,v=void 0)=>{d?.(),l.delete(o),v!==Be&&(c.activate(),v?(s.f|=ve,Le(s,v)):((s.f&ve)!==0&&(s.f^=ve),Le(s,h)),c.deactivate())};o.promise.then(u,h=>u(null,h||"unknown"))}),Xn(()=>{for(const a of l)a.reject(Be)}),new Promise(a=>{function o(c){function d(){c===i?a(s):o(i)}c.then(d,d)}o(i)})}function rt(e){const t=Ze(e);return er(t),t}function Ln(e){const t=Ze(e);return t.equals=Mn,t}function hi(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;n{t.ac.abort(Qe),t.ac=null}),t.fn!==null&&(t.teardown=Sr),We(t,0),Kt(t))}function Fn(e){if(e.effects!==null)for(const t of e.effects)t.teardown&&t.fn!==null&&Fe(t)}let yt=null,Me=null,b=null,It=null,K=null,Lt=null,Xe=!1,St=!1,Ne=null,at=null;var fn=0;let _i=1;class _e{id=_i++;#t=!1;linked=!0;#f=null;#e=null;async_deriveds=new Map;current=new Map;previous=new Map;#l=new Set;#r=new Set;#s=0;#n=new Map;#a=null;#i=[];#_=[];#o=new Set;#u=new Set;#d=new Map;#p=new Set;is_fork=!1;#c=!1;constructor(){Me===null?yt=Me=this:(Me.#e=this,this.#f=Me),Me=this}#w(){if(this.is_fork)return!0;for(const r of this.#n.keys()){for(var t=r,n=!1;t.parent!==null;){if(this.#d.has(t)){n=!0;break}t=t.parent}if(!n)return!0}return!1}skip_effect(t){this.#d.has(t)||this.#d.set(t,{d:[],m:[]}),this.#p.delete(t)}unskip_effect(t,n=r=>this.schedule(r)){var r=this.#d.get(t);if(r){this.#d.delete(t);for(var i of r.d)A(i,M),n(i);for(i of r.m)A(i,W),n(i)}this.#p.add(t)}#g(){this.#t=!0,fn++>1e3&&(this.#v(),gi());for(const a of this.#o)this.#u.delete(a),A(a,M),this.schedule(a);for(const a of this.#u)A(a,W),this.schedule(a);const t=this.#i;this.#i=[],this.apply();var n=Ne=[],r=[],i=at=[];for(const a of t)try{this.#E(a,n,r)}catch(o){throw zn(a),this.#w()||this.discard(),o}if(b=null,i.length>0){var s=_e.ensure();for(const a of i)s.schedule(a)}if(Ne=null,at=null,this.#w()){this.#h(r),this.#h(n);for(const[a,o]of this.#d)Hn(a,o);i.length>0&&b.#g();return}const f=this.#m();if(f){this.#h(r),this.#h(n),f.#y(this);return}this.#o.clear(),this.#u.clear();for(const a of this.#l)a(this);this.#l.clear(),It=this,an(r),an(n),It=null,this.#a?.resolve();var l=b;if(this.#s===0&&(this.#i.length===0||l!==null)&&this.#v(),this.#i.length>0)if(l!==null){const a=l;a.#i.push(...this.#i.filter(o=>!a.#i.includes(o)))}else l=this;l!==null&&l.#g()}#E(t,n,r){t.f^=N;for(var i=t.first;i!==null;){var s=i.f,f=(s&(G|oe))!==0,l=f&&(s&N)!==0,a=l||(s&R)!==0||this.#d.has(i);if(!a&&i.fn!==null){f?i.f^=N:(s&Pe)!==0?n.push(i):et(i)&&((s&X)!==0&&this.#u.add(i),Fe(i));var o=i.first;if(o!==null){i=o;continue}}for(;i!==null;){var c=i.next;if(c!==null){i=c;break}i=i.parent}}}#m(){for(var t=this.#f;t!==null;){if(!t.is_fork){for(const[n,[,r]]of this.current)if(t.current.has(n)&&!r)return t}t=t.#f}return null}#y(t){for(const[r,i]of t.current)!this.previous.has(r)&&t.previous.has(r)&&this.previous.set(r,t.previous.get(r)),this.current.set(r,i);for(const[r,i]of t.async_deriveds){const s=this.async_deriveds.get(r);s&&i.promise.then(s.resolve).catch(s.reject)}t.async_deriveds.clear(),this.transfer_effects(t.#o,t.#u);const n=r=>{var i=r.reactions;if(i!==null&&!((r.f&C)!==0&&(r.f&(M|W))===0))for(const l of i){var s=l.f;if((s&C)!==0)n(l);else{var f=l;s&(xe|X)&&!this.async_deriveds.has(f)&&(this.#u.delete(f),A(f,M),this.schedule(f))}}};for(const r of this.current.keys())n(r);this.oncommit(()=>t.discard()),t.#v(),b=this,this.#g()}#h(t){for(var n=0;n!d.current.get(u)[1]);if(!(!d.#t||i.length===0)){var s=i.filter(u=>!this.current.has(u));if(s.length===0)t&&d.discard();else if(n.length>0){if(t)for(const u of this.#p)d.unskip_effect(u,h=>{(h.f&(X|xe))!==0?d.schedule(h):d.#h([h])});d.activate();var f=new Set,l=new Map;for(var a of n)jn(a,s,f,l);l=new Map;var o=[...d.current].filter(([u,h])=>{const v=this.current.get(u);return v?v[0]!==h[0]||v[1]!==h[1]:!0}).map(([u])=>u);if(o.length>0)for(const u of this.#_)(u.f&(H|R|ct))===0&&Gt(u,o,l)&&((u.f&(xe|X))!==0?(A(u,M),d.schedule(u)):d.#o.add(u));if(d.#i.length>0&&!d.#c){d.apply();for(var c of d.#i)d.#E(c,[],[]);d.#i=[]}d.deactivate()}}}}increment(t,n){if(this.#s+=1,t){let r=this.#n.get(n)??0;this.#n.set(n,r+1)}}decrement(t,n){if(this.#s-=1,t){let r=this.#n.get(n)??0;r===1?this.#n.delete(n):this.#n.set(n,r-1)}this.#c||(this.#c=!0,ee(()=>{this.#c=!1,this.linked&&this.flush()}))}transfer_effects(t,n){for(const r of t)this.#o.add(r);for(const r of n)this.#u.add(r);t.clear(),n.clear()}oncommit(t){this.#l.add(t)}ondiscard(t){this.#r.add(t)}settled(){return(this.#a??=En()).promise}static ensure(){if(b===null){const t=b=new _e;!St&&!Xe&&ee(()=>{t.#t||t.flush()})}return b}apply(){{K=null;return}}schedule(t){if(Lt=t,t.b?.is_pending&&(t.f&(Pe|gt|yn))!==0&&(t.f&je)===0){t.b.defer_effect(t);return}for(var n=t;n.parent!==null;){n=n.parent;var r=n.f;if(Ne!==null&&n===w&&(m===null||(m.f&C)===0))return;if((r&(oe|G))!==0){if((r&N)===0)return;n.f^=N}}this.#i.push(n)}#v(){if(this.linked){var t=this.#f,n=this.#e;t===null?yt=n:t.#e=n,n===null?Me=t:n.#f=t,this.linked=!1}}}function pi(e){var t=Xe;Xe=!0;try{for(var n;;){if(ni(),b===null)return n;b.flush()}}finally{Xe=t}}function gi(){try{Lr()}catch(e){he(e,Lt)}}let fe=null;function an(e){var t=e.length;if(t!==0){for(var n=0;n0)){me.clear();for(const i of fe){if((i.f&(H|R))!==0)continue;const s=[i];let f=i.parent;for(;f!==null;)fe.has(f)&&(fe.delete(f),s.push(f)),f=f.parent;for(let l=s.length-1;l>=0;l--){const a=s[l];(a.f&(H|R))===0&&Fe(a)}}fe.clear()}}fe=null}}function jn(e,t,n,r){if(!n.has(e)&&(n.add(e),e.reactions!==null))for(const i of e.reactions){const s=i.f;(s&C)!==0?jn(i,t,n,r):(s&(xe|X))!==0&&(s&M)===0&&Gt(i,t,r)&&(A(i,M),Vt(i))}}function Gt(e,t,n){const r=n.get(e);if(r!==void 0)return r;if(e.deps!==null)for(const i of e.deps){if(ut.call(t,i))return!0;if((i.f&C)!==0&&Gt(i,t,n))return n.set(i,!0),!0}return n.set(e,!1),!1}function Vt(e){b.schedule(e)}function Hn(e,t){if(!((e.f&G)!==0&&(e.f&N)!==0)){(e.f&M)!==0?t.d.push(e):(e.f&W)!==0&&t.m.push(e),A(e,N);for(var n=e.first;n!==null;)Hn(n,t),n=n.next}}function zn(e){A(e,N);for(var t=e.first;t!==null;)zn(t),t=t.next}let vt=new Set;const me=new Map;let Un=!1;function Se(e,t){var n={f:0,v:e,reactions:null,equals:An,rv:0,wv:0};return n}function ie(e,t){const n=Se(e);return er(n),n}function wi(e,t=!1,n=!0){const r=Se(e);return t||(r.equals=Mn),r}function ae(e,t,n=!1){m!==null&&(!Z||(m.f&ct)!==0)&&kn()&&(m.f&(C|X|xe|ct))!==0&&(te===null||!te.has(e))&&Hr();let r=n?ke(t):t;return Le(e,r,at)}function Le(e,t,n=null){if(!e.equals(t)){me.set(e,ue?t:e.v);var r=_e.ensure();if(r.capture(e,t),(e.f&C)!==0){const i=e;(e.f&M)!==0&&Bt(i),K===null&&qt(i)}e.wv=nr(),qn(e,M,n),w!==null&&(w.f&N)!==0&&(w.f&(G|oe))===0&&(U===null?Ci([e]):U.push(e)),!r.is_fork&&vt.size>0&&!Un&&mi()}return t}function mi(){Un=!1;for(const e of vt){(e.f&N)!==0&&A(e,W);let t;try{t=et(e)}catch{t=!0}t&&Fe(e)}vt.clear()}function Ke(e){ae(e,e.v+1)}function qn(e,t,n){var r=e.reactions;if(r!==null)for(var i=r.length,s=0;s{if(Ee===s)return l();var a=m,o=Ee;V(null),cn(s);var c=l();return V(a),cn(o),c};return r&&n.set("length",ie(e.length)),new Proxy(e,{defineProperty(l,a,o){(!("value"in o)||o.configurable===!1||o.enumerable===!1||o.writable===!1)&&Fr();var c=n.get(a);return c===void 0?f(()=>{var d=ie(o.value);return n.set(a,d),d}):ae(c,o.value,!0),!0},deleteProperty(l,a){var o=n.get(a);if(o===void 0){if(a in l){const c=f(()=>ie(O));n.set(a,c),Ke(i)}}else ae(o,O),Ke(i);return!0},get(l,a,o){if(a===we)return e;var c=n.get(a),d=a in l;if(c===void 0&&(!d||Ce(l,a)?.writable)&&(c=f(()=>{var h=ke(d?l[a]:O),v=ie(h);return v}),n.set(a,c)),c!==void 0){var u=T(c);return u===O?void 0:u}return Reflect.get(l,a,o)},getOwnPropertyDescriptor(l,a){var o=Reflect.getOwnPropertyDescriptor(l,a);if(o&&"value"in o){var c=n.get(a);c&&(o.value=T(c))}else if(o===void 0){var d=n.get(a),u=d?.v;if(d!==void 0&&u!==O)return{enumerable:!0,configurable:!0,value:u,writable:!0}}return o},has(l,a){if(a===we)return!0;var o=n.get(a),c=o!==void 0&&o.v!==O||Reflect.has(l,a);if(o!==void 0||w!==null&&(!c||Ce(l,a)?.writable)){o===void 0&&(o=f(()=>{var u=c?ke(l[a]):O,h=ie(u);return h}),n.set(a,o));var d=T(o);if(d===O)return!1}return c},set(l,a,o,c){var d=n.get(a),u=a in l;if(r&&a==="length")for(var h=o;hie(O)),n.set(h+"",v))}if(d===void 0)(!u||Ce(l,a)?.writable)&&(d=f(()=>ie(void 0)),ae(d,ke(o)),n.set(a,d));else{u=d.v!==O;var p=f(()=>ke(o));ae(d,p)}var _=Reflect.getOwnPropertyDescriptor(l,a);if(_?.set&&_.set.call(c,o),!u){if(r&&typeof a=="string"){var g=n.get("length"),S=Number(a);Number.isInteger(S)&&S>=g.v&&ae(g,S+1)}Ke(i)}return!0},ownKeys(l){T(i);var a=Reflect.ownKeys(l).filter(d=>{var u=n.get(d);return u===void 0||u.v!==O});for(var[o,c]of n)c.v!==O&&!(o in l)&&a.push(o);return a},setPrototypeOf(){jr()}})}function ln(e){try{if(e!==null&&typeof e=="object"&&we in e)return e[we]}catch{}return e}function Fs(e,t){return Object.is(ln(e),ln(t))}var on,Bn,Gn,Vn;function bi(){if(on===void 0){on=window,Bn=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;Gn=Ce(t,"firstChild").get,Vn=Ce(t,"nextSibling").get,rn(e)&&(e[xt]=void 0,e[Sn]=null,e[Pt]=void 0,e.__e=void 0),rn(n)&&(n[Rt]=void 0)}}function le(e=""){return document.createTextNode(e)}function De(e){return Gn.call(e)}function $e(e){return Vn.call(e)}function Q(e,t){return De(e)}function js(e,t=!1){{var n=De(e);return n instanceof Comment&&n.data===""?$e(n):n}}function it(e,t=1,n=!1){let r=e;for(;t--;)r=$e(r);return r}function Ei(e){e.textContent=""}function Yn(){return!1}function yi(e,t,n){return n?document.createElement(e,{is:n}):document.createElement(e)}function Si(e){w===null&&(m===null&&Ir(),Rr()),ue&&Pr()}function Ti(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function ce(e,t){var n=w;n!==null&&(n.f&R)!==0&&(e|=R);var r={ctx:I,deps:null,nodes:null,f:e|M|B,first:null,fn:t,last:null,next:null,parent:n,b:n&&n.b,prev:null,teardown:null,wv:0,ac:null};b?.register_created_effect(r);var i=r;if((e&Pe)!==0)Ne!==null?Ne.push(r):_e.ensure().schedule(r);else if(t!==null){try{Fe(r)}catch(f){throw D(r),f}i.deps===null&&i.teardown===null&&i.nodes===null&&i.first===i.last&&(i.f&He)===0&&(i=i.first,(e&X)!==0&&(e&Re)!==0&&i!==null&&(i.f|=Re))}if(i!==null&&(i.parent=n,n!==null&&Ti(i,n),m!==null&&(m.f&C)!==0&&(e&oe)===0)){var s=m;(s.effects??=[]).push(i)}return r}function Yt(){return m!==null&&!Z}function Xn(e){const t=ce(gt,null);return A(t,N),t.teardown=e,t}function Hs(e){Si();var t=w.f,n=!m&&(t&G)!==0&&I!==null&&!I.i;if(n){var r=I;(r.e??=[]).push(e)}else return Kn(e)}function Kn(e){return ce(Pe|Mr,e)}function Ai(e){_e.ensure();const t=ce(oe|He,e);return(n={})=>new Promise(r=>{n.outro?be(t,()=>{D(t),r(void 0)}):(D(t),r(void 0))})}function Mi(e){return ce(Pe,e)}function Oi(e){return ce(xe|He,e)}function Zn(e,t=0){return ce(gt|t,e)}function Ge(e,t=[],n=[],r=[]){ui(r,t,n,i=>{ce(gt,()=>{e(...i.map(T))})})}function Xt(e,t=0){var n=ce(X|t,e);return n}function q(e){return ce(G|He,e)}function Wn(e){var t=e.teardown;if(t!==null){const n=ue,r=m;un(!0),V(null);try{t.call(null)}finally{un(n),V(r)}}}function Kt(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){const i=n.ac;i!==null&&ze(()=>{i.abort(Qe)});var r=n.next;(n.f&oe)!==0?n.parent=null:D(n,t),n=r}}function Ni(e){for(var t=e.first;t!==null;){var n=t.next;(t.f&G)===0&&D(t),t=n}}function D(e,t=!0){var n=!1;(t||(e.f&Ar)!==0)&&e.nodes!==null&&e.nodes.end!==null&&(ki(e.nodes.start,e.nodes.end),n=!0),e.f|=Ct,Kt(e,t&&!n),We(e,0);var r=e.nodes&&e.nodes.t;if(r!==null)for(const s of r)s.stop();Wn(e),e.f^=Ct,e.f|=H;var i=e.parent;i!==null&&i.first!==null&&Jn(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function ki(e,t){for(;e!==null;){var n=e===t?null:$e(e);e.remove(),e=n}}function Jn(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function be(e,t,n=!0){var r=[];Qn(e,r,!0);var i=()=>{n&&D(e),t&&t()},s=r.length;if(s>0){var f=()=>--s||i();for(var l of r)l.out(f)}else i()}function Qn(e,t,n){if((e.f&R)===0){e.f^=R;var r=e.nodes&&e.nodes.t;if(r!==null)for(const l of r)(l.is_global||n)&&t.push(l);for(var i=e.first;i!==null;){var s=i.next;if((i.f&oe)===0){var f=(i.f&Re)!==0||(i.f&G)!==0&&(e.f&X)!==0;Qn(i,t,f?n:!1)}i=s}}}function _t(e){$n(e,!0)}function $n(e,t){if((e.f&R)!==0){e.f^=R,(e.f&N)===0&&(A(e,M),_e.ensure().schedule(e));for(var n=e.first;n!==null;){var r=n.next,i=(n.f&Re)!==0||(n.f&G)!==0;$n(n,i?t:!1),n=r}var s=e.nodes&&e.nodes.t;if(s!==null)for(const f of s)(f.is_global||t)&&f.in()}}function Zt(e,t){if(e.nodes)for(var n=e.nodes.start,r=e.nodes.end;n!==null;){var i=n===r?null:$e(n);t.append(n),n=i}}let lt=!1,ue=!1;function un(e){ue=e}let m=null,Z=!1;function V(e){m=e}let w=null;function ne(e){w=e}let te=null;function er(e){m!==null&&(te??=new Set).add(e)}let L=null,j=0,U=null;function Ci(e){U=e}let tr=1,ge=0,Ee=ge;function cn(e){Ee=e}function nr(){return++tr}function et(e){var t=e.f;if((t&M)!==0)return!0;if(t&C&&(e.f&=~ye),(t&W)!==0){for(var n=e.deps,r=n.length,i=0;ie.wv)return!0}(t&B)!==0&&K===null&&A(e,N)}return!1}function rr(e,t,n=!0){var r=e.reactions;if(r!==null&&!(te!==null&&te.has(e)))for(var i=0;i{e.ac.abort(Qe)}),e.ac=null);try{e.f|=dt;var c=e.fn,d=c();e.f|=je;var u=e.deps,h=b?.is_fork;if(L!==null){var v;if(h||We(e,j),u!==null&&j>0)for(u.length=j+L.length,v=0;v{s.ac.abort(Qe),s.ac=null,A(s,M)}),vi(s),We(s,0)}}function We(e,t){var n=e.deps;if(n!==null)for(var r=t;rn?.call(this,s))}return e.startsWith("pointer")||e.startsWith("touch")||e==="wheel"?ee(()=>{t.addEventListener(e,i,r)}):t.addEventListener(e,i,r),i}function Li(e,t,n,r,i){var s={capture:r,passive:i},f=Ii(e,t,n,s);(t===document.body||t===window||t===document||t instanceof HTMLMediaElement)&&Xn(()=>{t.removeEventListener(e,f,s)})}function Di(e,t,n){(t[Ve]??={})[e]=n}function Fi(e){for(var t=0;t{throw p});throw u}}finally{e[Ve]=t,delete e.currentTarget,V(c),ne(d)}}}const ji=globalThis?.window?.trustedTypes&&globalThis.window.trustedTypes.createPolicy("svelte-trusted-html",{createHTML:e=>e});function Hi(e){return ji?.createHTML(e)??e}function lr(e){var t=yi("template");return t.innerHTML=Hi(e.replaceAll("","")),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-BoOKWXIu.js b/internal/web/dist/assets/index-BoOKWXIu.js new file mode 100644 index 0000000..9872a39 --- /dev/null +++ b/internal/web/dist/assets/index-BoOKWXIu.js @@ -0,0 +1,2 @@ +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/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-D3UhzFzr.js b/internal/web/dist/assets/mount-D3UhzFzr.js new file mode 100644 index 0000000..3f6541e --- /dev/null +++ b/internal/web/dist/assets/mount-D3UhzFzr.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":"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 + 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/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/index.html b/internal/web/dist/index.html index f6e00b1..1f104c9 100644 --- a/internal/web/dist/index.html +++ b/internal/web/dist/index.html @@ -6,8 +6,8 @@ Pesée - - + + 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 } diff --git a/web/src/admin/lib/fields.ts b/web/src/admin/lib/fields.ts index 5eed93a..5f28d1a 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': '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 e2d7336..3711596 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. +

                                                                        +