2024-12-12

This commit is contained in:
gcch 2024-12-12 10:36:51 +01:00
commit 0c39c95625
45 changed files with 827 additions and 319 deletions

View file

@ -66,7 +66,7 @@
);
--contenu-page-hauteur-minimale-avec-categories: calc(
100svh - var(--en-tete-hauteur) - var(--pied-de-page-hauteur)
- var(--menu-categories-produits-hauteur)
- var(--menu-categories-produits-hauteur)
);
/* Espacements */
--espace-xs: 0.25rem;
@ -248,8 +248,8 @@ button.bouton-retour-haut {
background: var(--couleur-fond);
border: 1px solid var(--couleur-noir);
border-radius: 100%;
transition: 0.2s background, 0.2s opacity, 0.2s visibility;
box-shadow: initial;
transition: 0.2s background, 0.2s opacity, 0.2s visibility;
}
button.bouton-retour-haut img {
width: 1rem;
@ -267,8 +267,17 @@ button.bouton-retour-haut[data-actif] {
}
}
fieldset {
all: initial;
display: flex;
flex-flow: column nowrap;
margin-top: var(--espace-l);
font: inherit;
}
input, select, textarea {
padding: var(--espace-xs);
accent-color: var(--couleur-jaune);
background: var(--couleur-gris);
border: 1px solid var(--couleur-noir);
outline: 2px solid transparent;
@ -340,6 +349,25 @@ label:has(~ input[type=checkbox], ~ input[type=radio]), input[type=checkbox] + l
}
}
input[type=radio] {
appearance: initial;
border-radius: 100%;
}
input[type=radio]:checked {
display: inline-flex;
place-content: center;
place-items: center;
}
input[type=radio]:checked::before {
content: " ";
inset: initial;
display: inline-block;
width: calc(var(--espace-l) / 2);
height: calc(var(--espace-l) / 2);
background: var(--couleur-noir);
border-radius: 100%;
}
textarea:focus-visible, textarea:focus-within {
outline: 1px solid var(--couleur-noir);
}
@ -612,9 +640,9 @@ body:has(#menu-mobile:not([aria-hidden=true])) {
background: transparent;
}
#en-tete .logo img {
image-rendering: crisp-edges;
shape-rendering: geometricPrecision;
object-fit: contain;
image-rendering: crisp-edges;
shape-rendering: geometricprecision;
}
#en-tete .logo button {
display: block;
@ -1052,8 +1080,8 @@ body:has(#menu-mobile:not([aria-hidden=true])) {
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
padding: var(--espace-m);
margin-bottom: var(--espace-xl);
padding: var(--espace-m);
}
.grille-produits article figure figcaption h3 {
font-style: italic;
@ -1450,9 +1478,9 @@ body:has(#menu-mobile:not([aria-hidden=true])) {
max-width: 100vw;
height: var(--pied-de-page-hauteur);
padding: var(--espace-m);
font-size: 0.8rem;
background: var(--couleur-jaune);
border-top: 1px solid var(--couleur-noir);
font-size: 0.8rem;
}
#pied-de-page .zone-menu-navigation-secondaire {
justify-self: start;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["../../../src/sass/pages/page-a-propos.scss"],"names":[],"mappings":"AAEA;EAEE;EAGA;EACA;EAEA;EACA;EACA;;AAEA;EACE;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;;AAGA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAKJ;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;AACA;AACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAIA;AACE;;AAKN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;;AAIJ;EACE;;AAEA;EACE;;AAIJ;EACE;EACA;;AAGF;EACE;EAEA;;AAGF;EACE;EAEA;;AAEA;EACE;;AAIJ;EACE;EAEA;;AAGF;EACE;EAEA;;AAGF;EACE;EAEA;;AAEA;EACE;;AAMR;EA3LF;IA4LI;;;;AAIJ;EACE;IACE","file":"page-a-propos.css"}
{"version":3,"sourceRoot":"","sources":["../../../src/sass/pages/page-a-propos.scss"],"names":[],"mappings":"AAEA;EAEE;EAGA;EACA;EAEA;EACA;EACA;;AAEA;EACE;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;;AAGA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAKJ;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;AAEA;AAEA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAIA;AACE;;AAKN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;;AAIJ;EACE;;AAEA;EACE;;AAIJ;EACE;EACA;;AAGF;EACE;EAEA;;AAGF;EACE;EAEA;;AAEA;EACE;;AAIJ;EACE;EAEA;;AAGF;EACE;EAEA;;AAGF;EACE;EAEA;;AAEA;EACE;;AAMR;EA7LF;IA8LI;;;;AAIJ;EACE;IACE","file":"page-a-propos.css"}

View file

@ -46,12 +46,6 @@
.page-modele-simple .contenu__textuel p + p {
margin-top: var(--espace-m);
}
@media (width <= 50rem) {
.page-modele-simple .contenu {
border-right: initial;
border-left: initial;
}
}
.page-modele-simple#page-cgv .contenu {
font-style: normal;
}
@ -59,8 +53,8 @@
font-style: italic;
}
.page-modele-simple#page-cgv .contenu__textuel {
padding: 0;
max-width: initial;
padding: 0;
}
.page-modele-simple#page-cgv .contenu__textuel__section {
width: 100%;
@ -70,10 +64,10 @@
}
.page-modele-simple#page-cgv .contenu__textuel__section header {
width: 100%;
margin-bottom: var(--espace-l);
padding: var(--espace-m) var(--espace-xl);
border-top: 1px solid var(--couleur-noir);
border-bottom: 1px solid var(--couleur-noir);
margin-bottom: var(--espace-l);
}
.page-modele-simple#page-cgv .contenu__textuel__section header h3 {
width: fit-content;
@ -83,9 +77,9 @@
}
.page-modele-simple#page-cgv .contenu__textuel__section ul {
margin-bottom: 1lh;
padding: 0 var(--espace-xl);
list-style: square;
list-style-position: inside;
padding: 0 var(--espace-xl);
}
.page-modele-simple#page-cgv .contenu__textuel__section p {
padding: 0 var(--espace-xl);
@ -93,5 +87,11 @@
.page-modele-simple#page-cgv .contenu__textuel__section p:last-of-type {
margin-bottom: var(--espace-xl);
}
@media (width <= 50rem) {
.page-modele-simple .contenu {
border-right: initial;
border-left: initial;
}
}
/*# sourceMappingURL=page-modele-simple.css.map */

View file

@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["../../../src/sass/pages/page-modele-simple.scss"],"names":[],"mappings":"AAEA;EAEE;AAAA;AAAA;EAKA;EAEA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAKN;EACE;IACE;IACA;;;AAKF;EACE;;AAEA;EACE;;AAGF;EACE;EACA;;AAEA;EACE;;AAGE;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;EACA;;AAKF;EACE;;AAEA;EACE","file":"page-modele-simple.css"}
{"version":3,"sourceRoot":"","sources":["../../../src/sass/pages/page-modele-simple.scss"],"names":[],"mappings":"AAEA;EAEE;AAAA;AAAA;EAKA;EAEA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAMJ;EACE;;AAEA;EACE;;AAGF;EACE;EACA;;AAEA;EACE;;AAGE;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;EACA;;AAKF;EACE;;AAEA;EACE;;AAQZ;EACE;IACE;IACA","file":"page-modele-simple.css"}

View file

