feat: Add Uziomy calculator page with grounding calculations and document generation

- Implemented Uziomy component for calculating grounding parameters.
- Added state management for input fields and results.
- Integrated DatePicker for date selection.
- Created functions for grounding calculations, document generation (DOCX), and DXF file generation.
- Enhanced UI with Tailwind CSS for better styling and responsiveness.
- Updated global styles to include Inter font and custom scrollbar styles.
- Configured Tailwind CSS to extend colors, fonts, and animations.
This commit is contained in:
2025-07-01 11:43:34 +02:00
parent 5c11a289df
commit d3ecfae5df
21 changed files with 4224 additions and 1022 deletions

View File

@@ -0,0 +1,241 @@
import { useState } from "react";
import { Card, CardHeader, CardContent, CardTitle, CardDescription, Button, Input } from "../ui/components";
import { PencilIcon, PlusIcon, TrashIcon, ArrowDownTrayIcon as DownloadIcon } from '@heroicons/react/24/outline';
import axios from "axios";
export default function Manual() {
const [points, setPoints] = useState([{ id: 0, len: 0, height: 0 }]);
const [args, setArgs] = useState({ scale: 200 });
const [isLoading, setIsLoading] = useState(false);
const reIndex = () => {
let newId = 0;
let newPoints = [];
for (let point of points) {
point.id = newId;
newPoints.push(point);
newId += 1;
}
setPoints([...newPoints]);
};
const addPoint = () => {
setPoints([
...points,
{
id: points.length,
len: Number(points[points.length - 1].len) + 1,
height: 0,
},
]);
};
const removePoint = (index) => {
let newPoints = points;
newPoints.splice(index, 1);
setPoints([...newPoints]);
reIndex();
};
const updatePoint = (index, field, value) => {
let newPoints = points;
newPoints[index][field] = value;
setPoints([...newPoints]);
};
const generation = (e) => {
setIsLoading(true);
let pointString = "Próbkowanie: 1\nSegment 0: w dół\nX: Y, Z";
for (let point of points) {
pointString += "\n";
pointString += Number(point.len) * 1.0;
pointString += ", 1.0, ";
pointString += point.height;
}
py(e, pointString, args);
};
const py = (e, profil, args) => {
e.preventDefault();
axios
.post("/api/spawn", {
profil: profil,
arguments: args,
})
.then(function (response) {
setIsLoading(false);
console.log(response);
if (response.data.toString().startsWith("Py Error")) {
alert("Błąd: " + response.data);
return;
}
document.getElementById("down").download = response.data.filename.toString() + ".dxf";
document.getElementById("down").click();
})
.catch(function (error) {
setIsLoading(false);
console.log(error);
});
};
return (
<div className="grid grid-cols-1 xl:grid-cols-2 gap-8">
{/* Input Section */}
<div className="space-y-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<PencilIcon className="w-5 h-5 text-blue-600" />
<span>Punkty profilu</span>
</CardTitle>
<CardDescription>
Wprowadź punkty profilu ręcznie - długość i wysokość nad poziomem morza
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
{/* Scale Input */}
<Input
label="Skala"
type="number"
placeholder="200"
value={args.scale}
onChange={(e) => setArgs({ ...args, scale: e.target.value })}
/>
{/* Points List */}
<div className="space-y-3 max-h-96 overflow-y-auto">
{points.map((point, index) => (
<div key={point.id} className="grid grid-cols-2 gap-3 p-3 bg-gray-50 rounded-lg">
<Input
label={`Punkt ${index + 1} - Długość (m)`}
type="number"
placeholder="0"
value={point.len}
onChange={(e) => updatePoint(index, 'len', e.target.value)}
/>
<div className="flex space-x-2">
<Input
label="Wysokość (m n.p.m.)"
type="number"
placeholder="0"
value={point.height}
onChange={(e) => updatePoint(index, 'height', e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter") {
addPoint();
// Focus next input after render
setTimeout(() => {
const nextInput = e.target.parentNode.parentNode.parentNode.nextSibling?.querySelector('input');
if (nextInput) nextInput.focus();
}, 100);
}
}}
/>
{points.length > 1 && (
<button
onClick={() => removePoint(index)}
className="self-end p-2 text-red-600 hover:bg-red-50 rounded-lg transition-colors"
title="Usuń punkt"
>
<TrashIcon className="w-5 h-5" />
</button>
)}
</div>
</div>
))}
</div>
{/* Action Buttons */}
<div className="flex space-x-3">
<Button
onClick={addPoint}
variant="outline"
className="flex-1"
>
<PlusIcon className="w-5 h-5 mr-2" />
Dodaj punkt
</Button>
<Button
onClick={generation}
disabled={isLoading}
loading={isLoading}
className="flex-1"
>
<DownloadIcon className="w-5 h-5 mr-2" />
{isLoading ? 'Generowanie...' : 'Generuj profil'}
</Button>
</div>
</CardContent>
</Card>
</div>
{/* Preview/Instructions Section */}
<div className="space-y-6">
<Card>
<CardHeader>
<CardTitle>Podgląd punktów</CardTitle>
<CardDescription>
Lista wprowadzonych punktów profilu
</CardDescription>
</CardHeader>
<CardContent>
{points.length > 0 ? (
<div className="space-y-2">
{points.map((point, index) => (
<div key={point.id} className="flex justify-between items-center p-3 bg-gray-50 rounded-lg">
<span className="font-medium text-gray-900">Punkt {index + 1}</span>
<div className="text-sm text-gray-600">
<span className="mr-4">Długość: {point.len}m</span>
<span>Wysokość: {point.height}m</span>
</div>
</div>
))}
</div>
) : (
<div className="text-center py-8">
<div className="mx-auto w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mb-4">
<PencilIcon className="w-8 h-8 text-gray-400" />
</div>
<p className="text-gray-500">Brak punktów do wyświetlenia</p>
</div>
)}
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Instrukcje</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-3 text-sm">
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-xs font-semibold text-blue-600">1</span>
</div>
<p className="text-gray-600">Wprowadź kolejne punkty profilu z długością i wysokością</p>
</div>
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-xs font-semibold text-blue-600">2</span>
</div>
<p className="text-gray-600">Użyj klawisza Enter aby szybko dodać następny punkt</p>
</div>
<div className="flex items-start space-x-3">
<div className="flex-shrink-0 w-6 h-6 bg-blue-100 rounded-full flex items-center justify-center">
<span className="text-xs font-semibold text-blue-600">3</span>
</div>
<p className="text-gray-600">Ustaw skalę i kliknij "Generuj profil"</p>
</div>
</div>
</CardContent>
</Card>
</div>
<a href="test.dxf" download="test.dxf" id="down" className="hidden" />
</div>
);
}