Implement car reservation schedule management system

Co-authored-by: pdf114514 <57948770+pdf114514@users.noreply.github.com>
Agent-Logs-Url: https://github.com/pdf114514/CarReservation/sessions/1d8c6b05-0e8d-4484-a2d8-8d427dfad9cb
This commit is contained in:
copilot-swe-agent[bot]
2026-03-20 18:03:33 +00:00
parent 3458e4d376
commit 50d3803610
22 changed files with 4500 additions and 0 deletions

View File

@@ -0,0 +1,191 @@
import { useState, useEffect } from 'react';
import { api } from '../api.js';
import styles from './CarManagement.module.css';
export default function CarManagement() {
const [cars, setCars] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [newCarName, setNewCarName] = useState('');
const [newCarDesc, setNewCarDesc] = useState('');
const [editingId, setEditingId] = useState(null);
const [editName, setEditName] = useState('');
const [editDesc, setEditDesc] = useState('');
const [submitting, setSubmitting] = useState(false);
const loadCars = async () => {
try {
setLoading(true);
const data = await api.getCars();
setCars(data);
setError(null);
} catch (e) {
setError(e.message);
} finally {
setLoading(false);
}
};
useEffect(() => {
loadCars();
}, []);
const handleAdd = async (e) => {
e.preventDefault();
if (!newCarName.trim()) return;
try {
setSubmitting(true);
await api.createCar({ name: newCarName.trim(), description: newCarDesc.trim() });
setNewCarName('');
setNewCarDesc('');
await loadCars();
} catch (e) {
setError(e.message);
} finally {
setSubmitting(false);
}
};
const handleDelete = async (id, name) => {
if (!confirm(`${name}」を削除しますか?\n関連する予約もすべて削除されます。`)) return;
try {
await api.deleteCar(id);
await loadCars();
} catch (e) {
setError(e.message);
}
};
const startEdit = (car) => {
setEditingId(car.id);
setEditName(car.name);
setEditDesc(car.description || '');
};
const cancelEdit = () => {
setEditingId(null);
setEditName('');
setEditDesc('');
};
const handleUpdate = async (id) => {
if (!editName.trim()) return;
try {
setSubmitting(true);
await api.updateCar(id, { name: editName.trim(), description: editDesc.trim() });
cancelEdit();
await loadCars();
} catch (e) {
setError(e.message);
} finally {
setSubmitting(false);
}
};
return (
<div className={styles.container}>
<h2 className={styles.heading}>代車管理</h2>
<div className={styles.addCard}>
<h3 className={styles.subHeading}>代車を追加</h3>
<form className={styles.form} onSubmit={handleAdd}>
<div className={styles.formRow}>
<input
type="text"
className={styles.input}
placeholder="車名(例:プリウス A"
value={newCarName}
onChange={(e) => setNewCarName(e.target.value)}
required
/>
<input
type="text"
className={styles.input}
placeholder="備考(任意)"
value={newCarDesc}
onChange={(e) => setNewCarDesc(e.target.value)}
/>
<button type="submit" className={styles.btnPrimary} disabled={submitting || !newCarName.trim()}>
+ 追加
</button>
</div>
</form>
</div>
{loading && <p className={styles.message}>読み込み中...</p>}
{error && <p className={styles.error}>エラー: {error}</p>}
{!loading && !error && (
<div className={styles.tableWrapper}>
<table className={styles.table}>
<thead>
<tr>
<th>ID</th>
<th>車名</th>
<th>備考</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{cars.length === 0 && (
<tr>
<td colSpan={4} className={styles.empty}>代車がありません</td>
</tr>
)}
{cars.map((car) => (
<tr key={car.id}>
<td className={styles.idCell}>{car.id}</td>
{editingId === car.id ? (
<>
<td>
<input
type="text"
className={styles.input}
value={editName}
onChange={(e) => setEditName(e.target.value)}
/>
</td>
<td>
<input
type="text"
className={styles.input}
value={editDesc}
onChange={(e) => setEditDesc(e.target.value)}
/>
</td>
<td className={styles.actions}>
<button
className={styles.btnSave}
onClick={() => handleUpdate(car.id)}
disabled={submitting}
>
保存
</button>
<button className={styles.btnCancel} onClick={cancelEdit}>
キャンセル
</button>
</td>
</>
) : (
<>
<td>{car.name}</td>
<td className={styles.descCell}>{car.description || '-'}</td>
<td className={styles.actions}>
<button className={styles.btnEdit} onClick={() => startEdit(car)}>
編集
</button>
<button className={styles.btnDelete} onClick={() => handleDelete(car.id, car.name)}>
削除
</button>
</td>
</>
)}
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
);
}