@ -1 +1 @@
.page-modele-simple{--page-hauteur-minimale:calc(100svh - var(--en-tete-hauteur) - var(--pied-de-page-hauteur) - var(--espace-xl) - 1px);--page-marges-bloc-debut:var(--en-tete-hauteur);margin-top:var(--page-marges-bloc-debut);margin-bottom:var(--espace-xl);border-bottom:1px solid var(--couleur-noir);flex-flow:column;display:flex}.page-modele-simple .contenu{width:min(50rem,100%);min-height:var(--page-hauteur-minimale);border:1px solid var(--couleur-noir);border-bottom:initial;flex-flow:column;place-items:center;margin:auto;font-style:italic;font-weight:400;display:flex}.page-modele-simple .contenu__en-tete{width:100%;padding:var(--espace-m)var(--espace-xl);color:var(--couleur-blanc);background:var(--couleur-noir)}.page-modele-simple .contenu__en-tete h2{text-transform:uppercase;width:fit-content;letter-spacing:var(--espacement-inter-lettres-etendu-l);margin:auto}.page-modele-simple .contenu__textuel{max-width:34rem;height:100%;padding:0 var(--espace-xl);text-wrap:pretty;flex-flow:column;flex:1;place-content:center;display:flex}.page-modele-simple .contenu__textuel p+p{margin-top:var(--espace-m)}@media (width<=50rem){.page-modele-simple .contenu{border-right:initial;border-left:initial}}.page-modele-simple#page-cgv .contenu{font-style:normal}.page-modele-simple#page-cgv .contenu header{font-style:italic}.page-modele-simple#page-cgv .contenu__textuel{max-width:initial;padding:0}.page-modele-simple#page-cgv .contenu__textuel__section{width:100%}.page-modele-simple#page-cgv .contenu__textuel__section:first-of-type header{border-top:initial}.page-modele-simple#page-cgv .contenu__textuel__section header{width:100%;padding:var(--espace-m)var(--espace-xl);border-top:1px solid var(--couleur-noir);border-bottom:1px solid var(--couleur-noir);margin-bottom:var(--espace-l)}.page-modele-simple#page-cgv .contenu__textuel__section header h3{text-transform:uppercase;width:fit-content;letter-spacing:var(--espacement-inter-lettres-etendu-l);margin:auto}.page-modele-simple#page-cgv .contenu__textuel__section ul{padding:0 var(--espace-xl);margin-bottom:1lh;list-style:square inside}.page-modele-simple#page-cgv .contenu__textuel__section p{padding:0 var(--espace-xl)}.page-modele-simple#page-cgv .contenu__textuel__section p:last-of-type{margin-bottom:var(--espace-xl)}
.page-modele-simple{--page-hauteur-minimale:calc(100svh - var(--en-tete-hauteur) - var(--pied-de-page-hauteur) - var(--espace-xl) - 1px);--page-marges-bloc-debut:var(--en-tete-hauteur);margin-top:var(--page-marges-bloc-debut);margin-bottom:var(--espace-xl);border-bottom:1px solid var(--couleur-noir);flex-flow:column;display:flex}.page-modele-simple .contenu{width:min(50rem,100%);min-height:var(--page-hauteur-minimale);border:1px solid var(--couleur-noir);border-bottom:initial;flex-flow:column;place-items:center;margin:auto;font-style:italic;font-weight:400;display:flex}.page-modele-simple .contenu__en-tete{width:100%;padding:var(--espace-m)var(--espace-xl);color:var(--couleur-blanc);background:var(--couleur-noir)}.page-modele-simple .contenu__en-tete h2{text-transform:uppercase;width:fit-content;letter-spacing:var(--espacement-inter-lettres-etendu-l);margin:auto}.page-modele-simple .contenu__textuel{max-width:34rem;height:100%;padding:0 var(--espace-xl);text-wrap:pretty;flex-flow:column;flex:1;place-content:center;display:flex}.page-modele-simple .contenu__textuel p+p{margin-top:var(--espace-m)}.page-modele-simple#page-cgv .contenu{font-style:normal}.page-modele-simple#page-cgv .contenu header{font-style:italic}.page-modele-simple#page-cgv .contenu__textuel{max-width:initial;padding:0}.page-modele-simple#page-cgv .contenu__textuel__section{width:100%}.page-modele-simple#page-cgv .contenu__textuel__section:first-of-type header{border-top:initial}.page-modele-simple#page-cgv .contenu__textuel__section header{width:100%;margin-bottom:var(--espace-l);padding:var(--espace-m)var(--espace-xl);border-top:1px solid var(--couleur-noir);border-bottom:1px solid var(--couleur-noir)}.page-modele-simple#page-cgv .contenu__textuel__section header h3{text-transform:uppercase;width:fit-content;letter-spacing:var(--espacement-inter-lettres-etendu-l);margin:auto}.page-modele-simple#page-cgv .contenu__textuel__section ul{padding:0 var(--espace-xl);margin-bottom:1lh;list-style:square inside}.page-modele-simple#page-cgv .contenu__textuel__section p{padding:0 var(--espace-xl)}.page-modele-simple#page-cgv .contenu__textuel__section p:last-of-type{margin-bottom:var(--espace-xl)}@media (width<=50rem){.page-modele-simple .contenu{border-right:initial;border-left:initial}}

View file

@ -144,7 +144,7 @@
margin-top: var(--espace-m);
}
#panneau-panier .panneau__sous-totaux {
width: min(30rem, 100%);
width: min(40rem, 100%);
margin: auto;
padding: var(--espace-l) var(--espace-xl);
text-align: center;
@ -165,6 +165,27 @@
#panneau-panier .panneau__sous-totaux__ligne#sous-total-livraison p:last-of-type span {
color: grey;
}
#panneau-panier .panneau__sous-totaux__choix-methode-livraison {
flex-flow: row wrap;
gap: var(--espace-xs) var(--espace-m);
justify-content: center;
font-size: 0.9rem;
letter-spacing: inherit;
}
#panneau-panier .panneau__sous-totaux__choix-methode-livraison > label:first-of-type {
cursor: revert;
flex-basis: 100%;
margin-bottom: var(--espace-xs);
color: grey;
text-align: center;
text-transform: lowercase;
}
#panneau-panier .panneau__sous-totaux__choix-methode-livraison div {
display: flex;
flex-flow: row nowrap;
column-gap: 1ch;
place-items: center;
}
#panneau-panier .panneau__sous-totaux__conditions-livraison {
margin-top: var(--espace-l);
font-size: 0.8rem;
@ -200,7 +221,8 @@
#panneau-panier .panneau__instructions-code-promo {
padding: var(--espace-xl) var(--espace-l);
}
#panneau-panier .panneau__instructions-code-promo .panneau__instructions-code-promo__code-promo input, #panneau-panier .panneau__instructions-code-promo .panneau__instructions-code-promo__code-promo button {
#panneau-panier .panneau__instructions-code-promo .panneau__instructions-code-promo__code-promo input,
#panneau-panier .panneau__instructions-code-promo .panneau__instructions-code-promo__code-promo button {
flex: 1;
}
#panneau-panier .panneau__sous-totaux {
@ -325,8 +347,8 @@
#panneau-informations-client .panneau__pied-de-page {
align-content: center;
padding: var(--espace-l) 0;
font-style: italic;
font-size: 1.25rem;
font-style: italic;
color: var(--couleur-blanc);
text-align: center;
text-transform: uppercase;

View file

@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["../../../src/sass/layouts/_panneau-panier.scss","../../../src/sass/layouts/_panneau-informations-client.scss","../../../src/sass/pages/page-panier.scss"],"names":[],"mappings":";AAEA;EACE;;AAEA;EACE;;AAIF;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGA;EACE;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAMN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAIJ;EACE;EACA;;AAIF;EACE;EACA;EACA;EACA;EACA;AAEA;AASA;AAAA;AAAA;AAAA;AAkBA;AASA;;AAnCA;EACE;EACA;EACA;EACA;EACA;;AAOF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAKJ;EACE;EACA;EACA;EACA;EACA;;AAIF;EACE;EACA;EACA;EACA;EACA;;AAOR;EACE;EACA;EACA;EACA;AAEA;AAAA;AAAA;AAAA;;AAIA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;AAEA;AAAA;AAAA;;AAGA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAKN;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAKA;EACE;;AAEA;EACE;;AAMR;EACE;EACA;EACA;EACA;EACA;;AAKJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;IACE;;EAGF;IACE;;EAEA;IACE;;EAIJ;IACE;;EAGE;IACE;;EAKN;IACE;;;;AC1PN;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAIF;EAEE;EACA;EACA;EAGA;EAEA;EACA;;AAEA;EACE;EACA;;AAIF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAMN;EACE;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAKF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGE;EACE;;AAMR;EACE;EACA;EACA;;AAIJ;EACE;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAKN;EACE;EACA;EACA;;AAEA;EACE;;AAIJ;EACE;;AAKJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAIJ;EACE;IACE;;;;ACpKN;AAAA;AAAA;AAAA;AAIA;AACE;EACA;AAEA;EACA;EAEA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAKN;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;;AAKF;EACE;;AAGF;EACE","file":"page-panier.css"}
{"version":3,"sourceRoot":"","sources":["../../../src/sass/layouts/_panneau-panier.scss","../../../src/sass/layouts/_panneau-informations-client.scss","../../../src/sass/pages/page-panier.scss"],"names":[],"mappings":";AAEA;EACE;;AAEA;EACE;;AAIF;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGA;EACE;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAMN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAIJ;EACE;EACA;;AAIF;EACE;EACA;EACA;EACA;EACA;AAEA;AASA;AAAA;AAAA;AAAA;AAmBA;AASA;;AApCA;EACE;EACA;EACA;EACA;EACA;;AAOF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEE;EACA;;AAKJ;EACE;EACA;EACA;EACA;EACA;;AAIF;EACE;EACA;EACA;EACA;EACA;;AAOR;EACE;EACA;EACA;EACA;AAEA;AAAA;AAAA;AAAA;;AAIA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;AAEA;AAAA;AAAA;;AAGA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAKN;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAKA;EACE;;AAEA;EACE;;AAMR;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAOJ;EACE;EACA;EACA;EACA;EACA;;AAKJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;IACE;;EAGF;IACE;;EAEA;IACE;;EAIJ;IACE;;EAGE;AAAA;IAEE;;EAKN;IACE;;;;ACvRN;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAIF;EAEE;EACA;EACA;EAGA;EAEA;EACA;;AAEA;EACE;EACA;;AAIF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAMN;EACE;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAKF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGE;EACE;;AAMR;EACE;EACA;EACA;;AAIJ;EACE;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAKN;EACE;EACA;EACA;;AAEA;EACE;;AAIJ;EACE;;AAKJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAIJ;EACE;IACE;;;;ACpKN;AAAA;AAAA;AAAA;AAIA;AACE;EACA;AAEA;EACA;EAEA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAGA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAKN;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;;AAKF;EACE;;AAGF;EACE","file":"page-panier.css"}

