@mostajs/wagon-ui 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/llms.txt +2 -1
- package/package.json +4 -1
- package/src/i18n/ar.js +19 -4
- package/src/i18n/en.js +7 -4
- package/src/i18n/fr.js +8 -5
- package/src/index.js +36 -14
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,26 @@
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
|
|
8
|
+
## 0.4.0 — 2026-10-08
|
|
9
|
+
|
|
10
|
+
- **Les textes passent par `@mostajs/i18n`** (≥ 0.2.0) au lieu d'un traducteur maison : même ordre de résolution
|
|
11
|
+
(surcharges `textes` de l'application, catalogue de la langue, français), même API `ui.t(lang)(cle, vars)`.
|
|
12
|
+
- **Pluriels CLDR** : `{n}` sert de compte. Fini « clé(s) active(s) », « restant(e)s » et la paire
|
|
13
|
+
`cles`/`clesPluriel` ; l'arabe a ses six formes — « بمفتاحين » pour deux, « 11 مفتاحًا » de 11 à 99, au lieu de
|
|
14
|
+
« مفتاح/مفاتيح ». Clés touchées : `tarifs.cles` (+ `tarifs.clesIllimitees`), `espace.appels`,
|
|
15
|
+
`espace.plafondAtteint`, `espace.restant` (« Reste : {n} »). ⚠️ Une application qui surchargeait `tarifs.clesPluriel`
|
|
16
|
+
dans `textes` doit passer à `tarifs.cles_other`.
|
|
17
|
+
- **Prix formatés dans la langue** : « 9,99 € », « €9.99 », et en arabe `ar-DZ` aux chiffres latins — au lieu de
|
|
18
|
+
« 9.99 € » avec un point partout. `ui.euros(centimes, lang)` exposé (pour les suites de conformité).
|
|
19
|
+
- Essais T-WUI-16 (catalogues vérifiés par `checkCatalogues`, clés construites `legal.*` comprises), T-WUI-17
|
|
20
|
+
(pluriels, duel arabe), T-WUI-18 (prix) ; T-WUI-7 compare désormais les clés LOGIQUES. Trois sabotages, détectés.
|
|
21
|
+
|
|
22
|
+
## 0.3.1 — 2026-10-02
|
|
23
|
+
|
|
24
|
+
- **Correctif** : l'en-tête `entete: 'barre'` portait la classe `barre`, déjà prise par la jauge de consommation
|
|
25
|
+
(`.barre{width:9em;height:.55em;border-radius:99px}`) — l'en-tête s'affichait en pilule tronquée. Il porte
|
|
26
|
+
désormais la classe `bande` ; T-WUI-15 vérifie qu'aucune autre règle ne vise la classe de l'en-tête.
|
|
7
27
|
## 0.3.0 — 2026-10-02
|
|
8
28
|
|
|
9
29
|
- **Palette Studio** (palette.amia.fr) : les jetons portent désormais les noms de la palette (`--brand`, `--link`,
|
package/llms.txt
CHANGED
|
@@ -20,12 +20,13 @@ Rendre les écrans communs à tout SaaS, identiques d'un produit à l'autre, san
|
|
|
20
20
|
- `ui.adminPaliers({ lang, offres, message? })` — champs `<slug>.<limite>` et `<slug>.maxCles` (POST /admin/plans).
|
|
21
21
|
- `ui.adminUsage({ lang, usage: { jours, totaux: { comptes, actifs, payants, erreurs, parRessource }, lignes: [{ nom, plan, actif, dernierJour, erreursFenetre, parRessource: { <cle>: { courant, limite, part, fenetre } } }] }, anonyme? })`
|
|
22
22
|
- `ui.legal({ lang, doc: 'mentions-legales'|'cgu'|'cgv'|'confidentialite', html })` · `ui.introuvable({ lang })`
|
|
23
|
-
- `ui.t(lang)(cle, vars)` · `CATALOGUES` · `dirDe(lang)` · `esc`
|
|
23
|
+
- `ui.t(lang)(cle, vars)` (par @mostajs/i18n : `{n}` = compte, pluriels CLDR, six formes en arabe) · `ui.euros(centimes, lang)` · `CATALOGUES` · `dirDe(lang)` · `esc`
|
|
24
24
|
|
|
25
25
|
## ROUTES ATTENDUES (liens et formulaires des vues)
|
|
26
26
|
`/`, `/tarifs`, `/aide`, `/apropos`, `/inscription` (POST : plan, nom, courriel, motdepasse), `/connexion` (POST : courriel, motdepasse), `/deconnexion`, `/compte`, `/compte/cles` (POST : libelle), `/compte/cles/revoquer` (POST : prefix), `/compte/portail` (POST), `/paiement` (POST : compte, plan), `/admin/plans` (POST), `/admin/usage`, `/mentions-legales`, `/cgu`, `/cgv`, `/confidentialite`.
|
|
27
27
|
|
|
28
28
|
## PIÈGES
|
|
29
|
+
- 0.4.0 : `tarifs.clesPluriel` n'existe plus → `tarifs.cles_one` / `tarifs.cles_other` (+ formes arabes) ; prix formatés par langue (« 9,99 € »).
|
|
29
30
|
- `legal({ html })` n'échappe pas `html` : il doit venir des textes de l'éditeur (mostajs/templates), jamais d'un formulaire.
|
|
30
31
|
- Les prix sont en CENTIMES. `-1` = sans limite ; `0` = non compris.
|
|
31
32
|
- Une ressource sans `parPeriode` s'affiche « libellé : n ».
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mostajs/wagon-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Les pages du wagon d'un SaaS — mise en page (langues, pied légal, en-têtes pour les moteurs), accueil, tarifs, inscription, connexion (mot de passe ou code), espace client (palier, jauges, clés, factures, gestion de l'abonnement), administration (paliers, usage), pages légales — en fr, en, ar. Extraites de mdseal, sans ce qu'il vend : l'identité et les ressources mesurées viennent de la configuration. Vues pures, zéro dépendance.",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"author": "Dr Hamid MADANI <drmdh@msn.com>",
|
|
@@ -68,5 +68,8 @@
|
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
70
|
"@mostajs/mjs-unit": "^0.7.0"
|
|
71
|
+
},
|
|
72
|
+
"dependencies": {
|
|
73
|
+
"@mostajs/i18n": "^0.2.0"
|
|
71
74
|
}
|
|
72
75
|
}
|
package/src/i18n/ar.js
CHANGED
|
@@ -26,8 +26,13 @@ export const ar = {
|
|
|
26
26
|
"titre": "الأسعار",
|
|
27
27
|
"parMois": "/شهريًّا",
|
|
28
28
|
"gratuit": "مجّاني",
|
|
29
|
-
"
|
|
30
|
-
"
|
|
29
|
+
"cles_zero": "بلا مفتاح واجهة",
|
|
30
|
+
"cles_one": "مفتاح واجهة واحد",
|
|
31
|
+
"cles_two": "مفتاحا واجهة",
|
|
32
|
+
"cles_few": "{n} مفاتيح واجهة",
|
|
33
|
+
"cles_many": "{n} مفتاحًا للواجهة",
|
|
34
|
+
"cles_other": "{n} مفتاح واجهة",
|
|
35
|
+
"clesIllimitees": "مفاتيح واجهة بلا حدّ",
|
|
31
36
|
"choisir": "اختر هذه الخطّة",
|
|
32
37
|
"commencer": "ابدأ مجّانًا",
|
|
33
38
|
"encours": "الدفع غير متاح حاليًّا",
|
|
@@ -76,8 +81,18 @@ export const ar = {
|
|
|
76
81
|
"revoquer": "إلغاء",
|
|
77
82
|
"revoquee": "مُلغًى",
|
|
78
83
|
"jamaisUtilisee": "لم يُستعمَل قطّ",
|
|
79
|
-
"
|
|
80
|
-
"
|
|
84
|
+
"appels_zero": "لا نداءات",
|
|
85
|
+
"appels_one": "نداء واحد",
|
|
86
|
+
"appels_two": "نداءان",
|
|
87
|
+
"appels_few": "{n} نداءات",
|
|
88
|
+
"appels_many": "{n} نداءً",
|
|
89
|
+
"appels_other": "{n} نداء",
|
|
90
|
+
"plafondAtteint_zero": "لا تسمح خطّتك بأيّ مفتاح نشِط.",
|
|
91
|
+
"plafondAtteint_one": "تسمح خطّتك بمفتاح نشِط واحد.",
|
|
92
|
+
"plafondAtteint_two": "تسمح خطّتك بمفتاحين نشِطين.",
|
|
93
|
+
"plafondAtteint_few": "تسمح خطّتك بـ {n} مفاتيح نشِطة.",
|
|
94
|
+
"plafondAtteint_many": "تسمح خطّتك بـ {n} مفتاحًا نشِطًا.",
|
|
95
|
+
"plafondAtteint_other": "تسمح خطّتك بـ {n} مفتاح نشِط.",
|
|
81
96
|
"cleCreee": "أُنشئ المفتاح. انسخه: لن يُعرَض مرّة أخرى.",
|
|
82
97
|
"gererAbonnement": "إدارة اشتراكي",
|
|
83
98
|
"gererAide": "وسيلة الدفع والفواتير والإلغاء — لدى Stripe.",
|
package/src/i18n/en.js
CHANGED
|
@@ -26,8 +26,9 @@ export const en = {
|
|
|
26
26
|
"titre": "Pricing",
|
|
27
27
|
"parMois": "/month",
|
|
28
28
|
"gratuit": "Free",
|
|
29
|
-
"
|
|
30
|
-
"
|
|
29
|
+
"cles_one": "{n} API key",
|
|
30
|
+
"cles_other": "{n} API keys",
|
|
31
|
+
"clesIllimitees": "Unlimited API keys",
|
|
31
32
|
"choisir": "Choose this plan",
|
|
32
33
|
"commencer": "Start for free",
|
|
33
34
|
"encours": "Payment is not available yet",
|
|
@@ -76,8 +77,10 @@ export const en = {
|
|
|
76
77
|
"revoquer": "Revoke",
|
|
77
78
|
"revoquee": "Revoked",
|
|
78
79
|
"jamaisUtilisee": "never used",
|
|
79
|
-
"
|
|
80
|
-
"
|
|
80
|
+
"appels_one": "{n} call",
|
|
81
|
+
"appels_other": "{n} calls",
|
|
82
|
+
"plafondAtteint_one": "Your plan allows {n} active key.",
|
|
83
|
+
"plafondAtteint_other": "Your plan allows {n} active keys.",
|
|
81
84
|
"cleCreee": "Key created. Copy it: it will not be shown again.",
|
|
82
85
|
"gererAbonnement": "Manage my subscription",
|
|
83
86
|
"gererAide": "Payment method, invoices, cancellation — at Stripe.",
|
package/src/i18n/fr.js
CHANGED
|
@@ -26,8 +26,9 @@ export const fr = {
|
|
|
26
26
|
"titre": "Tarifs",
|
|
27
27
|
"parMois": "/mois",
|
|
28
28
|
"gratuit": "Gratuit",
|
|
29
|
-
"
|
|
30
|
-
"
|
|
29
|
+
"cles_one": "{n} clé d’API",
|
|
30
|
+
"cles_other": "{n} clés d’API",
|
|
31
|
+
"clesIllimitees": "Clés d’API sans limite",
|
|
31
32
|
"choisir": "Choisir ce plan",
|
|
32
33
|
"commencer": "Commencer gratuitement",
|
|
33
34
|
"encours": "Paiement indisponible pour l’instant",
|
|
@@ -68,7 +69,7 @@ export const fr = {
|
|
|
68
69
|
"votrePlan": "Votre plan",
|
|
69
70
|
"consommationDuJour": "Aujourd’hui",
|
|
70
71
|
"consommationDuMois": "Ce mois-ci",
|
|
71
|
-
"restant": "{n}
|
|
72
|
+
"restant": "Reste : {n}",
|
|
72
73
|
"illimite": "sans limite",
|
|
73
74
|
"vosCles": "Vos clés d’API",
|
|
74
75
|
"creerUneCle": "Créer une clé",
|
|
@@ -76,8 +77,10 @@ export const fr = {
|
|
|
76
77
|
"revoquer": "Révoquer",
|
|
77
78
|
"revoquee": "Révoquée",
|
|
78
79
|
"jamaisUtilisee": "jamais utilisée",
|
|
79
|
-
"
|
|
80
|
-
"
|
|
80
|
+
"appels_one": "{n} appel",
|
|
81
|
+
"appels_other": "{n} appels",
|
|
82
|
+
"plafondAtteint_one": "Votre plan permet {n} clé active.",
|
|
83
|
+
"plafondAtteint_other": "Votre plan permet {n} clés actives.",
|
|
81
84
|
"cleCreee": "Clé créée. Copiez-la : elle ne sera plus affichée.",
|
|
82
85
|
"gererAbonnement": "Gérer mon abonnement",
|
|
83
86
|
"gererAide": "Moyen de paiement, factures, résiliation — chez Stripe.",
|
package/src/index.js
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* impression) ; le code reste de gauche à droite dans une page arabe ; une clé n'est montrée
|
|
22
22
|
* qu'une fois ; la gestion de l'abonnement n'apparaît que si elle est possible.
|
|
23
23
|
*/
|
|
24
|
+
import { createI18n, formatNumber, resolveFormatLocale } from '@mostajs/i18n';
|
|
24
25
|
import { fr } from './i18n/fr.js';
|
|
25
26
|
import { en } from './i18n/en.js';
|
|
26
27
|
import { ar } from './i18n/ar.js';
|
|
@@ -37,7 +38,11 @@ const lien = (href) => {
|
|
|
37
38
|
return /^(\/(?!\/)|https?:\/\/|mailto:|tel:|#|\?)/i.test(h) ? esc(h) : '#';
|
|
38
39
|
};
|
|
39
40
|
const NBSP = ' ';
|
|
40
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Un prix en euros, formaté dans la langue (0.4.0) : « 9,99 € », « €9.99 », et en arabe la locale ar-DZ aux
|
|
43
|
+
* chiffres latins (@mostajs/i18n) — plus « 9.99 € » avec un point, en français comme ailleurs.
|
|
44
|
+
*/
|
|
45
|
+
const euros = (centimes, lang = 'fr') => formatNumber(resolveFormatLocale(lang), (Number(centimes) || 0) / 100, { style: 'currency', currency: 'EUR' });
|
|
41
46
|
|
|
42
47
|
// 0.3 : les jetons portent les NOMS de Palette Studio (palette.amia.fr) — un thème qui les redéfinit habille le site
|
|
43
48
|
// entier ; les anciens noms en dérivent par var(), donc le suivent. Contrastes des replis vérifiés sur blanc.
|
|
@@ -107,11 +112,11 @@ img,pre{max-width:100%} pre{overflow-x:auto}
|
|
|
107
112
|
.champs label{flex:1 1 100%}
|
|
108
113
|
.grille-tarifs{grid-template-columns:1fr}
|
|
109
114
|
}
|
|
110
|
-
header.
|
|
111
|
-
header.
|
|
112
|
-
header.
|
|
113
|
-
header.
|
|
114
|
-
header.
|
|
115
|
+
header.bande{max-width:none;background:var(--brand);border-bottom:0;padding:.8rem max(1rem,calc((100% - 920px)/2))}
|
|
116
|
+
header.bande .brand,header.bande nav a,header.bande .moi a,header.bande .langues a,header.bande .langues b,header.bande .langues{color:var(--text-on-brand)}
|
|
117
|
+
header.bande nav a{opacity:.9} header.bande nav a.on,header.bande nav a:hover{background:rgba(255,255,255,.16);opacity:1}
|
|
118
|
+
header.bande .burger{color:var(--text-on-brand);border-color:rgba(255,255,255,.4)}
|
|
119
|
+
header.bande + main{padding-top:1.2rem}`;
|
|
115
120
|
}
|
|
116
121
|
|
|
117
122
|
/** Un thème de Palette Studio n'est accepté que s'il ne fait que redéfinir des jetons dans :root{…}. */
|
|
@@ -166,11 +171,26 @@ export function creerWagonUi({ marque, couleurs = {}, langues = ['fr', 'en', 'ar
|
|
|
166
171
|
/** Le thème courant (Palette Studio), relu à chaque page ; rejeté s'il sort de :root{…}. */
|
|
167
172
|
const themeCss = () => { try { const t = typeof theme === 'function' ? String(theme() ?? '') : ''; return THEME_SAIN.test(t) ? t : ''; } catch { return ''; } };
|
|
168
173
|
|
|
169
|
-
/**
|
|
174
|
+
/**
|
|
175
|
+
* Le traducteur d'une langue, par @mostajs/i18n (0.4.0) : surcharges de l'application, puis le catalogue, puis
|
|
176
|
+
* le français. Pluriels CLDR — `{n}` sert de compte : « 1 clé active », « 3 clés actives », et en arabe les six
|
|
177
|
+
* formes (« بمفتاحين » pour deux). Une clé absente se rend telle quelle, comme avant.
|
|
178
|
+
*/
|
|
179
|
+
const i18n = Object.fromEntries(LANGUES.map((l) => [l, createI18n({
|
|
180
|
+
locale: l, fallback: 'fr',
|
|
181
|
+
bundles: [
|
|
182
|
+
{ locale: 'fr', namespace: 'wagon', messages: CATALOGUES.fr },
|
|
183
|
+
...(l === 'fr' ? [] : [{ locale: l, namespace: 'wagon', messages: CATALOGUES[l] }]),
|
|
184
|
+
...['fr', l].filter((x, k, a) => a.indexOf(x) === k && textes[x]).map((x) => ({ locale: x, namespace: 'wagon', messages: textes[x] })),
|
|
185
|
+
],
|
|
186
|
+
})]));
|
|
170
187
|
function t(lang) {
|
|
171
188
|
const L = LANGUES.includes(lang) ? lang : LANGUES[0];
|
|
172
|
-
|
|
173
|
-
|
|
189
|
+
return (cle, vars) => {
|
|
190
|
+
const v = vars && typeof vars.n === 'number' && vars.count === undefined ? { ...vars, count: vars.n } : vars;
|
|
191
|
+
const r = i18n[L].t(`wagon:${cle}`, v);
|
|
192
|
+
return r === `wagon:${cle}` ? cle : r;
|
|
193
|
+
};
|
|
174
194
|
}
|
|
175
195
|
const langueDe = (lang) => (LANGUES.includes(lang) ? lang : LANGUES[0]);
|
|
176
196
|
|
|
@@ -184,6 +204,8 @@ export function creerWagonUi({ marque, couleurs = {}, langues = ['fr', 'en', 'ar
|
|
|
184
204
|
|
|
185
205
|
const ui = {
|
|
186
206
|
t, dir: dirDe, langues: LANGUES, style: STYLE,
|
|
207
|
+
/** Un prix en euros dans la langue : `ui.euros(999, 'fr')` → « 9,99 € ». */
|
|
208
|
+
euros: (centimes, lang) => euros(centimes, langueDe(lang)),
|
|
187
209
|
|
|
188
210
|
/**
|
|
189
211
|
* La page entière. `contenu` est du HTML déjà rendu (par une vue de ce module, ou par la charge
|
|
@@ -206,7 +228,7 @@ export function creerWagonUi({ marque, couleurs = {}, langues = ['fr', 'en', 'ar
|
|
|
206
228
|
return `<!doctype html><html lang="${L}" dir="${dirDe(L)}"><head><meta charset="utf-8">
|
|
207
229
|
<meta name="viewport" content="width=device-width, initial-scale=1"><title>${esc(titre)} — ${nom}</title>
|
|
208
230
|
${tete}<style>${STYLE}</style>${themeCss() ? `<style id="theme-palette">${themeCss()}</style>` : ''}</head><body>
|
|
209
|
-
<header${entete === 'barre' ? ' class="
|
|
231
|
+
<header${entete === 'barre' ? ' class="bande"' : ''}><a class="brand" href="/">${marque.symbole ? `${esc(marque.symbole)} ` : ''}${nom}</a><input type="checkbox" id="wagon-menu" class="bascule"><label for="wagon-menu" class="burger" aria-label="${esc(tr('layout.menu'))}">☰</label><div class="menu"><nav>${nav.map(([h, l]) => `<a href="${lien(h)}"${actif === h ? ' class="on"' : ''}>${esc(l)}</a>`).join('')}</nav><span class="moi"><a href="${connecte ? '/compte' : '/connexion'}">${esc(tr(connecte ? 'espace.compte' : 'espace.seConnecter'))}</a>${connecte ? ` · <a href="/deconnexion">${esc(tr('espace.seDeconnecter'))}</a>` : ''}</span>${choixLangue}</div></header>
|
|
210
232
|
<main>${contenu}</main>
|
|
211
233
|
<footer><b>${nom}</b>${slogan ? ` — ${esc(slogan)}` : ''} · ${esc(tr('layout.auteur'))} : <b>${esc(marque.auteur)}</b>${marque.courriel ? ` <${esc(marque.courriel)}>` : ''} · ${esc(marque.licence ?? 'AGPL-3.0')} · ${esc(tr('layout.propulse'))} <code>@mostajs/*</code><br><span class="legal">${DOCUMENTS_LEGAUX.map(([h, k]) => `<a href="/${h}">${esc(tr(`legal.${k}`))}</a>`).join(' · ')}</span></footer>
|
|
212
234
|
</body></html>`;
|
|
@@ -236,10 +258,10 @@ ${paliers.filter((p) => p.active !== false).map((p) => {
|
|
|
236
258
|
const cles = p.limits?.maxCles;
|
|
237
259
|
return `<section class="plan">
|
|
238
260
|
<h2>${esc(p.name)}</h2>
|
|
239
|
-
<p class="prix">${gratuit ? esc(tr('tarifs.gratuit')) : `<b>${esc(euros(p.price))}</b><span class="hint">${esc(tr('tarifs.parMois'))}</span>`}</p>
|
|
261
|
+
<p class="prix">${gratuit ? esc(tr('tarifs.gratuit')) : `<b>${esc(euros(p.price, L))}</b><span class="hint">${esc(tr('tarifs.parMois'))}</span>`}</p>
|
|
240
262
|
<ul>
|
|
241
263
|
${ressources.map((r) => `<li>${esc(ligneRessource(r, p.limits?.[r.limite], L))}</li>`).join('\n ')}
|
|
242
|
-
${cles !== undefined ? `<li>${esc(Number(cles) === -1 ?
|
|
264
|
+
${cles !== undefined ? `<li>${esc(Number(cles) === -1 ? tr('tarifs.clesIllimitees') : tr('tarifs.cles', { n: Number(cles) }))}</li>` : ''}
|
|
243
265
|
</ul>
|
|
244
266
|
<p><a class="bouton" href="${lien(`${inscriptionHref}?plan=${encodeURIComponent(p.slug)}`)}">${esc(tr(gratuit ? 'tarifs.commencer' : 'tarifs.choisir'))}</a></p>
|
|
245
267
|
</section>`;
|
|
@@ -258,7 +280,7 @@ ${erreur ? `<p class="msg">${esc(erreur)}</p>` : ''}
|
|
|
258
280
|
<form method="post" action="${lien(action)}">
|
|
259
281
|
<input type="hidden" name="plan" value="${esc(palier?.slug ?? '')}">
|
|
260
282
|
<fieldset><legend>${esc(tr('inscription.plan'))}</legend>
|
|
261
|
-
<p><b>${esc(palier?.name ?? '')}</b> — ${gratuit ? esc(tr('tarifs.gratuit')) : `${esc(euros(palier.price))}${esc(tr('tarifs.parMois'))}`}</p>
|
|
283
|
+
<p><b>${esc(palier?.name ?? '')}</b> — ${gratuit ? esc(tr('tarifs.gratuit')) : `${esc(euros(palier.price, L))}${esc(tr('tarifs.parMois'))}`}</p>
|
|
262
284
|
</fieldset>
|
|
263
285
|
<div class="champs">
|
|
264
286
|
<label>${esc(tr('inscription.nom'))}<input type="text" name="nom" required maxlength="80"></label>
|
|
@@ -335,7 +357,7 @@ ${erreur ? `<p class="msg">${esc(erreur)}</p>` : ''}
|
|
|
335
357
|
${erreur ? `<p class="msg">${esc(erreur)}</p>` : ''}
|
|
336
358
|
${cleNeuve ? `<p class="ok">${esc(tr('espace.cleCreee'))}</p><pre><code>${esc(cleNeuve)}</code></pre>` : ''}
|
|
337
359
|
<h2>${esc(tr('espace.votrePlan'))}</h2>
|
|
338
|
-
<p><b>${esc(v.plan?.name ?? '—')}</b>${Number(v.plan?.price) ? ` — ${esc(euros(v.plan.price))}${esc(tr('tarifs.parMois'))}` : ''}</p>
|
|
360
|
+
<p><b>${esc(v.plan?.name ?? '—')}</b>${Number(v.plan?.price) ? ` — ${esc(euros(v.plan.price, L))}${esc(tr('tarifs.parMois'))}` : ''}</p>
|
|
339
361
|
<ul class="jauges">
|
|
340
362
|
${ressources.map(jauge).join('\n ')}
|
|
341
363
|
</ul>
|