Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d7b5df74be | |||
| bb2b789d3f | |||
| 397272429d | |||
| 5314db52a0 | |||
| be6c852045 | |||
| d14cce5731 | |||
| 8910b77294 | |||
| f630bbf649 | |||
| 2aade91105 | |||
| bebc535cae |
@@ -0,0 +1,416 @@
|
||||
# 🛠️ Page Services — relecture & prochaine étape
|
||||
|
||||
Salut Guillaume ! 👋
|
||||
|
||||
Ta page services avance super bien : le tableau des prestations est clair, les boutons de navigation sont stylés, et tu as même ajouté une petite icône SVG sur le bouton « retour ». Bravo, c'est du bon travail ! 🎉
|
||||
|
||||
Ce guide a deux objectifs :
|
||||
1. Te montrer **3 bugs** à corriger (avec l'explication, pas juste la solution).
|
||||
2. T'apprendre à **définir et limiter une `<textarea>`** proprement.
|
||||
3. Te lancer sur ta **première mission JavaScript** 🚀 (un vrai morceau de programmation, dans le thème FleetZen).
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Bug 1 — Ton formulaire ne contient (presque) aucun champ
|
||||
|
||||
Regarde bien comment tu as écrit ton formulaire :
|
||||
|
||||
```html
|
||||
<form action="" method="post">Si vous avez des suggestions :</form> <!-- ❌ le </form> ferme ICI -->
|
||||
<ul>
|
||||
<li><input type="text" ... /></li> <!-- ces champs sont DEHORS du <form> ! -->
|
||||
<li><textarea ...></textarea></li>
|
||||
<button type="submit">Envoyer le message</button>
|
||||
</ul>
|
||||
```
|
||||
|
||||
Le problème : tu **ouvres** `<form>` puis tu le **refermes** (`</form>`) tout de suite, juste après la phrase d'intro. Du coup, tous tes champs (`<input>`, `<textarea>`, le bouton « Envoyer ») se retrouvent **à l'extérieur** du formulaire.
|
||||
|
||||
👉 Conséquence : le jour où le formulaire enverra vraiment les données, **rien ne partira**, parce que le navigateur n'envoie que ce qui est **à l'intérieur** des balises `<form>…</form>`.
|
||||
|
||||
### ✅ La correction : le `<form>` doit englober TOUS les champs
|
||||
|
||||
```html
|
||||
<form action="" method="post">
|
||||
<p>Si vous avez des suggestions :</p>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="name">Nom :</label>
|
||||
<input type="text" id="name" name="user_name" placeholder="Dupond" />
|
||||
</li>
|
||||
<li>
|
||||
<label for="mail">E-mail :</label>
|
||||
<input type="email" id="mail" name="user_mail" placeholder="dupond@gmail.com" />
|
||||
</li>
|
||||
<li>
|
||||
<label for="msg">Message :</label>
|
||||
<textarea id="msg" name="user_message" placeholder="Je pense qu'il faudrait..."></textarea>
|
||||
</li>
|
||||
</ul>
|
||||
<div id="boutton_formulaire">
|
||||
<button type="submit">Envoyer le message</button>
|
||||
</div>
|
||||
</form>
|
||||
```
|
||||
|
||||
> 💡 **À retenir :** un `<form>` est une **boîte**. Tout ce qu'on veut envoyer (champs + bouton « Envoyer ») doit être **dedans**, entre `<form>` et `</form>`.
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Bug 2 — Un dégradé qui ne s'affiche pas (parenthèses oubliées)
|
||||
|
||||
Dans `style_services.css`, ton bouton de la page courante `.x_to_x` :
|
||||
|
||||
```css
|
||||
.x_to_x {
|
||||
background-image:
|
||||
linear-gradient to top left, /* ❌ il manque les parenthèses */
|
||||
rgb(0 0 0 / 0.2),
|
||||
rgb(0 0 0 / 0.2) 30%,
|
||||
transparent;
|
||||
}
|
||||
```
|
||||
|
||||
`linear-gradient` est une **fonction** : comme en maths, ses arguments vont **entre parenthèses**. Sans elles, le navigateur ne comprend pas la ligne et **ignore tout le dégradé**. C'est pour ça que ce bouton n'a pas le même rendu que les autres.
|
||||
|
||||
### ✅ Correction (compare avec ta classe `.styled` qui, elle, est correcte)
|
||||
|
||||
```css
|
||||
.x_to_x {
|
||||
background-image: linear-gradient(
|
||||
to top left,
|
||||
rgb(0 0 0 / 0.2),
|
||||
rgb(0 0 0 / 0.2) 30%,
|
||||
transparent
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
> 💡 **À retenir :** `linear-gradient( … )`, `rgb( … )`, `calc( … )` … toutes les **fonctions** CSS prennent leurs arguments **entre parenthèses**. Une fonction sans parenthèses = ligne ignorée.
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Bug 3 — L'icône SVG n'est pas centrée dans le bouton « retour »
|
||||
|
||||
Ton bouton mélange une **image** (le SVG) et du **texte** côte à côte :
|
||||
|
||||
```html
|
||||
<button class="retour"><svg ...></svg>retour</button>
|
||||
```
|
||||
|
||||
Le souci : par défaut, une icône SVG se pose **sur la ligne du texte** (sur la « ligne de base », comme une lettre). Du coup elle paraît trop haute ou trop basse, et ton `line-height: 2.5` accentue le décalage. L'icône et le mot « retour » ne sont pas alignés sur leur **milieu**.
|
||||
|
||||
### ✅ La solution : transformer le bouton en boîte flex
|
||||
|
||||
```css
|
||||
.retour {
|
||||
display: inline-flex; /* le bouton range ses enfants (icône + texte) */
|
||||
align-items: center; /* ↕ les centre verticalement, sur le même milieu */
|
||||
justify-content: center; /* ↔ les centre horizontalement */
|
||||
gap: 6px; /* un petit espace entre l'icône et le mot */
|
||||
line-height: normal; /* on enlève le 2.5 qui déréglait l'alignement */
|
||||
/* ...garde le reste de tes styles (couleur, border-radius, etc.) */
|
||||
}
|
||||
```
|
||||
|
||||
`align-items: center`, c'est **exactement** le `flex` que tu connais déjà (tu l'utilises sur `#buttons` et `.align_x`). Ici on l'applique **au bouton lui-même**, parce que c'est lui le parent qui contient l'icône **et** le texte.
|
||||
|
||||
> 💡 **À retenir :** dès qu'un bouton (ou n'importe quelle boîte) contient **une icône + du texte** à aligner, passe-le en `display: inline-flex; align-items: center;`. C'est le réflexe pro pour centrer proprement une icône avec du texte.
|
||||
|
||||
> ⚠️ **Ça vaut aussi pour ton nouveau bouton « Envoyer le message »** : tu viens d'y ajouter l'icône SVG « avion en papier », il a donc exactement le même décalage. Applique-lui la même recette flex (par ex. une classe commune `.btn-icone` que tu mets sur les deux boutons, plutôt que de répéter le CSS).
|
||||
|
||||
---
|
||||
|
||||
## 📐 La leçon du jour : définir et **limiter** une `<textarea>`
|
||||
|
||||
Pour l'instant ta `<textarea>` est « brute » :
|
||||
|
||||
```html
|
||||
<textarea id="msg" name="user_message"></textarea>
|
||||
```
|
||||
|
||||
Par défaut elle est petite, et surtout l'utilisateur peut écrire **un roman de 10 000 mots** sans aucune limite. On va apprendre à la **cadrer**.
|
||||
|
||||
### 1️⃣ Lui donner une taille de départ : `rows` et `cols`
|
||||
|
||||
```html
|
||||
<textarea id="msg" name="user_message"
|
||||
rows="6" cols="40"></textarea>
|
||||
```
|
||||
|
||||
- `rows` = nombre de **lignes** visibles (la hauteur).
|
||||
- `cols` = nombre de **colonnes** = caractères en largeur.
|
||||
|
||||
> 🔎 `rows`/`cols` ne sont **pas** des pixels : c'est « combien de lignes/caractères je vois ». C'est la taille **de départ**.
|
||||
|
||||
### 2️⃣ Limiter le nombre de caractères : `maxlength`
|
||||
|
||||
```html
|
||||
<textarea id="msg" name="user_message"
|
||||
rows="6" cols="40"
|
||||
maxlength="500"></textarea>
|
||||
```
|
||||
|
||||
`maxlength="500"` = l'utilisateur **ne peut pas** taper plus de 500 caractères. C'est le navigateur qui bloque tout seul, sans JavaScript. 👍
|
||||
|
||||
### 3️⃣ Empêcher (ou cadrer) le redimensionnement : `resize` en CSS
|
||||
|
||||
Tu as remarqué la petite poignée en bas à droite d'une textarea ? L'utilisateur peut l'étirer et **casser ta mise en page**. On contrôle ça en CSS :
|
||||
|
||||
```css
|
||||
#msg {
|
||||
resize: vertical; /* il peut agrandir en hauteur seulement (recommandé) */
|
||||
/* resize: none; pour interdire complètement le redimensionnement */
|
||||
width: 100%; /* la largeur, on la pilote en CSS, pas avec cols */
|
||||
min-height: 120px;
|
||||
}
|
||||
```
|
||||
|
||||
> 💡 **À retenir :**
|
||||
> - `rows` / `cols` → **taille de départ** (lignes / caractères).
|
||||
> - `maxlength` → **limite** le nombre de caractères (le navigateur bloque tout seul).
|
||||
> - `resize` (CSS) → contrôle la **poignée** d'étirement (`vertical`, `horizontal`, `none`, `both`).
|
||||
> - Astuce de pro : on règle souvent la **largeur en CSS** (`width`) et on garde `rows` pour la hauteur de départ.
|
||||
|
||||
### 4️⃣ 👉 Votre question : laisser l'utilisateur redimensionner, mais **entre un min et un max**
|
||||
|
||||
Tu as choisi pour l'instant :
|
||||
|
||||
```css
|
||||
#msg {
|
||||
resize: none; /* la poignée est désactivée : l'utilisateur ne peut RIEN étirer */
|
||||
width: 200px;
|
||||
}
|
||||
```
|
||||
|
||||
`resize: none` est un **choix valable** (la zone est verrouillée). Mais vous m'avez demandé l'inverse : **autoriser** l'agrandissement, tout en **empêchant que ça parte trop grand ou trop petit**. La recette tient en 2 idées qui se combinent :
|
||||
|
||||
1. **`resize`** dit *dans quelle direction* on peut étirer.
|
||||
2. **`min-*` / `max-*`** disent *jusqu'où* on peut aller.
|
||||
|
||||
```css
|
||||
#msg {
|
||||
resize: vertical; /* on autorise l'étirement, mais en HAUTEUR seulement */
|
||||
|
||||
min-height: 80px; /* impossible de la réduire en dessous de 80px */
|
||||
max-height: 300px; /* impossible de l'agrandir au-delà de 300px */
|
||||
|
||||
width: 100%; /* largeur pilotée en CSS, pas par l'utilisateur */
|
||||
}
|
||||
```
|
||||
|
||||
Avec ça : l'utilisateur attrape la poignée, tire vers le bas pour avoir plus de place… mais **la zone refuse de dépasser 300px** et **de descendre sous 80px**. Ta mise en page reste protégée. 🎉
|
||||
|
||||
#### Et si on veut autoriser les **deux** sens (vertical **et** horizontal) ?
|
||||
|
||||
Même principe, mais avec **4 bornes** : 2 pour la hauteur, 2 pour la largeur. On passe `resize` à `both` :
|
||||
|
||||
```css
|
||||
#msg {
|
||||
resize: both; /* la poignée étire en hauteur ET en largeur */
|
||||
|
||||
min-height: 80px; /* bornes verticales */
|
||||
max-height: 300px;
|
||||
|
||||
min-width: 200px; /* bornes horizontales */
|
||||
max-width: 100%; /* ne dépasse jamais la largeur du parent → pas de scroll */
|
||||
}
|
||||
```
|
||||
|
||||
Chaque direction a sa paire `min`/`max` :
|
||||
- **hauteur** → `min-height` / `max-height`
|
||||
- **largeur** → `min-width` / `max-width`
|
||||
|
||||
L'utilisateur peut alors étirer dans tous les sens, mais **jamais au-delà** de ces 4 limites.
|
||||
|
||||
> ⚠️ Le piège classique en horizontal : mets `max-width: 100%` (et pas une grande valeur en `px`), sinon l'utilisateur peut élargir la zone **plus que l'écran** et faire réapparaître la barre de défilement horizontale (souviens-toi du bug du scroll 😉).
|
||||
|
||||
Récap des valeurs de `resize` :
|
||||
|
||||
| `resize` | Ce que l'utilisateur peut étirer |
|
||||
|---|---|
|
||||
| `none` | rien (verrouillé) — ton choix actuel |
|
||||
| `vertical` | la hauteur seulement (le plus confortable pour un message) |
|
||||
| `horizontal` | la largeur seulement |
|
||||
| `both` | les deux |
|
||||
|
||||
> 💡 **À retenir :** `resize` = **la direction** autorisée. `min-height`/`max-height` (et `min-width`/`max-width`) = **les bornes**. Les deux ensemble = un redimensionnement libre **mais encadré**.
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Ta première mission JavaScript : le **simulateur de devis FleetZen**
|
||||
|
||||
Jusqu'ici tu as fait du HTML (la **structure**) et du CSS (l'**apparence**). Le JavaScript, c'est la **3ᵉ couche** : le **comportement**, ce qui réagit quand on clique. 🧠
|
||||
|
||||
On va le faire **en mode tuto** : tu construis petit bout par petit bout, et **tu testes à chaque étape**. Ne copie pas tout d'un coup — avance palier par palier, c'est comme ça qu'on comprend. 🙂
|
||||
|
||||
**Objectif final :** un petit **simulateur** où le client choisit **combien de véhicules** il a, et le prix total se met à jour **tout seul**, sans recharger la page.
|
||||
|
||||
---
|
||||
|
||||
### 🪜 Étape 0 — Découvrir la console (ton meilleur ami)
|
||||
|
||||
Avant tout : ouvre ta page dans le navigateur, puis appuie sur **F12** → onglet **Console**. C'est là que le JS « parle ».
|
||||
|
||||
Crée un fichier `css/devis.js` avec une seule ligne :
|
||||
|
||||
```js
|
||||
console.log("Mon fichier JS est bien chargé !");
|
||||
```
|
||||
|
||||
Et branche-le en bas du `<body>`, **juste avant** `</body>` :
|
||||
|
||||
```html
|
||||
<script src="css/devis.js"></script>
|
||||
```
|
||||
|
||||
> 🔎 Pourquoi à la fin du body ? Parce que le JS s'exécute de haut en bas. S'il se lance **avant** que le HTML existe, il ne trouve rien à manipuler. À la fin, tout le HTML est déjà là. 👍
|
||||
|
||||
**✅ Teste maintenant :** recharge la page. Tu dois voir ton message dans la Console. Si oui, ton JS est branché. Sinon, vérifie le chemin du `src`.
|
||||
|
||||
---
|
||||
|
||||
### 🪜 Étape 1 — Ajouter le HTML du simulateur
|
||||
|
||||
Sous ton tableau, ajoute :
|
||||
|
||||
```html
|
||||
<section class="simulateur">
|
||||
<h2>Estimez votre devis</h2>
|
||||
<p>Nombre de véhicules dans votre flotte :</p>
|
||||
<button id="moins" type="button">−</button>
|
||||
<span id="nb_vehicules">1</span>
|
||||
<button id="plus" type="button">+</button>
|
||||
<p>Total géolocalisation : <strong id="total">30</strong> €/mois</p>
|
||||
</section>
|
||||
```
|
||||
|
||||
**✅ Teste maintenant :** tu dois voir les boutons `−` et `+` et le chiffre `1`. Pour l'instant ils ne font rien, c'est normal : on n'a pas encore écrit le comportement.
|
||||
|
||||
> 🔎 Remarque les **`id`** (`moins`, `plus`, `nb_vehicules`, `total`). Un `id` est une **étiquette unique** : c'est par là que le JS va « attraper » chaque élément à l'étape suivante.
|
||||
|
||||
---
|
||||
|
||||
### 🪜 Étape 2 — Attraper les éléments depuis le JS
|
||||
|
||||
Dans `devis.js`, remplace ton `console.log` de test par :
|
||||
|
||||
```js
|
||||
// "Attraper" un élément du HTML grâce à son id
|
||||
const boutonPlus = document.getElementById("plus");
|
||||
|
||||
console.log(boutonPlus); // pour vérifier qu'on l'a bien trouvé
|
||||
```
|
||||
|
||||
**✅ Teste maintenant :** dans la Console tu dois voir s'afficher ton `<button id="plus">`. Si tu vois `null`, c'est que l'id ne correspond pas → vérifie l'orthographe.
|
||||
|
||||
> 🧩 **Idée clé n°1 — SÉLECTIONNER.** `document.getElementById("plus")` veut dire : « dans le document, va me chercher l'élément dont l'id est *plus* ». On le range dans une variable pour s'en servir après.
|
||||
|
||||
---
|
||||
|
||||
### 🪜 Étape 3 — Réagir à un clic
|
||||
|
||||
Ajoute en dessous :
|
||||
|
||||
```js
|
||||
boutonPlus.addEventListener("click", function () {
|
||||
console.log("Clic sur + détecté !");
|
||||
});
|
||||
```
|
||||
|
||||
**✅ Teste maintenant :** clique sur le bouton `+`. À **chaque** clic, un message apparaît dans la Console. 🎉
|
||||
|
||||
> 🧩 **Idée clé n°2 — RÉAGIR.** `addEventListener("click", … )` se lit : « écoute les clics sur ce bouton, et quand il y en a un, exécute cette fonction ». La fonction entre `{ }` est le « que faire quand ça arrive ».
|
||||
|
||||
---
|
||||
|
||||
### 🪜 Étape 4 — Faire vraiment changer le compteur
|
||||
|
||||
Maintenant on remplace le `console.log` par une vraie logique. Voici le fichier complet :
|
||||
|
||||
```js
|
||||
// 1. On attrape tous les éléments dont on a besoin
|
||||
const affichageNb = document.getElementById("nb_vehicules");
|
||||
const affichageTotal = document.getElementById("total");
|
||||
const boutonPlus = document.getElementById("plus");
|
||||
const boutonMoins = document.getElementById("moins");
|
||||
|
||||
const PRIX_PAR_VEHICULE = 30; // 30 €/véhicule/mois (cf. ton tableau)
|
||||
let nbVehicules = 1; // "let" = une variable qui va CHANGER
|
||||
|
||||
// 2. Une fonction qui réécrit l'affichage à partir de nbVehicules
|
||||
function mettreAJour() {
|
||||
affichageNb.textContent = nbVehicules;
|
||||
affichageTotal.textContent = nbVehicules * PRIX_PAR_VEHICULE;
|
||||
}
|
||||
|
||||
// 3. Au clic sur "+", on augmente puis on réaffiche
|
||||
boutonPlus.addEventListener("click", function () {
|
||||
nbVehicules = nbVehicules + 1;
|
||||
mettreAJour();
|
||||
});
|
||||
|
||||
// 4. Au clic sur "−", on diminue (mais jamais en dessous de 1)
|
||||
boutonMoins.addEventListener("click", function () {
|
||||
if (nbVehicules > 1) {
|
||||
nbVehicules = nbVehicules - 1;
|
||||
}
|
||||
mettreAJour();
|
||||
});
|
||||
```
|
||||
|
||||
> 🧩 **Idée clé n°3 — MODIFIER.** `element.textContent = …` réécrit le texte affiché **en direct**, sans recharger la page. C'est ça, la magie du JS.
|
||||
>
|
||||
> 🔎 Pourquoi une fonction `mettreAJour()` ? Pour ne pas se répéter : le `+` et le `−` ont tous les deux besoin de réafficher. On écrit le « comment afficher » **une seule fois** et on l'appelle aux deux endroits (c'est le même esprit que le composant réutilisable, juste après 👇).
|
||||
|
||||
**✅ Teste maintenant :** clique 3 fois sur `+` → tu dois lire **4 véhicules** et **120 €**. Clique sur `−` jusqu'en bas → ça doit **bloquer à 1**. Si oui : bravo, tu viens d'écrire ta première vraie logique en JavaScript ! 🎉
|
||||
|
||||
> 🎯 **Défis bonus** (dans l'ordre de difficulté) :
|
||||
> 1. Empêcher aussi de dépasser 100 véhicules.
|
||||
> 2. Afficher le total avec un séparateur de milliers.
|
||||
> 3. Ajouter un 2ᵉ service (ex. consommation à 15 €) et calculer un total combiné.
|
||||
|
||||
---
|
||||
|
||||
## ♻️ Bonus — La notion de **composant réutilisable** (la navbar)
|
||||
|
||||
Regarde : la barre du haut (`#nav_barre`) est **recopiée à l'identique** dans `index.html`, `services.html` et `contact.html`. Le jour où tu changes un lien, tu dois le changer **dans 3 fichiers** → source d'erreurs (d'ailleurs, vérifie tes liens entre pages 😉).
|
||||
|
||||
C'est **le** problème que résolvent les **composants** : on écrit la navbar **une seule fois**, et on la réutilise partout.
|
||||
|
||||
Première approche très simple en JavaScript : une fonction qui **génère** la navbar.
|
||||
|
||||
```js
|
||||
function creerNavbar() {
|
||||
return `
|
||||
<div id="nav_barre" class="shadow">
|
||||
<h1>FleetZen</h1>
|
||||
<div id="buttons">
|
||||
<a href="contact.html"><button class="styled">contact</button></a>
|
||||
<a href="services.html"><button class="styled">services</button></a>
|
||||
<a href="../index.html"><button class="retour">retour</button></a>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// On l'injecte tout en haut du body :
|
||||
document.body.insertAdjacentHTML("afterbegin", creerNavbar());
|
||||
```
|
||||
|
||||
**✅ Teste :** enlève la navbar écrite « en dur » dans le HTML, garde ce JS, recharge → la navbar réapparaît, générée par la fonction. Maintenant le jour où tu changes un lien, tu le changes **à un seul endroit** pour **toutes** les pages.
|
||||
|
||||
> 💡 **À retenir :** un **composant** = un bout d'interface écrit **une fois** et **réutilisé** partout. C'est l'idée centrale derrière React, Vue, etc. — mais ça commence par une simple fonction comme celle-ci.
|
||||
|
||||
---
|
||||
|
||||
## ✅ Ta checklist
|
||||
|
||||
- [ ] Remettre tous les champs **dans** le `<form>` (Bug 1)
|
||||
- [ ] Réparer le dégradé de `.x_to_x` avec les parenthèses (Bug 2)
|
||||
- [ ] Centrer l'icône SVG des boutons « retour » **et** « Envoyer » avec `inline-flex` (Bug 3)
|
||||
- [ ] Ajouter `rows`, `maxlength` + redimensionnement borné (`resize: vertical` + `min/max-height`)
|
||||
- [ ] Faire marcher le **simulateur de devis** en JavaScript
|
||||
- [ ] (Bonus) Transformer la navbar en composant réutilisable
|
||||
- [ ] Commits petits et clairs, puis `git push`
|
||||
|
||||
Pose-moi toutes tes questions. Tu gères ! 🚗💨
|
||||
@@ -81,7 +81,53 @@ h1 {
|
||||
inset -2px -2px 3px rgb(255 255 255 / 0.6),
|
||||
inset 2px 2px 3px rgb(0 0 0 / 0.6);
|
||||
}
|
||||
.x_to_x {
|
||||
border: 0;
|
||||
height: 70px;
|
||||
width: 130px;
|
||||
line-height: 2.5;
|
||||
padding: 0 20px;
|
||||
font-size: 1rem;
|
||||
text-align: center;
|
||||
color: white;
|
||||
text-shadow: 1px 1px 1px black;
|
||||
border-radius: 10px;
|
||||
background-color: midnightblue;
|
||||
background-image:
|
||||
linear-gradient to top left,
|
||||
rgb(0 0 0 / 0.2),
|
||||
rgb(0 0 0 / 0.2) 30%,
|
||||
transparent;
|
||||
}
|
||||
.styled {
|
||||
border: 0;
|
||||
height: 70px;
|
||||
width: 130px;
|
||||
line-height: 2.5;
|
||||
padding: 0 20px;
|
||||
font-size: 1rem;
|
||||
text-align: center;
|
||||
color: white;
|
||||
text-shadow: 1px 1px 1px black;
|
||||
border-radius: 10px;
|
||||
background-color: royalblue;
|
||||
background-image: linear-gradient(
|
||||
to top left,
|
||||
rgb(0 0 0 / 0.2),
|
||||
rgb(0 0 0 / 0.2) 30%,
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
||||
.styled:hover {
|
||||
background-color: midnightblue;
|
||||
}
|
||||
|
||||
.styled:active {
|
||||
box-shadow:
|
||||
inset -2px -2px 3px rgb(255 255 255 / 0.6),
|
||||
inset 2px 2px 3px rgb(0 0 0 / 0.6);
|
||||
}
|
||||
#buttons {
|
||||
height: 100%;
|
||||
width: 25%;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
const affichageNb = document.getElementById("nb_vehicules");
|
||||
const affichageGéo = document.getElementById("géolocalisation");
|
||||
const affichageConso = document.getElementById("consommation");
|
||||
const boutonPlus = document.getElementById("plus");
|
||||
const boutonMoins = document.getElementById("moins");
|
||||
const PRIX_GPS = 30;
|
||||
const PRIX_CONSOMMATION = 15;
|
||||
let nbVehicules = 1;
|
||||
let totalargent = 0;
|
||||
function mettreAJourGPS() {
|
||||
affichageNb.textContent = nbVehicules;
|
||||
affichageGéo.textContent = nbVehicules * PRIX_GPS;
|
||||
}
|
||||
function mettreAJourCONSO() {
|
||||
affichageNb.textContent = nbVehicules;
|
||||
affichageConso.textContent = nbVehicules * PRIX_CONSOMMATION;
|
||||
}
|
||||
function mettreAJourTOTAL() {
|
||||
affichageNb.textContent = totalargent;
|
||||
affichageGéo.textContent = totalargent + PRIX_GPS + PRIX_CONSOMMATION;
|
||||
}
|
||||
boutonPlus.addEventListener("click", function () {
|
||||
nbVehicules = nbVehicules + 1;
|
||||
(mettreAJourGPS(), mettreAJourCONSO());
|
||||
if (nbVehicules > 99) {
|
||||
nbVehicules = nbVehicules - 1;
|
||||
}
|
||||
});
|
||||
boutonMoins.addEventListener("click", function () {
|
||||
if (nbVehicules > 1) {
|
||||
nbVehicules = nbVehicules - 1;
|
||||
}
|
||||
(mettreAJourGPS(), mettreAJourCONSO());
|
||||
});
|
||||
@@ -0,0 +1,213 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgb(190, 255, 255);
|
||||
margin: 0;
|
||||
padding-bottom: 60px;
|
||||
}
|
||||
#img_1 {
|
||||
width: 20%;
|
||||
height: auto;
|
||||
}
|
||||
.image_footer {
|
||||
display: flex;
|
||||
width: 30%;
|
||||
height: auto;
|
||||
}
|
||||
.align_x {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
padding-top: 20px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
#text_1 {
|
||||
width: 65%;
|
||||
font-size: 200%;
|
||||
}
|
||||
h1 {
|
||||
background-color: steelblue;
|
||||
font-size: 40px;
|
||||
}
|
||||
.footer {
|
||||
text-align: center;
|
||||
background-color: cadetblue;
|
||||
font-size: 25px;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
}
|
||||
#nav_barre {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
background-color: steelblue;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.styled {
|
||||
border: 0;
|
||||
height: 70px;
|
||||
width: 130px;
|
||||
line-height: 2.5;
|
||||
padding: 0 20px;
|
||||
font-size: 1rem;
|
||||
text-align: center;
|
||||
color: white;
|
||||
text-shadow: 1px 1px 1px black;
|
||||
border-radius: 10px;
|
||||
background-color: royalblue;
|
||||
background-image: linear-gradient(
|
||||
to top left,
|
||||
rgb(0 0 0 / 0.2),
|
||||
rgb(0 0 0 / 0.2) 30%,
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
||||
.styled:hover {
|
||||
background-color: midnightblue;
|
||||
}
|
||||
|
||||
.styled:active {
|
||||
box-shadow:
|
||||
inset -2px -2px 3px rgb(255 255 255 / 0.6),
|
||||
inset 2px 2px 3px rgb(0 0 0 / 0.6);
|
||||
}
|
||||
.x_to_x {
|
||||
border: 0;
|
||||
height: 70px;
|
||||
width: 130px;
|
||||
line-height: 2.5;
|
||||
padding: 0 20px;
|
||||
font-size: 1rem;
|
||||
text-align: center;
|
||||
color: white;
|
||||
text-shadow: 1px 1px 1px black;
|
||||
border-radius: 10px;
|
||||
background-color: midnightblue;
|
||||
background-image: linear-gradient(
|
||||
to top left,
|
||||
rgb(0 0 0 / 0.2),
|
||||
rgb(0 0 0 / 0.2) 30%,
|
||||
transparent
|
||||
);
|
||||
}
|
||||
.retour {
|
||||
border: 0;
|
||||
height: 50px;
|
||||
width: 130px;
|
||||
line-height: 2.5;
|
||||
padding: 0 20px;
|
||||
font-size: 1rem;
|
||||
text-align: center;
|
||||
color: black;
|
||||
border-radius: 10px;
|
||||
background-color: white;
|
||||
background-image: linear-gradient(
|
||||
to top left,
|
||||
rgb(0 0 0 / 0.2),
|
||||
rgb(0 0 0 / 0.2) 30%,
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
||||
.retour:hover {
|
||||
background-color: gray;
|
||||
}
|
||||
|
||||
.retour:active {
|
||||
box-shadow:
|
||||
inset -2px -2px 3px rgb(255 255 255 / 0.6),
|
||||
inset 2px 2px 3px rgb(0 0 0 / 0.6);
|
||||
}
|
||||
#buttons {
|
||||
height: 100%;
|
||||
width: 37%;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
flex-direction: row-reverse;
|
||||
margin-left: auto;
|
||||
align-items: center;
|
||||
}
|
||||
.shadow {
|
||||
box-shadow: 0px 10px 5px gray;
|
||||
}
|
||||
p {
|
||||
font-family: cursive;
|
||||
}
|
||||
.sous-titre {
|
||||
font-size: 25px;
|
||||
}
|
||||
.table {
|
||||
margin-bottom: 30px;
|
||||
width: 70%;
|
||||
height: auto;
|
||||
background-color: dodgerblue;
|
||||
border-collapse: collapse;
|
||||
font-size: 25px;
|
||||
table-layout: auto;
|
||||
border: 2px solid;
|
||||
th,
|
||||
td {
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
td {
|
||||
background-color: lightskyblue;
|
||||
}
|
||||
}
|
||||
.padding_left {
|
||||
padding-left: 10px;
|
||||
}
|
||||
#boutton_formulaire {
|
||||
width: 200px;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
flex-direction: row;
|
||||
margin-left: auto;
|
||||
align-items: center;
|
||||
padding: 2px;
|
||||
text-align: center;
|
||||
}
|
||||
.formulaire {
|
||||
background-color: lightskyblue;
|
||||
padding: 10px;
|
||||
width: 40%;
|
||||
height: 300px;
|
||||
border: solid;
|
||||
display: flex;
|
||||
font-size: 20px;
|
||||
li {
|
||||
padding: 15px;
|
||||
}
|
||||
ul {
|
||||
list-style-type: none;
|
||||
}
|
||||
}
|
||||
.center {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
#msg {
|
||||
resize: horizontal;
|
||||
min-width: 50px;
|
||||
max-width: 300px;
|
||||
}
|
||||
.btn_icone {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
line-height: normal;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="css/style_services.css" />
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width", initial-scame="1"/>
|
||||
<title>FleetZen/services</title>
|
||||
<html lang="fr">
|
||||
</head>
|
||||
<body>
|
||||
<div id="nav_barre" class="shadow">
|
||||
<h1>FleetZen</h1>
|
||||
<div id="buttons">
|
||||
<a href="../pages/contact.html"><button class="styled">contact</button></a>
|
||||
<a href="../pages/services.html"><button class="x_to_x">services</button></a>
|
||||
<a href="../index.html"><button class="retour btn_icone"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-undo2-icon lucide-undo-2"><path d="M9 14 4 9l5-5"/><path d="M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11"/></svg>retour</button></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="padding_left">
|
||||
<p class="sous-titre">Découvrez nos services tous plus utiles les uns que les autres !</p>
|
||||
<table border="1" class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nom</th>
|
||||
<th>Description</th>
|
||||
<th>Prix</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr>
|
||||
<td>Géolocalisation</td>
|
||||
<td>Afin de retrouver l'ensemble de vos véhicules sur une carte en temps réel !</td>
|
||||
<td>30€/véhicule/mois</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Odomètre</td>
|
||||
<td>Ayez accès à un killomètrage précis.</td>
|
||||
<td>200€</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Consommation</td>
|
||||
<td>Le carburant consommé par votre flotte.</td>
|
||||
<td>15£/véhicule/mois</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Impact Carbonne</td>
|
||||
<td>Ayez sous les yeux tous les chiffres concernant le CO2 rejeté par votre flotte</td>
|
||||
<td>30€/véhicule/mois</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<section class="simulateur">
|
||||
<h2>Estimez votre devis</h2>
|
||||
<p>Nombre de véhicules dans votre flotte :</p>
|
||||
<button id="moins" type="button">−</button>
|
||||
<span id="nb_vehicules">1</span>
|
||||
<button id="plus" type="button">+</button>
|
||||
<p>Total géolocalisation : <strong id="géolocalisation">30</strong> €/mois</p>
|
||||
<p>Total consommation : <strong id="consommation">15</strong> €/mois</p>
|
||||
</section>
|
||||
<div class="center">
|
||||
<div class="formulaire">
|
||||
<form action="" method="post">
|
||||
<p> Si vous avez des suggestions :</p>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="name">Nom :</label>
|
||||
<input type="text" placeholder="Dupond" id="name" name="user_name" />
|
||||
</li>
|
||||
<li>
|
||||
<label for="mail">E-mail :</label>
|
||||
<input type="email" placeholder="dupondrichard@gmail.com" id="mail" name="user_mail" />
|
||||
</li>
|
||||
<li>
|
||||
<label for="msg">Message :</label>
|
||||
<textarea id="msg" placeholder="Je pense qu'il faudrait..." name="user_message"
|
||||
row="6" cols="40" maxlength="500"></textarea>
|
||||
</li>
|
||||
<div id="boutton_formulaire">
|
||||
<button type="submit"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-send-icon lucide-send"><path d="M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z"/><path d="m21.854 2.147-10.94 10.939"/></svg>Envoyer le message</button>
|
||||
</div>
|
||||
</ul>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="footer">
|
||||
stagiaires rpz echoes
|
||||
</footer>
|
||||
<script src="css/devis.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user