Càlcul preus

import React, { useState, useEffect, useMemo } from ‘react’; import { Settings, Calculator, Plus, Trash2, Layout, FileDown, Edit3, Save, X, Info, Upload, Download, AlertCircle } from ‘lucide-react’; export default function App() { // — PERSISTÈNCIA I ESTATS — const [materials, setMaterials] = useState(() => { const saved = localStorage.getItem(‘calc_materials’); return saved ? JSON.parse(saved) : [ { id: 1, nom: ‘Fusta de Pi’, tipus: ‘area’, costBase: 25, marge: 50, escalonats: [{ min: 5, preu: 22 }, { min: 10, preu: 18 }] }, { id: 2, nom: ‘Fotocòpies Color’, tipus: ‘unitat’, costBase: 1, marge: 100, escalonats: [{ min: 100, preu: 0.50 }, { min: 200, preu: 0.10 }] } ]; }); useEffect(() => { localStorage.setItem(‘calc_materials’, JSON.stringify(materials)); }, [materials]); const [view, setView] = useState(‘client’); const [editingId, setEditingId] = useState(null); const [bulkText, setBulkText] = useState(”); const [showBulk, setShowBulk] = useState(false); // — ESTATS CALCULADORA CLIENT — const [selectedId, setSelectedId] = useState(”); const [width, setWidth] = useState(”); const [height, setHeight] = useState(”); const [quantity, setQuantity] = useState(1); // — FORMULARI EDICIÓ/CREACIÓ — const [form, setForm] = useState({ nom: ”, tipus: ‘area’, costBase: ”, marge: ”, escalonats: [] }); // — LÒGICA DE CÀLCUL — const calculateResult = useMemo(() => { const item = materials.find(m => m.id === parseInt(selectedId)); if (!item) return null; let preuBaseAplicat = item.costBase; const totalMesura = item.tipus === ‘area’ ? (parseFloat(width || 0) * parseFloat(height || 0) / 10000) * quantity : quantity; if (item.escalonats && item.escalonats.length > 0) { const sortedTiers = […item.escalonats].sort((a, b) => b.min – a.min); const tier = sortedTiers.find(t => totalMesura >= t.min); if (tier) preuBaseAplicat = tier.preu; } const subtotalCost = totalMesura * preuBaseAplicat; const subtotalPVP = subtotalCost * (1 + item.marge / 100); const iva = subtotalPVP * 0.21; const totalAmbIva = subtotalPVP + iva; return { subtotalPVP: subtotalPVP.toFixed(2), iva: iva.toFixed(2), total: totalAmbIva.toFixed(2), preuUnitariPVP: (subtotalPVP / quantity).toFixed(2), mesuraTotal: totalMesura.toFixed(2), nom: item.nom, tipus: item.tipus }; }, [selectedId, width, height, quantity, materials]); // — ACCIONS ADMIN — const handleSave = () => { if (!form.nom || !form.costBase) return; const newMaterial = { …form, id: editingId || Date.now(), costBase: parseFloat(form.costBase), marge: parseFloat(form.marge || 0), escalonats: form.escalonats.map(e => ({ min: parseFloat(e.min), preu: parseFloat(e.preu) })) }; if (editingId) { setMaterials(materials.map(m => m.id === editingId ? newMaterial : m)); } else { setMaterials([…materials, newMaterial]); } resetForm(); }; const resetForm = () => { setForm({ nom: ”, tipus: ‘area’, costBase: ”, marge: ”, escalonats: [] }); setEditingId(null); }; const handleBulkImport = () => { try { const lines = bulkText.trim().split(‘\n’); const newItems = lines.map(line => { const [nom, tipus, costBase, marge] = line.split(‘,’); return { id: Math.random() * 100000, nom: nom.trim(), tipus: tipus.trim() === ‘unitat’ ? ‘unitat’ : ‘area’, costBase: parseFloat(costBase), marge: parseFloat(marge), escalonats: [] }; }); setMaterials([…materials, …newItems]); setBulkText(”); setShowBulk(false); } catch (e) { console.error(“Error en format CSV”); } }; const handlePrint = () => window.print(); return (
{/* Navbar per WordPress */}
{/* VISTA CLIENT */} {view === ‘client’ && (

Calcula el teu Pressupost

{selectedId && materials.find(m => m.id === parseInt(selectedId))?.tipus === ‘area’ && (
setWidth(e.target.value)} className=”p-4 bg-gray-50 border border-gray-200 rounded-2xl” placeholder=”Amplada (cm)”/> setHeight(e.target.value)} className=”p-4 bg-gray-50 border border-gray-200 rounded-2xl” placeholder=”Alçada (cm)”/>
)} setQuantity(Math.max(1, parseInt(e.target.value) || 1))} className=”w-full p-4 bg-gray-50 border border-gray-200 rounded-2xl” placeholder=”Quantitat”/>
{calculateResult ? (

Pressupost Professional

{calculateResult.nom}

{calculateResult.total} € (PVP)
Preu SENSE IVA{calculateResult.subtotalPVP} €
IVA (21%){calculateResult.iva} €
Total amb IVA{calculateResult.total} €

* Aquest pressupost és orientatiu i pot variar segons la complexitat de la instal·lació.

) : (

Selecciona un producte per començar.

)}
)} {/* VISTA ADMIN */} {view === ‘admin’ && (

Gestió de Productes

{showBulk && (

Importació Massiva (CSV)

Enganxa una línia per producte: Nom, tipus (area/unitat), costBase, marge