Files
kentekengen/src_frontend_orig/CarManager.jsx
2026-03-01 13:23:49 +00:00

460 lines
15 KiB
JavaScript

import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { Header } from './components/Header.jsx';
import { Card } from './components/Card.jsx';
import { Button } from './components/Button.jsx';
import { PageTransition } from './components/PageTransition.jsx';
import {
getCarsFolder,
loadCars,
resetCarsFolder,
saveCars,
selectCarsFolder,
} from './carStorage.js';
// Admin interface to add, edit, and remove car size presets.
export default function CarManager() {
const navigate = useNavigate();
const [options, setOptions] = useState([]);
const [addForm, setAddForm] = useState({
name: '',
width: '',
height: '',
hasRear: false,
rearWidth: '',
rearHeight: '',
});
const [editName, setEditName] = useState('');
const [editForm, setEditForm] = useState({
name: '',
width: '',
height: '',
hasRear: false,
rearWidth: '',
rearHeight: '',
});
const [deleteName, setDeleteName] = useState('');
const [password, setPassword] = useState('');
const [authenticated, setAuthenticated] = useState(false);
const [error, setError] = useState('');
const [toast, setToast] = useState(false);
const [carsFolder, setCarsFolder] = useState('');
const [folderMessage, setFolderMessage] = useState('');
const applyCars = (cars) => {
const sorted = [...cars].sort((a, b) => a.name.localeCompare(b.name));
setOptions(sorted);
setEditName(sorted[0]?.name || '');
setDeleteName(sorted[0]?.name || '');
};
useEffect(() => {
loadCars().then((opts) => {
applyCars(opts);
});
getCarsFolder().then((folder) => {
setCarsFolder(folder || '');
});
}, []);
useEffect(() => {
const car = options.find((o) => o.name === editName);
if (car) {
setEditForm({
name: car.name,
width: String(car.width),
height: String(car.height),
hasRear: Boolean(car.rearWidth && car.rearHeight),
rearWidth: car.rearWidth ? String(car.rearWidth) : '',
rearHeight: car.rearHeight ? String(car.rearHeight) : '',
});
}
}, [editName, options]);
useEffect(() => {
setDeleteName((dn) =>
options.find((o) => o.name === dn) ? dn : options[0]?.name || ''
);
}, [options]);
useEffect(() => {
if (toast) {
const t = setTimeout(() => setToast(false), 2000);
return () => clearTimeout(t);
}
}, [toast]);
useEffect(() => {
if (folderMessage) {
const t = setTimeout(() => setFolderMessage(''), 2500);
return () => clearTimeout(t);
}
}, [folderMessage]);
const handleAddChange = (e) => {
const { name, value, type, checked } = e.target;
setAddForm((fd) => ({
...fd,
[name]: type === 'checkbox' ? checked : value,
}));
};
const handleEditChange = (e) => {
const { name, value, type, checked } = e.target;
setEditForm((fd) => ({
...fd,
[name]: type === 'checkbox' ? checked : value,
}));
};
const handleAddSubmit = async (e) => {
e.preventDefault();
const newCar = {
name: addForm.name,
width: parseFloat(addForm.width),
height: parseFloat(addForm.height),
};
if (addForm.hasRear) {
newCar.rearWidth = parseFloat(addForm.rearWidth);
newCar.rearHeight = parseFloat(addForm.rearHeight);
}
const newOptions = [...options, newCar].sort((a, b) =>
a.name.localeCompare(b.name)
);
setOptions(newOptions);
await saveCars(newOptions);
setAddForm({
name: '',
width: '',
height: '',
hasRear: false,
rearWidth: '',
rearHeight: '',
});
setToast(true);
};
const handleEditSubmit = async (e) => {
e.preventDefault();
const updatedCar = {
name: editForm.name,
width: parseFloat(editForm.width),
height: parseFloat(editForm.height),
};
if (editForm.hasRear) {
updatedCar.rearWidth = parseFloat(editForm.rearWidth);
updatedCar.rearHeight = parseFloat(editForm.rearHeight);
}
const newOptions = options
.map((o) => (o.name === editName ? updatedCar : o))
.sort((a, b) => a.name.localeCompare(b.name));
setOptions(newOptions);
setEditName(updatedCar.name);
await saveCars(newOptions);
setToast(true);
};
const handleDeleteSubmit = async (e) => {
e.preventDefault();
const newOptions = options.filter((o) => o.name !== deleteName);
setOptions(newOptions);
setDeleteName(newOptions[0]?.name || '');
await saveCars(newOptions);
setToast(true);
};
const handleAuthSubmit = (e) => {
e.preventDefault();
if (password === 'Rotterdam-010') {
setAuthenticated(true);
setPassword('');
setError('');
} else {
setError('Onjuist wachtwoord');
}
};
const handleSelectFolder = async () => {
const result = await selectCarsFolder();
if (!result || result.canceled) {
setFolderMessage('Geen map gekozen');
return;
}
setCarsFolder(result.folder || '');
if (result.cars) {
applyCars(result.cars);
} else {
const refreshed = await loadCars();
applyCars(refreshed);
}
setFolderMessage('Opslaglocatie bijgewerkt');
};
const handleResetFolder = async () => {
const result = await resetCarsFolder();
if (result?.folder) {
setCarsFolder(result.folder);
}
if (result?.cars) {
applyCars(result.cars);
} else {
const refreshed = await loadCars();
applyCars(refreshed);
}
setFolderMessage('Standaardmap ingesteld');
};
return (
<PageTransition>
<Header
count={options.length}
title="Kinderauto's beheren"
onHome={() => navigate('/')}
/>
{!authenticated ? (
<div className="container mt-6">
<Card title="Toegang vereist">
<form onSubmit={handleAuthSubmit} className="mb-lg">
<div className="form-row">
<label htmlFor="managerPassword">Wachtwoord:</label>
<input
id="managerPassword"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
</div>
{error && <p className="text-sm text-red-500">{error}</p>}
<Button type="submit" variant="primary" className="mt">Inloggen</Button>
</form>
</Card>
</div>
) : (
<div className="container mt-6">
<aside className="side-cards">
<Card title="Opslaglocatie">
<p className="text-sm">
Huidige map: <strong>{carsFolder || 'Standaard'}</strong>
</p>
<div className="button-row mt">
<Button type="button" variant="primary" onClick={handleSelectFolder}>
Kies map
</Button>
<Button type="button" variant="ghost" onClick={handleResetFolder}>
Standaardmap
</Button>
</div>
{folderMessage && <p className="text-sm mt">{folderMessage}</p>}
</Card>
<Card title="Kinderauto toevoegen">
<form onSubmit={handleAddSubmit} className="mb-lg">
<div className="form-row">
<label htmlFor="addName">Naam:</label>
<input
id="addName"
name="name"
value={addForm.name}
onChange={handleAddChange}
required
/>
</div>
<div className="form-row">
<label htmlFor="addWidth">Breedte (cm):</label>
<input
id="addWidth"
type="number"
step="0.1"
min="0"
name="width"
value={addForm.width}
onChange={handleAddChange}
required
/>
</div>
<div className="form-row">
<label htmlFor="addHeight">Hoogte (cm):</label>
<input
id="addHeight"
type="number"
step="0.1"
min="0"
name="height"
value={addForm.height}
onChange={handleAddChange}
required
/>
</div>
<div className="form-row checkbox">
<label>
<input
type="checkbox"
name="hasRear"
checked={addForm.hasRear}
onChange={handleAddChange}
/>
Achter heeft andere maat
</label>
</div>
{addForm.hasRear && (
<>
<div className="form-row">
<label htmlFor="addRearWidth">Breedte achter (cm):</label>
<input
id="addRearWidth"
type="number"
step="0.1"
min="0"
name="rearWidth"
value={addForm.rearWidth}
onChange={handleAddChange}
required
/>
</div>
<div className="form-row">
<label htmlFor="addRearHeight">Hoogte achter (cm):</label>
<input
id="addRearHeight"
type="number"
step="0.1"
min="0"
name="rearHeight"
value={addForm.rearHeight}
onChange={handleAddChange}
required
/>
</div>
</>
)}
<Button type="submit" variant="primary" className="mt">
Opslaan
</Button>
</form>
</Card>
<Card title="Kinderauto bewerken">
<form onSubmit={handleEditSubmit} className="mb-lg">
<div className="form-row">
<label htmlFor="editSelect">Selecteer:</label>
<select
id="editSelect"
value={editName}
onChange={(e) => setEditName(e.target.value)}
>
{options.map((opt) => (
<option key={opt.name} value={opt.name}>
{opt.name}
</option>
))}
</select>
</div>
<div className="form-row">
<label htmlFor="editName">Naam:</label>
<input
id="editName"
name="name"
value={editForm.name}
onChange={handleEditChange}
required
/>
</div>
<div className="form-row">
<label htmlFor="editWidth">Breedte (cm):</label>
<input
id="editWidth"
type="number"
step="0.1"
min="0"
name="width"
value={editForm.width}
onChange={handleEditChange}
required
/>
</div>
<div className="form-row">
<label htmlFor="editHeight">Hoogte (cm):</label>
<input
id="editHeight"
type="number"
step="0.1"
min="0"
name="height"
value={editForm.height}
onChange={handleEditChange}
required
/>
</div>
<div className="form-row checkbox">
<label>
<input
type="checkbox"
name="hasRear"
checked={editForm.hasRear}
onChange={handleEditChange}
/>
Achter heeft andere maat
</label>
</div>
{editForm.hasRear && (
<>
<div className="form-row">
<label htmlFor="editRearWidth">Breedte achter (cm):</label>
<input
id="editRearWidth"
type="number"
step="0.1"
min="0"
name="rearWidth"
value={editForm.rearWidth}
onChange={handleEditChange}
required
/>
</div>
<div className="form-row">
<label htmlFor="editRearHeight">Hoogte achter (cm):</label>
<input
id="editRearHeight"
type="number"
step="0.1"
min="0"
name="rearHeight"
value={editForm.rearHeight}
onChange={handleEditChange}
required
/>
</div>
</>
)}
<Button type="submit" variant="primary" className="mt">
Bijwerken
</Button>
</form>
</Card>
<Card title="Kinderauto verwijderen">
<form onSubmit={handleDeleteSubmit} className="mb-lg">
<div className="form-row">
<label htmlFor="deleteSelect">Selecteer:</label>
<select
id="deleteSelect"
value={deleteName}
onChange={(e) => setDeleteName(e.target.value)}
>
{options.map((opt) => (
<option key={opt.name} value={opt.name}>
{opt.name}
</option>
))}
</select>
</div>
<Button type="submit" variant="ghost" className="mt">
Verwijder
</Button>
</form>
</Card>
</aside>
</div>
)}
{toast && <div className="toast">Opgeslagen</div>}
</PageTransition>
);
}