Naprawa obsługi GPX, czyszczenie projektu, naprawa błędów związanych z obrazami
This commit is contained in:
@@ -1,128 +1 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { WizardState } from '../types';
|
||||
import { UploadCloud, FileJson, AlertCircle } from 'lucide-react';
|
||||
import { parseGpxFile } from '../utils/gpxUtils';
|
||||
|
||||
interface StepDataProps {
|
||||
data: WizardState;
|
||||
updateData: (updates: Partial<WizardState>) => void;
|
||||
}
|
||||
|
||||
const StepData: React.FC<StepDataProps> = ({ data, updateData }) => {
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [isParsing, setIsParsing] = useState(false);
|
||||
|
||||
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
if (!file.name.toLowerCase().endsWith('.gpx')) {
|
||||
setError('Proszę wybrać plik .gpx');
|
||||
return;
|
||||
}
|
||||
|
||||
setError(null);
|
||||
setIsParsing(true);
|
||||
|
||||
try {
|
||||
const stats = await parseGpxFile(file);
|
||||
updateData({ stats });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
setError('Błąd parsowania pliku GPX. Spróbuj innego pliku lub wpisz dane ręcznie.');
|
||||
} finally {
|
||||
setIsParsing(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleStatsChange = (key: keyof typeof data.stats, value: string) => {
|
||||
updateData({
|
||||
stats: {
|
||||
...data.stats,
|
||||
[key]: value
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-10 animate-fade-in">
|
||||
<div>
|
||||
<h2 className="text-3xl font-bold tracking-tight text-gray-900 mb-3">Dane Aktywności</h2>
|
||||
<p className="text-gray-500 mb-8 text-lg">Wgraj plik GPX lub wpisz dane ręcznie.</p>
|
||||
</div>
|
||||
|
||||
{/* Upload Zone */}
|
||||
<div
|
||||
className={`border-2 border-dashed rounded-md p-10 flex flex-col items-center justify-center text-center transition-all cursor-pointer group ${
|
||||
error ? 'border-red-300 bg-red-50' : 'border-gray-200 hover:border-[#EA4420] hover:bg-[#EA4420]/5'
|
||||
}`}
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
>
|
||||
<input
|
||||
type="file"
|
||||
ref={fileInputRef}
|
||||
onChange={handleFileChange}
|
||||
accept=".gpx"
|
||||
className="hidden"
|
||||
/>
|
||||
|
||||
{isParsing ? (
|
||||
<div className="animate-pulse flex flex-col items-center">
|
||||
<FileJson size={48} className="text-[#EA4420] mb-4 stroke-1" />
|
||||
<p className="text-[#EA4420] font-semibold">Analizowanie pliku...</p>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<UploadCloud size={48} className="text-gray-300 group-hover:text-[#EA4420] mb-4 stroke-1 transition-colors" />
|
||||
<p className="text-gray-900 font-bold text-lg">Kliknij, aby wgrać plik GPX</p>
|
||||
<p className="text-gray-500 text-sm mt-2">lub przeciągnij i upuść tutaj</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="flex items-center space-x-2 text-red-600 bg-red-50 p-4 rounded-md text-sm border border-red-100">
|
||||
<AlertCircle size={18} />
|
||||
<span className="font-medium">{error}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Manual Override Inputs */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<div>
|
||||
<label className="block text-sm font-bold text-gray-700 mb-2">Dystans</label>
|
||||
<input
|
||||
type="text"
|
||||
value={data.stats.distance}
|
||||
onChange={(e) => handleStatsChange('distance', e.target.value)}
|
||||
className="w-full p-4 border border-gray-200 rounded-md focus:ring-1 focus:ring-[#EA4420] focus:border-[#EA4420] outline-none transition-all font-medium text-gray-900 placeholder-gray-300"
|
||||
placeholder="np. 12.5 km"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-bold text-gray-700 mb-2">Czas trwania</label>
|
||||
<input
|
||||
type="text"
|
||||
value={data.stats.duration}
|
||||
onChange={(e) => handleStatsChange('duration', e.target.value)}
|
||||
className="w-full p-4 border border-gray-200 rounded-md focus:ring-1 focus:ring-[#EA4420] focus:border-[#EA4420] outline-none transition-all font-medium text-gray-900 placeholder-gray-300"
|
||||
placeholder="np. 1h 45m"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-bold text-gray-700 mb-2">Przewyższenia</label>
|
||||
<input
|
||||
type="text"
|
||||
value={data.stats.elevation}
|
||||
onChange={(e) => handleStatsChange('elevation', e.target.value)}
|
||||
className="w-full p-4 border border-gray-200 rounded-md focus:ring-1 focus:ring-[#EA4420] focus:border-[#EA4420] outline-none transition-all font-medium text-gray-900 placeholder-gray-300"
|
||||
placeholder="np. 350m"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StepData;
|
||||
// Unused. Logic moved to StepDetails.tsx
|
||||
Reference in New Issue
Block a user