File diff suppressed because one or more lines are too long

View file

@ -94,9 +94,9 @@ $sous_total_panier = WC()->cart->get_subtotal();
/** @var string|null $code_promo Le code promo appliqué au Panier s'il existe. */
$code_promo = collect(WC()->cart->get_applied_coupons())->first();
/** @var int $sous_total_reduction Le total du montant de la Réduction appliquée au Panier */
$sous_total_reduction = Number::format(WC()->cart->get_totals()["discount_total"], precision: 2);
$sous_total_reduction = Number::format(WC()->cart->get_totals()["discount_total"], maxPrecision: 2);
/** @var float $total_panier Le total de la Commande dans le Panier. */
$total_panier = Number::format(floatval(WC()->cart->get_totals()["total"]), precision: 2);
$total_panier = Number::format(floatval(WC()->cart->get_totals()["total"]), maxPrecision: 2);
foreach (WC()->cart->get_cart() as $cle_panier => $article_panier) {
$panier[$cle_panier] = [
@ -122,17 +122,24 @@ foreach (WC()->cart->get_cart() as $cle_panier => $article_panier) {
$email = WC()->customer->get_billing_email();
$adresse_livraison = WC()->customer->get_shipping();
$adresse_facturation = WC()->customer->get_billing();
$adresse_renseignee = $adresse_livraison["city"] != "";
$pays_livraison = collect(WC()->countries->get_countries())->only($pays_acceptes)->toArray();
$total_livraison = Number::format(floatval(WC()->cart->get_totals()["shipping_total"]), precision: 0);
// echo "<pre>";
// print_r(WC()->countries->get_countries());
// print_r($adresse_livraison);
// echo "</pre>";
$methodes_livraison = collect(WC()->session->get("shipping_for_package_0")["rates"])
->values()
->map(function (WC_Shipping_Rate $methode) {
return [
"id" => $methode->get_method_id(),
"prix" => Number::format(intval($methode->get_cost()), maxPrecision: 2),
"selectionnee" => collect(WC()->session->get("chosen_shipping_methods"))->first() == $methode->get_id(),
"titre" => $methode->get_label(),
];
});
$contexte["email"] = $email;
$contexte["adresse_livraison"] = $adresse_livraison;
$contexte["adresse_facturation"] = $adresse_facturation;
$contexte["adresse_renseignee"] = $adresse_renseignee;
$contexte["sous_total_panier"] = $sous_total_panier;
$contexte["code_promo"] = $code_promo;
$contexte["sous_total_reduction"] = $sous_total_reduction;
@ -140,10 +147,12 @@ $contexte["total_panier"] = $total_panier;
$contexte["produits_panier"] = $panier;
$contexte["pays_livraison"] = $pays_livraison;
$contexte["sous_total_livraison"] = $total_livraison;
$contexte["methode_livraison"] =
collect(WC()->session->get("shipping_for_package_0")["rates"])
->first()
?->get_label() ?? "";
$contexte["methodes_livraison"] = $methodes_livraison;
// echo "<pre>";
// print_r($methodes_livraison);
// print_r(WC()->session->get("chosen_shipping_methods"));
// echo "</pre>";
// Charge les scripts et styles de la page
function charge_scripts_styles_page_panier(): void {

View file

@ -77,8 +77,8 @@ button {
background: var(--couleur-fond);
border: 1px solid var(--couleur-noir);
border-radius: 100%;
transition: 0.2s background, 0.2s opacity, 0.2s visibility;
box-shadow: initial;
transition: 0.2s background, 0.2s opacity, 0.2s visibility;
img {
width: 1rem;

View file

@ -1,7 +1,16 @@
// Styles de base pour les éléments de formulaires.
fieldset {
all: initial;
display: flex;
flex-flow: column nowrap;
margin-top: var(--espace-l);
font: inherit;
}
input, select, textarea {
padding: var(--espace-xs);
accent-color: var(--couleur-jaune);
background: var(--couleur-gris);
border: 1px solid var(--couleur-noir);
outline: 2px solid transparent;
@ -93,6 +102,27 @@ input[type="checkbox"], input[type="radio"] {
}
}
input[type="radio"] {
appearance: initial;
border-radius: 100%;
&:checked {
display: inline-flex;
place-content: center;
place-items: center;
&::before {
content: " ";
inset: initial;
display: inline-block;
width: calc(var(--espace-l) / 2);
height: calc(var(--espace-l) / 2);
background: var(--couleur-noir);
border-radius: 100%;
}
}
}
textarea {
&:focus-visible, &:focus-within {
outline: 1px solid var(--couleur-noir);

View file

@ -33,9 +33,9 @@
}
img {
image-rendering: crisp-edges;
shape-rendering: geometricPrecision;
object-fit: contain;
image-rendering: crisp-edges;
shape-rendering: geometricprecision;
}
button {

View file

@ -105,8 +105,8 @@
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
padding: var(--espace-m);
margin-bottom: var(--espace-xl);
padding: var(--espace-m);
h3 {
font-style: italic;

View file

@ -150,8 +150,8 @@
.panneau__pied-de-page {
align-content: center;
padding: var(--espace-l) 0;
font-style: italic;
font-size: 1.25rem;
font-style: italic;
color: var(--couleur-blanc);
text-align: center;
text-transform: uppercase;

View file

@ -171,7 +171,7 @@
}
.panneau__sous-totaux {
width: min(30rem, 100%);
width: min(40rem, 100%);
margin: auto;
padding: var(--espace-l) var(--espace-xl);
text-align: center;
@ -199,6 +199,32 @@
}
}
&__choix-methode-livraison {
flex-flow: row wrap;
gap: var(--espace-xs) var(--espace-m);
justify-content: center;
font-size: 0.9rem;
letter-spacing: inherit;
> label:first-of-type {
cursor: revert;
flex-basis: 100%;
margin-bottom: var(--espace-xs);
color: grey;
text-align: center;
text-transform: lowercase;
}
div {
display: flex;
flex-flow: row nowrap;
column-gap: 1ch;
place-items: center;
input {}
}
}
&__conditions-livraison {
margin-top: var(--espace-l);
font-size: 0.8rem;

View file

@ -8,9 +8,9 @@
max-width: 100vw;
height: var(--pied-de-page-hauteur);
padding: var(--espace-m);
font-size: 0.8rem;
background: var(--couleur-jaune);
border-top: 1px solid var(--couleur-noir);
font-size: 0.8rem;
// Liens vers les pages du site
.zone-menu-navigation-secondaire {

View file

@ -59,7 +59,9 @@
pointer-events: none;
display: block;
width: 1.75rem;
/* filter: drop-shadow(1px 1px 3px var(--couleur-gris-fonce)); */
/* transition: 0.2s filter; */
}

View file

@ -57,13 +57,6 @@
}
}
@media (width <= 50rem) {
.contenu {
border-right: initial;
border-left: initial;
}
}
&#page-cgv {
.contenu {
font-style: normal;
@ -73,8 +66,8 @@
}
&__textuel {
padding: 0;
max-width: initial;
padding: 0;
&__section {
width: 100%;
@ -87,10 +80,10 @@
header {
width: 100%;
margin-bottom: var(--espace-l);
padding: var(--espace-m) var(--espace-xl);
border-top: 1px solid var(--couleur-noir);
border-bottom: 1px solid var(--couleur-noir);
margin-bottom: var(--espace-l);
h3 {
width: fit-content;
@ -102,9 +95,9 @@
ul {
margin-bottom: 1lh;
padding: 0 var(--espace-xl);
list-style: square;
list-style-position: inside;
padding: 0 var(--espace-xl);
li {}
}
@ -120,4 +113,11 @@
}
}
}
@media (width <= 50rem) {
.contenu {
border-right: initial;
border-left: initial;
}
}
}

View file

@ -46,6 +46,7 @@ export const SELECTEUR_BOUTON_SOUSTRACTION_QUANTITE = "button.detail-produit__ac
export const SELECTEUR_BOUTON_SUPPRESSION_PANIER = "button.detail-produit__actions__suppression";
export const SELECTEUR_CHAMP_CODE_PROMO = "#panneau-panier #champ-code-promo";
export const SELECTEUR_CHAMP_QUANTITE_LIGNE_PANIER = "input";
export const SELECTEUR_CONTENEUR_METHODES_LIVRAISON = "#panneau-panier #choix-methode-livraison";
export const SELECTEUR_ENSEMBLE_CODE_PROMO = "#panneau-panier #ensemble-code-promo";
export const SELECTEUR_ENTREES_PANIER = "article";
export const SELECTEUR_FORMULAIRE_FACTURATION = "#panneau-informations-client .panneau__formulaires__facturation";

View file

@ -0,0 +1,4 @@
export const ADRESSES_MAJ = "adressesMaj";
export const CODE_PROMO_MAJ = "codePromoMaj";
export const METHODES_LIVRAISON_MAJ = "methodesLivraisonMaj";
export const PRODUITS_MAJ = "produitsMaj";

View file

@ -0,0 +1,30 @@
import { ADRESSES_MAJ, CODE_PROMO_MAJ, METHODES_LIVRAISON_MAJ, PRODUITS_MAJ } from "../../constantes/evenements";
export const ADRESSES_MAJ_EVENT = new CustomEvent(ADRESSES_MAJ, {});
export const CODE_PROMO_MAJ_EVENT = new CustomEvent(CODE_PROMO_MAJ, {});
export class MagasinPanier extends EventTarget {
emetAdressesMaj(): void {
this.dispatchEvent(
new CustomEvent(ADRESSES_MAJ),
);
}
emetCodePromoMaj(): void {
this.dispatchEvent(
new CustomEvent(CODE_PROMO_MAJ),
);
}
emetMethodesLivraisonMaj(): void {
this.dispatchEvent(
new CustomEvent(METHODES_LIVRAISON_MAJ),
);
}
emetProduitsMaj(): void {
this.dispatchEvent(
new CustomEvent(PRODUITS_MAJ),
);
}
}

View file

@ -0,0 +1,30 @@
import type { GenericSchema, InferOutput, ValiError } from "valibot";
import { type Either, Maybe } from "purify-ts";
import { eitherJsonParse } from "./dom.ts";
import { ErreurEntreeInexistante, type NonExistingKeyError } from "./erreurs.ts";
import { eitherValiParseCurried } from "./validation.ts";
export type GetLocalStorage<S extends GenericSchema> = Either<ErreursGetLocalStorage<S>, InferOutput<S>>;
type ErreursGetLocalStorage<S extends GenericSchema> =
| NonExistingKeyError
| SyntaxError
| ValiError<S>;
/**
* Récupère une entrée dans le Stockage Local (`localStorage`) sous forme d'`Either`.
*
* @param cle La clé de l'entrée.
* @returns Un `Either` avec une `NonExistingKeyError` si la clé est absente (`Left`), la
* valeur de l'entrée sinon (`Right`).
*/
export const eitherGetLocalStorage = (cle: string): Either<NonExistingKeyError, string> =>
Maybe
.fromNullable(localStorage.getItem(cle))
.toEither(ErreurEntreeInexistante(`Clé ${cle} absente dans le stockage de session.`));
export const getAndParseLocalStorage = <S extends GenericSchema>(cle: string, schema: S): GetLocalStorage<S> =>
eitherGetLocalStorage(cle)
.chain(eitherJsonParse)
.chain(eitherValiParseCurried(schema));

View file

@ -14,7 +14,7 @@ import { NOM_CANAL_BOUTON_PANIER, NOM_CANAL_CONTENU_PANIER, TYPES_MESSAGES } fro
import { reporteErreur } from "./erreurs.ts";
import { WCErrorSchema } from "./schemas/api/erreurs.ts";
import { MessageMajBoutonPanierSchema, MessageMajContenuPanierSchema } from "./schemas/messages.ts";
import { eitherParse } from "./validation.ts";
import { eitherValiParse } from "./validation.ts";
const canalPostMessage = (canal: BroadcastChannel, message: unknown): BroadcastChannel => {
canal.postMessage(message);
@ -78,6 +78,6 @@ export const valideMessageMajContenuPanier = (
// Correspondances
export const reponseEstCodeErreurWC = (reponse: ReponseSimplifiee, codeErreurWC: string): boolean =>
eitherParse(reponse, WCErrorSchema)
eitherValiParse(reponse, WCErrorSchema)
.map(v => v.body.code === codeErreurWC)
.orDefault(false);

View file

@ -14,5 +14,8 @@ export const inverseNombre = (nombre: number | string): number => Number(nombre)
export const formateEnEuros = (nombre: number | string): string => `${String(nombre)}`;
export const diviseParCentEtFormateEnEuros = (nombre: number | string): string =>
pipe(Number(nombre), diviseParCent, formateEnEuros);
export const diviseParCentEtArrondis = (nombre: number | string): string =>
pipe(Number(nombre), diviseParCent, arrondisADeuxDecimales);

View file

@ -5,7 +5,11 @@
import { Either } from "purify-ts";
import { type GenericSchema, type InferOutput, parse, type ValiError } from "valibot";
export const eitherParse = <Schema extends GenericSchema>(
export const eitherValiParse = <Schema extends GenericSchema>(
valeur: unknown,
schema: Schema,
): Either<ValiError<Schema>, InferOutput<Schema>> => Either.encase(() => parse(schema, valeur));
export const eitherValiParseCurried =
<S extends GenericSchema>(schema: S) => (valeur: unknown): Either<ValiError<S>, InferOutput<S>> =>
Either.encase(() => parse(schema, valeur));

View file

@ -3,7 +3,6 @@ import { find as arrayFind, head as arrayHead } from "@mobily/ts-belt/Array";
import { get as dictGet, map as dictMap, values as dictValues } from "@mobily/ts-belt/Dict";
import { flatMap as optionFlatMap, getWithDefault as optionGetWithDefault } from "@mobily/ts-belt/Option";
import { trim as stringTrim } from "@mobily/ts-belt/String";
import { Option } from "@swan-io/boxed";
import { EitherAsync, Maybe } from "purify-ts";
import { match, P } from "ts-pattern";
import { type AnySchema, ValiError } from "valibot";
@ -21,13 +20,16 @@ import {
ATTRIBUT_LIVRAISON_VALIDEE,
SELECTEUR_BOUTON_ACTIONS_FORMULAIRE,
SELECTEUR_BOUTON_SEPARATION_ADRESSES,
SELECTEUR_CONTENEUR_METHODES_LIVRAISON,
SELECTEUR_ENTREES_PANIER,
SELECTEUR_FORMULAIRE_PANIER,
SELECTEUR_INSTRUCTIONS_CLIENT,
SELECTEUR_MESSAGE_FORMULAIRE_ADRESSES,
SELECTEUR_SOUS_TOTAL_LIVRAISON_COUT,
SELECTEUR_SOUS_TOTAL_LIVRAISON_PRESTATAIRE,
SELECTEUR_SOUS_TOTAL_PRODUITS,
SELECTEUR_TOTAL_PANIER,
} from "../constantes/dom";
import { ADRESSES_MAJ, CODE_PROMO_MAJ } from "../constantes/evenements";
import { NOM_CANAL_REVALIDATION_LIVRAISON } from "../constantes/messages";
import {
ERREUR_ADRESSE_MAUVAIS_CODE_POSTAL,
@ -38,6 +40,7 @@ import {
import { eitherJsonParse, eitherSessionStorageGet } from "../lib/dom";
import { leveErreur, type NonExistingKeyError, reporteErreur, reporteEtJournaliseErreur } from "../lib/erreurs";
import { ErreurAdresseInvalide } from "../lib/erreurs/adresses";
import { ADRESSES_MAJ_EVENT } from "../lib/evenements/panier";
import { emetUniqueMessageBroadcastChannel } from "../lib/messages";
import { diviseParCent, formateEnEuros } from "../lib/nombres";
import { creeReponseSimplifiee, eitherAsyncFetch, postBackend, traiteErreursBackendWooCommerce } from "../lib/reseau";
@ -53,7 +56,8 @@ import {
recupereElementsDocumentEither,
recupereEleOuLeve,
} from "../lib/utils";
import { eitherParse } from "../lib/validation";
import { eitherValiParse } from "../lib/validation";
import { genereHtmlMethodesLivraison } from "./scripts-page-panier-methodes-livraison";
// @ts-expect-error -- États injectés par le modèle PHP
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- États injectés par le modèle PHP
@ -62,42 +66,54 @@ const ETATS_PAGE: EtatsPageGenerique = _etats;
const E = {
BOUTON_ACTIONS_FORMULAIRE: recupereEleOuLeve<HTMLButtonElement>(SELECTEUR_BOUTON_ACTIONS_FORMULAIRE),
BOUTON_SEPARATION_ADRESSES: recupereEleOuLeve<HTMLInputElement>(SELECTEUR_BOUTON_SEPARATION_ADRESSES),
CONTENEUR_METHODES_LIVRAISON: recupereEleOuLeve<HTMLFieldSetElement>(SELECTEUR_CONTENEUR_METHODES_LIVRAISON),
ENTREES_PANIER_EITHER: recupereElementsDocumentEither<HTMLElement>(SELECTEUR_ENTREES_PANIER),
FORMULAIRE_PANIER: recupereEleOuLeve<HTMLFormElement>(SELECTEUR_FORMULAIRE_PANIER),
INSTRUCTIONS_CLIENT: recupereEleOuLeve<HTMLTextAreaElement>(SELECTEUR_INSTRUCTIONS_CLIENT),
MESSAGE_ADRESSES: recupereEleOuLeve<HTMLParagraphElement>(SELECTEUR_MESSAGE_FORMULAIRE_ADRESSES),
TOTAL_LIVRAISON_COUT: recupereEleOuLeve<HTMLElement>(SELECTEUR_SOUS_TOTAL_LIVRAISON_COUT),
TOTAL_LIVRAISON_PRESTATAIRE: recupereEleOuLeve<HTMLSpanElement>(SELECTEUR_SOUS_TOTAL_LIVRAISON_PRESTATAIRE),
SOUS_TOTAL_LIVRAISON_COUT: recupereEleOuLeve<HTMLElement>(SELECTEUR_SOUS_TOTAL_LIVRAISON_COUT),
SOUS_TOTAL_PRODUITS: recupereEleOuLeve<HTMLElement>(SELECTEUR_SOUS_TOTAL_PRODUITS),
TOTAL_PANIER: recupereEleOuLeve<HTMLSpanElement>(SELECTEUR_TOTAL_PANIER),
};
/**
* Initialise l'écoute sur le `BroadcastChannel` de revalidation de la livraison.
*
* Lorsqu'un Message est émis, force l'Utilisateur à réaliser une requête pour le recalcul de ses conditions de
* livraison.
* Réinitialise le bouton de soumission du Formulaire en revenant à l'étape de validation des adresses.
*
* @returns void
*/
export const initialiseCanalRevalidationLivraison = (): void => {
new BroadcastChannel(NOM_CANAL_REVALIDATION_LIVRAISON).addEventListener("message", (): void => {
E.BOUTON_ACTIONS_FORMULAIRE.removeAttribute(ATTRIBUT_LIVRAISON_VALIDEE);
E.BOUTON_ACTIONS_FORMULAIRE.textContent = "Calculate shipping";
});
export const reinitialiseValidationLivraison = (): void => {
E.BOUTON_ACTIONS_FORMULAIRE.removeAttribute(ATTRIBUT_LIVRAISON_VALIDEE);
E.BOUTON_ACTIONS_FORMULAIRE.textContent = "Calculate shipping";
};
/**
* Lorsque le Formulaire du Panier change (que l'Utilisateur a changé une valeur), demande une revalidation
* de la livraison si elle a é précédemment validée.
* Initialise les Émetteurs d'Événements sur divers parties du Panier.
*
* 1. Émet un Événement de mise à jour des Adresses quand le Formulaire est modifié (s'il est valide).
*
* @returns void
*/
export const demandeRevalidationLivraisonAMajFormulaires = (): void =>
export const initialiseEmetteursEvenementsFormulairePanier = (): void => {
E.FORMULAIRE_PANIER.addEventListener("change", (): void => {
Maybe
.fromFalsy(E.FORMULAIRE_PANIER.checkValidity())
.chainNullable((): boolean => E.BOUTON_ACTIONS_FORMULAIRE.hasAttribute(ATTRIBUT_LIVRAISON_VALIDEE))
.ifJust((): void => emetUniqueMessageBroadcastChannel(NOM_CANAL_REVALIDATION_LIVRAISON, true));
.ifJust((): boolean => window.dispatchEvent(ADRESSES_MAJ_EVENT));
});
};
/**
* Souscris aux différents Événements liés aux Panier et déclenche différentes opérations en fonction de ces derniers.
*
* @returns void
*/
export const souscrisEvenementsPanier = (): void => {
window.addEventListener(ADRESSES_MAJ, (): void => {
reinitialiseValidationLivraison();
});
window.addEventListener(CODE_PROMO_MAJ, (): void => {
reinitialiseValidationLivraison();
});
};
export const initialiseBoutonCalculLivraison = (): void => {
// Déclenche la requête pour la soumission des adresses
@ -106,7 +122,9 @@ export const initialiseBoutonCalculLivraison = (): void => {
// Ne fais rien si le Formulaire n'est pas valide
.fromFalsy(E.FORMULAIRE_PANIER.checkValidity())
// Ne fais rien si la livraison a déjà été validée
.chainNullable((): boolean => !E.BOUTON_ACTIONS_FORMULAIRE.hasAttribute(ATTRIBUT_LIVRAISON_VALIDEE))
.chainNullable((): boolean | null =>
E.BOUTON_ACTIONS_FORMULAIRE.hasAttribute(ATTRIBUT_LIVRAISON_VALIDEE) ? null : true
)
.ifJust((): void => {
evenement.preventDefault();
@ -151,7 +169,7 @@ export const initialiseBoutonCalculLivraison = (): void => {
// Réalise la requête et traite sa réponse
void EitherAsync
// 1. Valide les Arguments de la Requête
.liftEither(eitherParse(argumentsFormulaire, WCStoreCartUpdateCustomerArgsSchema))
.liftEither(eitherValiParse(argumentsFormulaire, WCStoreCartUpdateCustomerArgsSchema))
// 2. Exécute un Effet pour empêcher les requêtes concurrentes et lancer une animation de chargement
.ifRight((): void => {
// Désactive le Bouton pour empêcher des requêtes concurrentes
@ -176,28 +194,29 @@ export const initialiseBoutonCalculLivraison = (): void => {
)
)
// 5. Vérifie le Schéma de la Réponse
.chain((corps: unknown) => EitherAsync.liftEither(eitherParse(corps, WCStoreCartSchema)))
.chain((corps: unknown) => EitherAsync.liftEither(eitherValiParse(corps, WCStoreCartSchema)))
// 6. Exécute un Effet pour la mise à jour du DOM avec les Résultats
.ifRight((panier: WCStoreCart): void => {
E.MESSAGE_ADRESSES.textContent = " ";
const sousTotalLivraison = panier.totals.total_shipping === 0
? "Free"
: pipe(diviseParCent(panier.totals.total_shipping), formateEnEuros);
// const sousTotalLivraison = panier.totals.total_shipping === 0
// ? "Free"
// : pipe(diviseParCent(panier.totals.total_shipping), formateEnEuros);
const sousTotalLivraison = pipe(diviseParCent(panier.totals.total_shipping), formateEnEuros);
const methodesLivraison = pipe(arrayHead(panier.shipping_rates), optionFlatMap(dictGet("shipping_rates")));
const methodeChoisie = optionFlatMap(methodesLivraison, arrayHead);
const prestataireLivraison = pipe(methodeChoisie, optionFlatMap(dictGet("name")));
// Met à jour les informations de Livraison affichés à l'Utilisateur
E.TOTAL_LIVRAISON_COUT.textContent = sousTotalLivraison;
E.TOTAL_LIVRAISON_PRESTATAIRE.textContent = optionGetWithDefault(prestataireLivraison, "???");
E.SOUS_TOTAL_LIVRAISON_COUT.textContent = sousTotalLivraison;
// Sauvegarde la Méthode de Livraison dans le Stockage de Session
sessionStorage.setItem(
// Sauvegarde la Méthode de Livraison dans LocalStorage
localStorage.setItem(
"shipping_rates",
JSON.stringify(optionGetWithDefault(methodesLivraison, [])),
);
// Affiche les méthodes disponibles à l'Utilisateur
genereHtmlMethodesLivraison(E.CONTENEUR_METHODES_LIVRAISON, optionGetWithDefault(methodesLivraison, []));
// Active le Bouton pour la création de la Commande
E.BOUTON_ACTIONS_FORMULAIRE.textContent = "Check-out";
E.BOUTON_ACTIONS_FORMULAIRE.setAttribute(ATTRIBUT_LIVRAISON_VALIDEE, "");
@ -277,7 +296,7 @@ export const initialiseBoutonCreationCommande = (): void => {
// Récupère la méthode de livraison depuis le stockage de session sous forme d'objet
.chain(eitherJsonParse)
// Vérifie la forme de l'objet récupéré
.chain((json: JSONValue) => eitherParse(json, WCStoreShippingRateShippingRatesSchema))
.chain((json: JSONValue) => eitherValiParse(json, WCStoreShippingRateShippingRatesSchema))
// Traite de manière différenciée les Erreurs
.ifLeft((erreur: NonExistingKeyError | SyntaxError | ValiError<AnySchema>): void => {
match(erreur)
@ -356,7 +375,7 @@ export const initialiseBoutonCreationCommande = (): void => {
// Réalise la requête et traite sa réponse
void EitherAsync
// 1. Valide les Arguments de la Requête
.liftEither(eitherParse(argumentsFormulaire, WCV3OrdersArgsSchema))
.liftEither(eitherValiParse(argumentsFormulaire, WCV3OrdersArgsSchema))
// 2. Exécute un Effet pour empêcher les requêtes concurrentes et lancer une animation de chargement
.ifRight((): void => {
// Désactive le Bouton pour empêcher des requêtes concurrentes
@ -382,7 +401,7 @@ export const initialiseBoutonCreationCommande = (): void => {
)
)
// 5. Vérifie le Schéma de la Réponse
.chain((corps: unknown) => EitherAsync.liftEither(eitherParse(corps, WCV3OrderSchema)))
.chain((corps: unknown) => EitherAsync.liftEither(eitherValiParse(corps, WCV3OrderSchema)))
// 6. Exécute un Effet pour la mise à jour du DOM avec les Résultats
.ifRight((commande: WCV3Order): void => {
E.BOUTON_ACTIONS_FORMULAIRE.removeAttribute(ATTRIBUT_CHARGEMENT);
@ -390,11 +409,11 @@ export const initialiseBoutonCreationCommande = (): void => {
E.MESSAGE_ADRESSES.textContent = " ";
// Redirige vers Stripe
Option
Maybe
.fromNullable(new URL(`https://${window.location.host}/checkout`))
.tapSome(url => url.searchParams.append("order_key", commande.order_key))
.tapSome(url => url.searchParams.append("order_id", String(commande.id)))
.tapSome(url => location.assign(url));
.ifJust(url => url.searchParams.append("order_key", commande.order_key))
.ifJust(url => url.searchParams.append("order_id", String(commande.id)))
.ifJust(url => location.assign(url));
})
// 7. Traite les Erreurs et affiche un message à l'Utilisateur
.ifLeft((erreur: FetchErrors | HttpCodeErrors | ValiError<AnySchema>): void => {

View file

@ -33,6 +33,7 @@ import { lanceAnimationCycleLoading } from "../lib/animations";
import { accorderCibleASelecteur } from "../lib/dom";
import { reporteErreur, ServerError } from "../lib/erreurs";
import { ErreurCodePromoInvalide } from "../lib/erreurs/codes-promo";
import { CODE_PROMO_MAJ_EVENT } from "../lib/evenements/panier";
import { estReponse500 } from "../lib/gardes";
import { emetUniqueMessageBroadcastChannel, reponseEstCodeErreurWC } from "../lib/messages";
import { arrondisADeuxDecimales, diviseParCent, formateEnEuros, inverseNombre } from "../lib/nombres";
@ -41,7 +42,7 @@ import { WCStoreCartSchema } from "../lib/schemas/api/cart";
import { WCStoreCartApplyCouponArgsSchema } from "../lib/schemas/api/cart-apply-coupon";
import { WCStoreCartRemoveCouponArgsSchema } from "../lib/schemas/api/cart-remove-coupon";
import { recupereEleOuLeve } from "../lib/utils";
import { eitherParse } from "../lib/validation";
import { eitherValiParse } from "../lib/validation";
// @ts-expect-error -- États injectés par le modèle PHP
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- États injectés par le modèle PHP
@ -82,7 +83,7 @@ export const initialiseElementsCodePromo = (): void => {
({ valeurCodePromo }) =>
void EitherAsync
// Vérifie le Schéma des arguments
.liftEither(eitherParse({ code: valeurCodePromo }, WCStoreCartApplyCouponArgsSchema))
.liftEither(eitherValiParse({ code: valeurCodePromo }, WCStoreCartApplyCouponArgsSchema))
.ifRight(() => {
// Désactive le Bouton pour empêcher des requêtes concurrentes
E.BOUTON_CODE_PROMO.setAttribute(ATTRIBUT_DESACTIVE, "");
@ -120,7 +121,7 @@ export const initialiseElementsCodePromo = (): void => {
})
)
// Vérifie le Schéma de la Réponse du backend
.chain((corpsReponse: unknown) => EitherAsync.liftEither(eitherParse(corpsReponse, WCStoreCartSchema)))
.chain((corpsReponse: unknown) => EitherAsync.liftEither(eitherValiParse(corpsReponse, WCStoreCartSchema)))
// Déclenche les mises à jour du DOM avec les données du nouveau Panier
.ifRight((panier: WCStoreCart) => {
E.ENSEMBLE_CODE_PROMO.toggleAttribute(ATTRIBUT_CODE_PROMO_PRESENT);
@ -141,7 +142,8 @@ export const initialiseElementsCodePromo = (): void => {
formateEnEuros,
);
emetUniqueMessageBroadcastChannel(NOM_CANAL_REVALIDATION_LIVRAISON, true);
window.dispatchEvent(CODE_PROMO_MAJ_EVENT);
// emetUniqueMessageBroadcastChannel(NOM_CANAL_REVALIDATION_LIVRAISON, true);
})
.ifLeft(erreur => {
// Rétablis le texte d'origine
@ -174,6 +176,7 @@ export const initialiseElementsCodePromo = (): void => {
})
.finally(() => {
// Désactive l'animation de chargement et rend le Bouton de nouveau cliquable
// TODO: Créer un type d'Événement ?
E.BOUTON_CODE_PROMO.removeAttribute(ATTRIBUT_CHARGEMENT);
E.BOUTON_CODE_PROMO.removeAttribute(ATTRIBUT_DESACTIVE);
})
@ -189,7 +192,7 @@ export const initialiseElementsCodePromo = (): void => {
},
({ valeurCodePromo }) =>
void EitherAsync
.liftEither(eitherParse({ code: valeurCodePromo }, WCStoreCartRemoveCouponArgsSchema))
.liftEither(eitherValiParse({ code: valeurCodePromo }, WCStoreCartRemoveCouponArgsSchema))
.ifRight(() => {
E.BOUTON_CODE_PROMO.setAttribute(ATTRIBUT_DESACTIVE, "");
E.BOUTON_CODE_PROMO.setAttribute(ATTRIBUT_CHARGEMENT, "");
@ -209,7 +212,7 @@ export const initialiseElementsCodePromo = (): void => {
return await reponse.json();
})
)
.chain((corpsReponse: unknown) => EitherAsync.liftEither(eitherParse(corpsReponse, WCStoreCartSchema)))
.chain((corpsReponse: unknown) => EitherAsync.liftEither(eitherValiParse(corpsReponse, WCStoreCartSchema)))
.ifRight((panier: WCStoreCart) => {
E.ENSEMBLE_CODE_PROMO.toggleAttribute(ATTRIBUT_CODE_PROMO_PRESENT);
E.ENSEMBLE_CODE_PROMO.reset();

View file

@ -0,0 +1,111 @@
import { find as arrayFind, forEach as arrayForEach, map as arrayMap } from "@mobily/ts-belt/Array";
import { html, render, type TemplateResult } from "lit-html";
import { Maybe } from "purify-ts";
import type { WCStoreShippingRateShippingRate } from "../lib/types/api/cart";
import type { WCStoreShippingRateShippingRates } from "../lib/types/api/couts-livraison";
import {
ATTRIBUT_HIDDEN,
SELECTEUR_CONTENEUR_METHODES_LIVRAISON,
SELECTEUR_SOUS_TOTAL_LIVRAISON_COUT,
SELECTEUR_SOUS_TOTAL_PRODUITS,
SELECTEUR_TOTAL_PANIER,
} from "../constantes/dom";
import { recupereElementsAvecSelecteur } from "../lib/dom";
import { reporteEtJournaliseErreur } from "../lib/erreurs";
import { getAndParseLocalStorage } from "../lib/local-storage";
import { diviseParCent, diviseParCentEtFormateEnEuros, formateEnEuros } from "../lib/nombres";
import { WCStoreShippingRateShippingRatesSchema } from "../lib/schemas/api/couts-livraison";
import { recupereEleOuLeve } from "../lib/utils";
const E = {
CONTENEUR_METHODES_LIVRAISON: recupereEleOuLeve<HTMLFieldSetElement>(SELECTEUR_CONTENEUR_METHODES_LIVRAISON),
SOUS_TOTAL_LIVRAISON_COUT: recupereEleOuLeve<HTMLElement>(SELECTEUR_SOUS_TOTAL_LIVRAISON_COUT),
SOUS_TOTAL_PRODUITS: recupereEleOuLeve<HTMLElement>(SELECTEUR_SOUS_TOTAL_PRODUITS),
TOTAL_PANIER: recupereEleOuLeve<HTMLSpanElement>(SELECTEUR_TOTAL_PANIER),
};
/**
* Créé un Événement MethodesLivraisonMaj déclenché quand l'Utilisateur change son choix avec en corps un object ShippingRates
* À l'Émission, sauvegarde les méthodes mises à jour dans le LocalStorage et met à jour le DOM
* Le déclenchement de la mise à jour des adresses déclenche aussi cet Événement.
* Le LocalStorage est comparé avec la Réponse, et si la méthode jusqe- sélectionnée est présente dans la Réponse, la mise à jour du DOM doit se faire en conservant ce choix.
*/
export const initialiseBoutonsChoixMethodesLivraison = (): void => {
recupereElementsAvecSelecteur(E.CONTENEUR_METHODES_LIVRAISON)<HTMLInputElement>("input").ifRight(
arrayForEach((i): void =>
// Met à jour sous-total de livraison et total au changement de méthode
i.addEventListener("click", (evenement: MouseEvent): void => {
const methodeChoisie = (evenement.target as HTMLInputElement).value;
getAndParseLocalStorage("shipping_rates", WCStoreShippingRateShippingRatesSchema)
.map(arrayMap((m): WCStoreShippingRateShippingRate => {
m.selected = m.method_id === methodeChoisie;
return m;
}))
// Enregistre les méthodes de livraison mises à jour
.ifRight((xs): void => localStorage.setItem("shipping_rates", JSON.stringify(xs)))
.ifLeft(reporteEtJournaliseErreur)
// Met à jour sous-total livraison et total
.toMaybe()
.chainNullable(xs => xs.find(m => m.selected))
.ifJust((m): void => {
Maybe
.fromNullable(E.SOUS_TOTAL_PRODUITS.textContent)
.map(s => s.split("€")[0])
.chainNullable(Number)
.map(t => formateEnEuros(t + diviseParCent(m.price)))
.ifJust(t => {
E.TOTAL_PANIER.textContent = t;
E.SOUS_TOTAL_LIVRAISON_COUT.textContent = diviseParCentEtFormateEnEuros(m.price);
});
});
})
),
);
};
export const genereHtmlMethodesLivraison = (
conteneur: HTMLElement,
methodes: WCStoreShippingRateShippingRates,
): void => {
// Cache les méthodes s'il n'y en a pas
if (methodes.length === 0) {
conteneur.setAttribute(ATTRIBUT_HIDDEN, "");
return;
}
// Retire les méthodes de livraison initiales
recupereElementsAvecSelecteur(conteneur)("div[data-methode-initiale]").ifRight(arrayForEach(div => div.remove()));
const methodeSelectionnee: string = getAndParseLocalStorage("shipping_rates", WCStoreShippingRateShippingRatesSchema)
.ifLeft(reporteEtJournaliseErreur)
.toMaybe()
.chainNullable(arrayFind(m => m.selected))
.map(m => m.method_id)
.orDefault("");
const methodesHtml: ReadonlyArray<TemplateResult> = arrayMap(methodes, methode => {
return html`
<div>
<input
id="methode-livraison-${methode.method_id}"
name="choix-methode-livraison"
type="radio"
value="${methode.method_id}"
.checked="${methode.method_id === methodeSelectionnee}"
>
<label for="methode-livraison-${methode.method_id}">${methode.name} (${
diviseParCentEtFormateEnEuros(methode.price)
})</label>
</div>`;
});
// Ajoute les nouveaux Produits dans le DOM
conteneur.removeAttribute(ATTRIBUT_HIDDEN);
render(methodesHtml, conteneur);
// Recréé les Écouteurs de clic sur les choix de méthodes
initialiseBoutonsChoixMethodesLivraison();
};

View file

@ -37,7 +37,7 @@ import { WCStoreCartSchema } from "../lib/schemas/api/cart";
import { WCStoreCartRemoveItemArgsSchema } from "../lib/schemas/api/cart-remove-item";
import { WCStoreCartUpdateItemArgsSchema } from "../lib/schemas/api/cart-update-item";
import { recupereElementsDocumentEither } from "../lib/utils";
import { eitherParse } from "../lib/validation";
import { eitherValiParse } from "../lib/validation";
// @ts-expect-error -- États injectés par le modèle PHP
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- États injectés par le modèle PHP
@ -122,7 +122,9 @@ export const initialiseActionsEntreesPanier = (): void => {
// Réalise la requête et traite sa réponse
void EitherAsync
// 1. Valide les Arguments de la Requête
.liftEither(eitherParse({ key: clePanier, quantity: valeur + 1 }, WCStoreCartUpdateItemArgsSchema))
.liftEither(
eitherValiParse({ key: clePanier, quantity: valeur + 1 }, WCStoreCartUpdateItemArgsSchema),
)
// 2. Exécute un Effet pour empêcher les requêtes concurrentes
.ifRight(() => pipe(entrees, arrayMap(recupereElementsEntreePanier), majActivationBoutons(false)))
// 3. Exécute la requête via fetch sous forme d'EitherAsync
@ -145,7 +147,7 @@ export const initialiseActionsEntreesPanier = (): void => {
)
)
// 5. Vérifie le Schéma de la Réponse
.chain((corps: unknown) => EitherAsync.liftEither(eitherParse(corps, WCStoreCartSchema)))
.chain((corps: unknown) => EitherAsync.liftEither(eitherValiParse(corps, WCStoreCartSchema)))
// 6. Exécute un Effet pour la mise à jour du DOM avec les Résultats
.ifRight((panier: WCStoreCart): void => {
// Émet un Message avec le nouveau nombre de Produits dans le Panier
@ -202,7 +204,9 @@ export const initialiseActionsEntreesPanier = (): void => {
// Réalise la requête et traite sa réponse
void EitherAsync
// 1. Valide les Arguments de la Requête
.liftEither(eitherParse({ key: clePanier, quantity: valeur - 1 }, WCStoreCartUpdateItemArgsSchema))
.liftEither(
eitherValiParse({ key: clePanier, quantity: valeur - 1 }, WCStoreCartUpdateItemArgsSchema),
)
// 2. Exécute un Effet pour empêcher les requêtes concurrentes
.ifRight(() => pipe(entrees, arrayMap(recupereElementsEntreePanier), majActivationBoutons(false)))
// 3. Exécute la requête via fetch sous forme d'EitherAsync
@ -225,7 +229,7 @@ export const initialiseActionsEntreesPanier = (): void => {
)
)
// 5. Vérifie le Schéma de la Réponse
.chain((corps: unknown) => EitherAsync.liftEither(eitherParse(corps, WCStoreCartSchema)))
.chain((corps: unknown) => EitherAsync.liftEither(eitherValiParse(corps, WCStoreCartSchema)))
// 6. Exécute un Effet pour la mise à jour du DOM avec les Résultats
.ifRight((panier: WCStoreCart): void => {
// Émet un Message avec le nouveau nombre de Produits dans le Panier
@ -281,7 +285,7 @@ export const initialiseActionsEntreesPanier = (): void => {
// Réalise la requête et traite sa réponse
void EitherAsync
// 1. Valide les Arguments de la Requête
.liftEither(eitherParse({ key: clePanier }, WCStoreCartRemoveItemArgsSchema))
.liftEither(eitherValiParse({ key: clePanier }, WCStoreCartRemoveItemArgsSchema))
// 2. Exécute un Effet pour empêcher les requêtes concurrentes
.ifRight(() => pipe(entrees, arrayMap(recupereElementsEntreePanier), majActivationBoutons(false)))
// 3. Exécute la requête via fetch sous forme d'EitherAsync
@ -304,7 +308,7 @@ export const initialiseActionsEntreesPanier = (): void => {
)
)
// 5. Vérifie le Schéma de la Réponse
.chain((corps: unknown) => EitherAsync.liftEither(eitherParse(corps, WCStoreCartSchema)))
.chain((corps: unknown) => EitherAsync.liftEither(eitherValiParse(corps, WCStoreCartSchema)))
// 6. Exécute un Effet pour la mise à jour du DOM avec les Résultats
.ifRight((panier: WCStoreCart): void => {
// Émet un Message avec le nouveau nombre de Produits dans le Panier

View file

@ -28,7 +28,7 @@ import { BadRequestError, reporteErreur, ServerError } from "./lib/erreurs.ts";
import { creeReponseSimplifiee, getBackendAvecParametresUrl } from "./lib/reseau.ts";
import { WCV3ProductsArgsSchema, WCV3ProductsSchema } from "./lib/schemas/api/v3/products.ts";
import { recupereEleOuLeve } from "./lib/utils.ts";
import { eitherParse } from "./lib/validation.ts";
import { eitherValiParse } from "./lib/validation.ts";
// @ts-expect-error -- États injectés par le modèle PHP
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- États injectés par le modèle PHP
@ -62,7 +62,7 @@ const initialisePageBoutique = (): void => {
void EitherAsync
// 1. Valide les Arguments de la Requête
.liftEither(eitherParse(args, WCV3ProductsArgsSchema))
.liftEither(eitherValiParse(args, WCV3ProductsArgsSchema))
// 2. Exécute un Effet pour empêcher les requêtes concurrentes et lancer une animation de chargement
.ifRight(() => {
// Désactive le Bouton pour empêcher des requêtes concurrentes
@ -94,7 +94,7 @@ const initialisePageBoutique = (): void => {
})
)
// 5. Vérifie le Schéma de la Réponse
.chain((corpsReponse: unknown) => EitherAsync.liftEither(eitherParse(corpsReponse, WCV3ProductsSchema)))
.chain((corpsReponse: unknown) => EitherAsync.liftEither(eitherValiParse(corpsReponse, WCV3ProductsSchema)))
// 6. Exécute un Effet pour la mise à jour du DOM avec les Résultats
.ifRight((donnees: WCV3Products) => {
// Cache le bouton s'il y a moins de 12 Produits disponibles (que l'on est à la dernière page)

View file

@ -33,12 +33,13 @@ import { valideMessageMajBoutonPanier, valideMessageMajContenuPanier } from "./l
import { arrondisADeuxDecimales, diviseParCent, formateEnEuros, inverseNombre } from "./lib/nombres.ts";
import { propEither, recupereElementsDocumentEither, recupereEleOuLeve } from "./lib/utils.ts";
import {
demandeRevalidationLivraisonAMajFormulaires,
initialiseBoutonCalculLivraison,
initialiseBoutonCreationCommande,
initialiseCanalRevalidationLivraison,
initialiseEmetteursEvenementsFormulairePanier,
souscrisEvenementsPanier,
} from "./page-panier/scripts-page-panier-adresses.ts";
import { initialiseElementsCodePromo } from "./page-panier/scripts-page-panier-code-promo.ts";
import { initialiseBoutonsChoixMethodesLivraison } from "./page-panier/scripts-page-panier-methodes-livraison.ts";
import { initialiseActionsEntreesPanier } from "./page-panier/scripts-page-panier-panneau-produits.ts";
type ElementsEntreePanier = {
@ -193,13 +194,14 @@ const initialiseMajFormulairesPanier = (): void => {
};
document.addEventListener("DOMContentLoaded", (): void => {
initialiseCanalRevalidationLivraison();
initialiseEmetteursEvenementsFormulairePanier();
souscrisEvenementsPanier();
initialiseActionsEntreesPanier();
initialiseBoutonsChoixMethodesLivraison();
initialiseMajConteneurPanier();
initialiseMajContenuPanier();
initialiseMajFormulairesPanier();
initialiseBoutonCalculLivraison();
initialiseBoutonCreationCommande();
initialiseElementsCodePromo();
demandeRevalidationLivraisonAMajFormulaires();
});

View file

@ -40,7 +40,7 @@ import { creeReponseSimplifiee, eitherAsyncFetch, postBackend } from "./lib/rese
import { WCStoreCartAddItemArgsSchema } from "./lib/schemas/api/cart-add-item.ts";
import { WCStoreCartSchema } from "./lib/schemas/api/cart.ts";
import { recupereElementDocumentEither, recupereEleOuLeve, recupereElesOuLeve } from "./lib/utils.ts";
import { eitherParse } from "./lib/validation.ts";
import { eitherValiParse } from "./lib/validation.ts";
type EnsembleLienContenu = [HTMLAnchorElement, HTMLElement];
/** États utiles pour les scripts de la page. */
@ -201,7 +201,7 @@ const ajouteProduitAuPanier = (): void => {
// Réalise la Requête et traite sa Réponse
void EitherAsync
// 1. Valide les arguments de la Requête
.liftEither(eitherParse(argsRequete, WCStoreCartAddItemArgsSchema))
.liftEither(eitherValiParse(argsRequete, WCStoreCartAddItemArgsSchema))
// 2. Exécute un Effet pour empêcher les requêtes concurrentes et lancer une animation de chargement
.ifRight(() => {
// Désactive le Bouton pour empêcher des requêtes concurrentes
@ -233,7 +233,7 @@ const ajouteProduitAuPanier = (): void => {
)
)
// 5. Vérifie le Schéma de la Réponse
.chain((corpsReponse: unknown) => EitherAsync.liftEither(eitherParse(corpsReponse, WCStoreCartSchema)))
.chain((corpsReponse: unknown) => EitherAsync.liftEither(eitherValiParse(corpsReponse, WCStoreCartSchema)))
// 6. Exécute un Effet pour la mise à jour du DOM avec les Résultats
.ifRight((panier: WCStoreCart) =>
pipe(

View file

@ -40,7 +40,7 @@
<div class="detail-produit__actions">
<button
class="detail-produit__actions__soustraction"
{{ produit.quantite > 1 ? "" : "disabled" }}
{{ produit.quantite < 1 ? "disabled" }}
type="button"
>
-
@ -85,18 +85,18 @@
<form
action=""
class="panneau__instructions-code-promo__code-promo"
{{ code_promo ? "data-code-promo-present" : "" }}
{{ code_promo ? "data-code-promo-present" }}
id="ensemble-code-promo"
>
<input
{{ code_promo ? "disabled" : "" }}
{{ code_promo ? "disabled" }}
id="champ-code-promo"
maxlength="20"
minlength="3"
name="code-promo"
placeholder="Discount code or gift card"
type="text"
value='{{ code_promo ? code_promo : ""}}'
value="{{ code_promo ? code_promo }}"
>
<button
class="bouton-blanc-sur-noir"
@ -123,7 +123,7 @@
<div
class="panneau__sous-totaux__ligne"
id="sous-total-reduction"
{{ code_promo ? "" : "hidden"}}
{{ not code_promo ? "hidden"}}
>
<p>Discount:</p>
<p><strong>-{{ sous_total_reduction }}€</strong></p>
@ -134,23 +134,43 @@
id="sous-total-livraison"
>
<p>Shipping:</p>
{% if sous_total_livraison == 0 %}
{% if not adresse_renseignee %}
<p>
<strong>Enter your delivery address</strong>
<br>
<span>&nbsp;</span>
</p>
{% else %}
<p>
<strong>{{ sous_total_livraison }}€</strong>
<br>
<span>{{ methode_livraison }}</span>
</p>
{% endif %}
</div>
<fieldset
class="panneau__sous-totaux__choix-methode-livraison"
{{ not adresse_renseignee ? "hidden" }}
id="choix-methode-livraison"
>
<label>Select your shipping method</label>
{% for methode_livraison in methodes_livraison %}
<div data-methode-initiale>
<input
{{ methode_livraison.selectionnee ? "checked"}}
data-prix="{{ methode_livraison.prix }}"
id="methode-livraison-{{ methode_livraison.id }}"
name="choix-methode-livraison"
type="radio"
value="{{ methode_livraison.id }}"
>
<label for="methode-livraison-{{ methode_livraison.id }}">{{ methode_livraison.titre }} ({{
methode_livraison.prix
}}€)</label>
</div>
{% endfor %}
</fieldset>
<p class="panneau__sous-totaux__conditions-livraison">
Belgium and France: free shipping on orders above 50€.<br>
Belgium and France: free shipping on orders above 50€ (Pickup Point only).<br>
Worldwide: free shipping on orders above 100€.
</p>
</div>