vpi / index.html
Onyx4291's picture
Objectif Créer un **calculateur** permettant aux utilisateurs de savoir combien de **sacs de ragréage VPI** sont nécessaires pour une surface donnée, en fonction : * de la **surface de la pièce (m²)**, * de **l’épaisseur moyenne du ragréage (mm)**, * du **type de ragréage VPI choisi**. --- ## 🖥️ Interface Utilisateur (UI) ### 1. **Champs de saisie** : | Champ | Type | Détail | | ---------------------- | ------------------------------------ | ----------------------------------------- | | Surface (m²) | Champ numérique | Valeur positive décimale autorisée | | Épaisseur moyenne (mm) | Champ numérique | Valeur positive décimale autorisée | | Produit VPI | Menu déroulant (select) | Liste dynamique des produits ragréage VPI | | Marge de sécurité (%) | Slider (0% à 15%) ou champ numérique | Valeur facultative, par défaut 5% | --- ### 2. **Bouton de calcul** * Texte : `Calculer` * Action : déclenche le calcul --- ### 3. **Résultat affiché** | Élément | Contenu | | ------------------------------------------------------- | -------------------------------------------------- | | Nombre de sacs nécessaires | Valeur arrondie à l'entier supérieur | | Quantité totale de ragréage nécessaire (kg) | Nombre décimal | | Détail du produit choisi (nom, rendement, poids du sac) | Texte | | (Facultatif) Coût estimé si tarif du sac connu | Affiché uniquement si champ "prix unitaire" rempli | --- ## 🧠 Logique de calcul ### 1. Formule générale ``` volume_total_m3 = surface_m2 * épaisseur_mm / 1000 poids_total_kg = volume_total_m3 * densité_kg_m3 (donnée du produit VPI) nombre_de_sacs = poids_total_kg / poids_sac_kg nombre_de_sacs_final = arrondi supérieur(nombre_de_sacs * (1 + marge_de_sécurité/100)) ``` --- ## 📦 Données à intégrer : Produits VPI (base de référence) Voici un **exemple de structure JSON** pour la base de produits (à compléter ou connecter dynamiquement via un fichier ou une API si disponible) : ```json [ { "nom": "VPI RAGREAGE 400", "type": "Ragréage autolissant intérieur", "poids_sac_kg": 25, "rendement_m2_pour_10mm": 1.5, "densité_kg_m3": 1800 }, { "nom": "VPI RAGREAGE 500", "type": "Ragréage fibré extérieur", "poids_sac_kg": 25, "rendement_m2_pour_10mm": 1.4, "densité_kg_m3": 1900 }, { "nom": "VPI RAGREAGE 600", "type": "Ragréage à prise rapide", "poids_sac_kg": 20, "rendement_m2_pour_10mm": 1.2, "densité_kg_m3": 1850 } ] ``` ⚠️ À vérifier dans les **fiches techniques VPI** (sur leur site) pour les valeurs exactes. --- ## 📈 Exemple de calcul **Entrées utilisateur :** * Surface = 30 m² * Épaisseur = 8 mm * Produit = "VPI RAGREAGE 500" * Marge de sécurité = 5 % **Calcul :** 1. `volume = 30 × 8 / 1000 = 0.24 m³` 2. `poids_total = 0.24 × 1900 = 456 kg` 3. `nombre_de_sacs = 456 / 25 = 18.24` 4. `nombre_de_sacs_final = 18.24 × 1.05 = 19.15 → 20 sacs` --- ## 🔌 Extensions possibles * 🧾 Ajout champ "prix unitaire" pour estimation du coût * 🖨️ Bouton “Imprimer le résultat” * 📊 Graphique visuel pour visualiser la quantité nécessaire - Initial Deployment
da95101 verified
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Calculateur de ragréage VPI</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.slider-thumb::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
background: #3b82f6;
cursor: pointer;
border-radius: 50%;
}
.slider-thumb::-moz-range-thumb {
width: 20px;
height: 20px;
background: #3b82f6;
cursor: pointer;
border-radius: 50%;
}
.result-card {
background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.product-card {
transition: all 0.3s ease;
}
.product-card:hover {
transform: translateY(-5px);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
</style>
</head>
<body class="bg-gray-50 min-h-screen">
<div class="container mx-auto px-4 py-8">
<!-- Header -->
<header class="text-center mb-12">
<h1 class="text-3xl md:text-4xl font-bold text-gray-800 mb-2">
<i class="fas fa-calculator text-blue-500 mr-2"></i>
Calculateur de ragréage VPI
</h1>
<p class="text-gray-600 max-w-2xl mx-auto">
Calculez le nombre de sacs nécessaires pour votre projet de ragréage en fonction de la surface, de l'épaisseur et du produit VPI choisi.
</p>
</header>
<!-- Calculator Section -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
<!-- Input Form -->
<div class="bg-white rounded-xl shadow-md p-6 lg:col-span-2">
<h2 class="text-xl font-semibold text-gray-800 mb-6 flex items-center">
<i class="fas fa-edit text-blue-500 mr-2"></i>
Paramètres du projet
</h2>
<form id="calculatorForm">
<!-- Surface Input -->
<div class="mb-6">
<label for="surface" class="block text-sm font-medium text-gray-700 mb-1">
<i class="fas fa-ruler-combined text-blue-500 mr-1"></i>
Surface à ragréer (m²)
</label>
<div class="relative">
<input type="number" id="surface" step="0.01" min="0"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="Ex: 30.5" required>
<div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
<span class="text-gray-500"></span>
</div>
</div>
</div>
<!-- Thickness Input -->
<div class="mb-6">
<label for="thickness" class="block text-sm font-medium text-gray-700 mb-1">
<i class="fas fa-arrows-alt-v text-blue-500 mr-1"></i>
Épaisseur moyenne du ragréage (mm)
</label>
<div class="relative">
<input type="number" id="thickness" step="0.1" min="0"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="Ex: 8.5" required>
<div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
<span class="text-gray-500">mm</span>
</div>
</div>
</div>
<!-- Product Selection -->
<div class="mb-6">
<label for="product" class="block text-sm font-medium text-gray-700 mb-1">
<i class="fas fa-boxes text-blue-500 mr-1"></i>
Produit VPI
</label>
<select id="product"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" required>
<option value="" disabled selected>Sélectionnez un produit</option>
<!-- Options will be populated by JavaScript -->
</select>
</div>
<!-- Safety Margin Slider -->
<div class="mb-8">
<label for="safetyMargin" class="block text-sm font-medium text-gray-700 mb-1">
<i class="fas fa-shield-alt text-blue-500 mr-1"></i>
Marge de sécurité
<span id="safetyMarginValue" class="text-blue-600 font-medium">5%</span>
</label>
<div class="flex items-center space-x-4">
<input type="range" id="safetyMargin" min="0" max="15" value="5"
class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer slider-thumb">
<span class="text-gray-500 text-sm">0%</span>
<span class="text-gray-500 text-sm">15%</span>
</div>
</div>
<!-- Unit Price (Optional) -->
<div class="mb-6">
<label for="unitPrice" class="block text-sm font-medium text-gray-700 mb-1">
<i class="fas fa-tag text-blue-500 mr-1"></i>
Prix unitaire (optionnel)
</label>
<div class="relative">
<input type="number" id="unitPrice" step="0.01" min="0"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="Ex: 25.99">
<div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
<span class="text-gray-500"></span>
</div>
</div>
</div>
<!-- Calculate Button -->
<button type="submit"
class="w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-4 rounded-lg transition duration-300 flex items-center justify-center">
<i class="fas fa-calculator mr-2"></i>
Calculer
</button>
</form>
</div>
<!-- Results Section -->
<div id="resultsContainer" class="hidden">
<div class="result-card rounded-xl p-6 h-full">
<h2 class="text-xl font-semibold text-gray-800 mb-6 flex items-center">
<i class="fas fa-chart-bar text-blue-500 mr-2"></i>
Résultats du calcul
</h2>
<div class="space-y-4">
<!-- Number of Bags -->
<div class="bg-white p-4 rounded-lg shadow-sm">
<div class="flex items-center justify-between">
<div>
<h3 class="text-sm font-medium text-gray-500">Nombre de sacs nécessaires</h3>
<p id="bagsNeeded" class="text-2xl font-bold text-blue-600">0</p>
</div>
<div class="bg-blue-100 p-3 rounded-full">
<i class="fas fa-shopping-bag text-blue-600 text-xl"></i>
</div>
</div>
</div>
<!-- Total Quantity -->
<div class="bg-white p-4 rounded-lg shadow-sm">
<div class="flex items-center justify-between">
<div>
<h3 class="text-sm font-medium text-gray-500">Quantité totale nécessaire</h3>
<p id="totalQuantity" class="text-xl font-bold text-gray-800">0 kg</p>
</div>
<div class="bg-gray-100 p-3 rounded-full">
<i class="fas fa-weight text-gray-600 text-xl"></i>
</div>
</div>
</div>
<!-- Product Details -->
<div class="bg-white p-4 rounded-lg shadow-sm">
<h3 class="text-sm font-medium text-gray-500 mb-2">Détails du produit</h3>
<div id="productDetails" class="text-gray-700">
<p class="font-medium" id="productName">-</p>
<p class="text-sm" id="productType">-</p>
<p class="text-sm mt-1"><span class="font-medium">Rendement:</span> <span id="productYield">-</span> m²/sac pour 10mm</p>
<p class="text-sm"><span class="font-medium">Poids du sac:</span> <span id="productWeight">-</span> kg</p>
</div>
</div>
<!-- Estimated Cost -->
<div id="estimatedCostContainer" class="hidden bg-white p-4 rounded-lg shadow-sm">
<div class="flex items-center justify-between">
<div>
<h3 class="text-sm font-medium text-gray-500">Coût estimé</h3>
<p id="estimatedCost" class="text-xl font-bold text-green-600">0 €</p>
</div>
<div class="bg-green-100 p-3 rounded-full">
<i class="fas fa-euro-sign text-green-600 text-xl"></i>
</div>
</div>
</div>
<!-- Visual Representation -->
<div class="bg-white p-4 rounded-lg shadow-sm">
<h3 class="text-sm font-medium text-gray-500 mb-3">Répartition</h3>
<div class="flex items-center justify-center h-32">
<div id="visualBags" class="flex flex-wrap justify-center gap-2">
<!-- Bags will be visualized here -->
</div>
</div>
</div>
<!-- Print Button -->
<button id="printButton"
class="w-full mt-4 bg-gray-100 hover:bg-gray-200 text-gray-800 font-medium py-2 px-4 rounded-lg transition duration-300 flex items-center justify-center">
<i class="fas fa-print mr-2"></i>
Imprimer le résultat
</button>
</div>
</div>
</div>
<!-- Product Information (Initially shown, hidden after calculation) -->
<div id="productInfo" class="lg:col-span-1">
<div class="bg-white rounded-xl shadow-md p-6">
<h2 class="text-xl font-semibold text-gray-800 mb-6 flex items-center">
<i class="fas fa-info-circle text-blue-500 mr-2"></i>
Produits VPI disponibles
</h2>
<div class="space-y-4">
<!-- Product cards will be populated by JavaScript -->
</div>
<div class="mt-6 p-4 bg-blue-50 rounded-lg">
<h3 class="text-sm font-medium text-blue-800 mb-2 flex items-center">
<i class="fas fa-lightbulb text-blue-600 mr-2"></i>
Conseil professionnel
</h3>
<p class="text-xs text-blue-700">
Pour des résultats optimaux, prévoyez toujours une marge de sécurité de 5-10% pour tenir compte des pertes et des variations d'épaisseur.
</p>
</div>
</div>
</div>
</div>
</div>
<script>
// Product data
const products = [
{
"id": 1,
"nom": "VPI RAGREAGE 400",
"type": "Ragréage autolissant intérieur",
"poids_sac_kg": 25,
"rendement_m2_pour_10mm": 1.5,
"densité_kg_m3": 1800,
"description": "Idéal pour les sols intérieurs, offre une excellente planéité et facilite la pose des revêtements.",
"color": "bg-blue-100 text-blue-800"
},
{
"id": 2,
"nom": "VPI RAGREAGE 500",
"type": "Ragréage fibré extérieur",
"poids_sac_kg": 25,
"rendement_m2_pour_10mm": 1.4,
"densité_kg_m3": 1900,
"description": "Parfait pour les applications extérieures, résistant aux intempéries grâce à ses fibres.",
"color": "bg-green-100 text-green-800"
},
{
"id": 3,
"nom": "VPI RAGREAGE 600",
"type": "Ragréage à prise rapide",
"poids_sac_kg": 20,
"rendement_m2_pour_10mm": 1.2,
"densité_kg_m3": 1850,
"description": "Temps de prise accéléré pour les projets nécessitant une intervention rapide.",
"color": "bg-orange-100 text-orange-800"
}
];
// DOM elements
const calculatorForm = document.getElementById('calculatorForm');
const productSelect = document.getElementById('product');
const safetyMarginSlider = document.getElementById('safetyMargin');
const safetyMarginValue = document.getElementById('safetyMarginValue');
const resultsContainer = document.getElementById('resultsContainer');
const productInfo = document.getElementById('productInfo');
const productDetails = document.getElementById('productDetails');
const bagsNeeded = document.getElementById('bagsNeeded');
const totalQuantity = document.getElementById('totalQuantity');
const productName = document.getElementById('productName');
const productType = document.getElementById('productType');
const productYield = document.getElementById('productYield');
const productWeight = document.getElementById('productWeight');
const estimatedCostContainer = document.getElementById('estimatedCostContainer');
const estimatedCost = document.getElementById('estimatedCost');
const visualBags = document.getElementById('visualBags');
const printButton = document.getElementById('printButton');
const productCardsContainer = document.querySelector('#productInfo .space-y-4');
// Initialize the app
function init() {
// Populate product select dropdown
products.forEach(product => {
const option = document.createElement('option');
option.value = product.id;
option.textContent = `${product.nom} (${product.type})`;
productSelect.appendChild(option);
});
// Populate product cards
products.forEach(product => {
const card = document.createElement('div');
card.className = `product-card bg-white p-4 rounded-lg border border-gray-200 ${product.color.replace('text', 'border')}`;
card.innerHTML = `
<h3 class="font-medium mb-1">${product.nom}</h3>
<p class="text-xs text-gray-600 mb-2">${product.type}</p>
<div class="flex justify-between text-sm">
<div>
<p><span class="font-medium">Poids:</span> ${product.poids_sac_kg} kg</p>
<p><span class="font-medium">Rendement:</span> ${product.rendement_m2_pour_10mm} m²/sac (10mm)</p>
</div>
<div class="flex items-center">
<span class="px-2 py-1 rounded-full ${product.color} text-xs font-medium">VPI</span>
</div>
</div>
`;
productCardsContainer.appendChild(card);
});
// Update safety margin value display
safetyMarginSlider.addEventListener('input', () => {
safetyMarginValue.textContent = `${safetyMarginSlider.value}%`;
});
// Handle form submission
calculatorForm.addEventListener('submit', calculate);
// Handle print button
printButton.addEventListener('click', () => {
window.print();
});
}
// Calculation function
function calculate(e) {
e.preventDefault();
// Get input values
const surface = parseFloat(document.getElementById('surface').value);
const thickness = parseFloat(document.getElementById('thickness').value);
const productId = parseInt(document.getElementById('product').value);
const safetyMargin = parseInt(document.getElementById('safetyMargin').value) / 100;
const unitPrice = parseFloat(document.getElementById('unitPrice').value) || 0;
// Find selected product
const selectedProduct = products.find(p => p.id === productId);
// Perform calculations
const volume = surface * thickness / 1000; // m³
const totalWeight = volume * selectedProduct.densité_kg_m3; // kg
const bags = totalWeight / selectedProduct.poids_sac_kg;
const bagsWithMargin = bags * (1 + safetyMargin);
const roundedBags = Math.ceil(bagsWithMargin);
// Calculate estimated cost if unit price is provided
const totalCost = roundedBags * unitPrice;
// Display results
bagsNeeded.textContent = roundedBags;
totalQuantity.textContent = `${totalWeight.toFixed(2)} kg`;
productName.textContent = selectedProduct.nom;
productType.textContent = selectedProduct.type;
productYield.textContent = selectedProduct.rendement_m2_pour_10mm;
productWeight.textContent = selectedProduct.poids_sac_kg;
// Show/hide estimated cost
if (unitPrice > 0) {
estimatedCost.textContent = `${totalCost.toFixed(2)} €`;
estimatedCostContainer.classList.remove('hidden');
} else {
estimatedCostContainer.classList.add('hidden');
}
// Visual representation of bags
visualBags.innerHTML = '';
const maxBagsToShow = 20;
const bagsToShow = roundedBags <= maxBagsToShow ? roundedBags : maxBagsToShow;
for (let i = 0; i < bagsToShow; i++) {
const bag = document.createElement('div');
bag.className = 'w-8 h-10 bg-blue-200 rounded flex items-center justify-center';
bag.innerHTML = '<i class="fas fa-shopping-bag text-blue-600 text-xs"></i>';
visualBags.appendChild(bag);
}
if (roundedBags > maxBagsToShow) {
const moreBags = document.createElement('div');
moreBags.className = 'w-8 h-10 bg-blue-100 rounded flex items-center justify-center text-xs font-medium';
moreBags.textContent = `+${roundedBags - maxBagsToShow}`;
visualBags.appendChild(moreBags);
}
// Show results and hide product info
resultsContainer.classList.remove('hidden');
productInfo.classList.add('hidden');
// Scroll to results
resultsContainer.scrollIntoView({ behavior: 'smooth' });
}
// Initialize the app when DOM is loaded
document.addEventListener('DOMContentLoaded', init);
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Onyx4291/vpi" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>