import React, { useState, useMemo } from "react";
import { Calculator, Plus, Minus, Save, Trash, ChevronDown, ChevronUp } from "lucide-react";
import * as XLSX from "xlsx";
const Field = ({ label, suffix, value, onChange, placeholder = "", step = 0.1, type = "number" }) => (
onChange(type === 'number' ? (parseFloat(e.target.value) || 0) : e.target.value)}
style={{ padding: '6px 8px', width: '100%', boxSizing: 'border-box' }}
/>
{suffix && {suffix}}
);
export default function MultiRoomConstructionCalculator() {
const [roomName, setRoomName] = useState("");
const [length, setLength] = useState("");
const [width, setWidth] = useState("");
const [height, setHeight] = useState("");
const [windows, setWindows] = useState([]);
const [doors, setDoors] = useState([]);
const [rooms, setRooms] = useState([]);
const [expandedRooms, setExpandedRooms] = useState([]);
const addWindow = () => setWindows([...windows, { width: 1.2, height: 1.5 }]);
const removeWindow = (i) => setWindows(windows.filter((_, idx) => idx !== i));
const updateWindow = (i, key, value) => {
const updated = [...windows];
updated[i][key] = value;
setWindows(updated);
};
const addDoor = () => setDoors([...doors, { width: 0.9, height: 2.0 }]);
const removeDoor = (i) => setDoors(doors.filter((_, idx) => idx !== i));
const updateDoor = (i, key, value) => {
const updated = [...doors];
updated[i][key] = value;
setDoors(updated);
};
const results = useMemo(() => {
if (!length || !width || !height) return null;
const perimeter = 2 * (parseFloat(length) + parseFloat(width));
const wallArea = perimeter * parseFloat(height);
const floorArea = parseFloat(length) * parseFloat(width);
const windowsArea = windows.reduce((sum, o) => sum + (parseFloat(o.width) || 0) * (parseFloat(o.height) || 0), 0);
const doorsArea = doors.reduce((sum, o) => sum + (parseFloat(o.width) || 0) * (parseFloat(o.height) || 0), 0);
const netArea = wallArea - (windowsArea + doorsArea);
return { perimeter, wallArea, floorArea, windowsArea, doorsArea, netArea };
}, [length, width, height, windows, doors]);
const saveRoom = () => {
if (!results || !roomName) return;
setRooms([
...rooms,
{
name: roomName,
perimeter: results.perimeter,
wallArea: results.wallArea,
floorArea: results.floorArea,
windowsArea: results.windowsArea,
doorsArea: results.doorsArea,
netArea: results.netArea,
},
]);
setRoomName("");
setLength("");
setWidth("");
setHeight("");
setWindows([]);
setDoors([]);
};
const deleteRoom = (i) => setRooms(rooms.filter((_, idx) => idx !== i));
const toggleExpand = (i) => {
if (expandedRooms.includes(i)) {
setExpandedRooms(expandedRooms.filter(idx => idx !== i));
} else {
setExpandedRooms([...expandedRooms, i]);
}
};
const exportExcel = () => {
if (!results && rooms.length === 0) return;
const wsData = [
["Помещение", "Периметр (м)", "Площадь стен без вычета (м²)", "Площадь пола (м²)", "Площадь окон (м²)", "Площадь дверей (м²)", "Площадь стен с вычетом (м²)"],
...rooms.map(r => [r.name, r.perimeter, r.wallArea, r.floorArea, r.windowsArea, r.doorsArea, r.netArea]),
];
if (results && roomName) {
wsData.push([roomName, results.perimeter, results.wallArea, results.floorArea, results.windowsArea, results.doorsArea, results.netArea]);
}
const totalPerimeter = rooms.reduce((sum, r) => sum + r.perimeter, results ? results.perimeter : 0);
const totalWall = rooms.reduce((sum, r) => sum + r.wallArea, results ? results.wallArea : 0);
const totalFloor = rooms.reduce((sum, r) => sum + r.floorArea, results ? results.floorArea : 0);
const totalWindows = rooms.reduce((sum, r) => sum + r.windowsArea, results ? results.windowsArea : 0);
const totalDoors = rooms.reduce((sum, r) => sum + r.doorsArea, results ? results.doorsArea : 0);
const totalNet = rooms.reduce((sum, r) => sum + r.netArea, results ? results.netArea : 0);
wsData.push(["ИТОГО", totalPerimeter, totalWall, totalFloor, totalWindows, totalDoors, totalNet]);
const ws = XLSX.utils.aoa_to_sheet(wsData);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Rooms Report");
const today = new Date().toISOString().split("T")[0];
XLSX.writeFile(wb, `report_${today}.xlsx`, { compression: true });
};
return (
Строительный Калькулятор
Добавляйте несколько помещений, учитывайте окна и двери и выгружайте общий отчёт в Excel.
Окна
{windows.map((o, i) => (
updateWindow(i, 'width', v)} /> updateWindow(i, 'height', v)} />
))}
Двери
{doors.map((o, i) => (
updateDoor(i, 'width', v)} /> updateDoor(i, 'height', v)} />
))}
{rooms.map((r, i) => (
toggleExpand(i)} style={{ cursor: 'pointer', fontWeight: 'bold' }}>{r.name}
{expandedRooms.includes(i) && (
Периметр: {r.perimeter.toFixed(2)} м
Площадь стен без вычета: {r.wallArea.toFixed(2)} м²
Площадь пола: {r.floorArea.toFixed(2)} м²
Площадь окон: {r.windowsArea.toFixed(2)} м²
Площадь дверей: {r.doorsArea.toFixed(2)} м²
Площадь стен с вычетом: {r.netArea.toFixed(2)} м²
)}
))}
);
}