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,152 @@
import { useState, useEffect } from 'react';
import { format } from 'date-fns';
import styles from './ReservationModal.module.css';
export default function ReservationModal({ cars, reservation, onSave, onDelete, onClose }) {
const isEdit = !!reservation?.id;
const [carId, setCarId] = useState('');
const [startDate, setStartDate] = useState('');
const [endDate, setEndDate] = useState('');
const [customerName, setCustomerName] = useState('');
const [notes, setNotes] = useState('');
const [submitting, setSubmitting] = useState(false);
useEffect(() => {
if (reservation) {
setCarId(String(reservation.car_id || (cars[0]?.id ?? '')));
setStartDate(reservation.start_date || format(new Date(), 'yyyy-MM-dd'));
setEndDate(reservation.end_date || format(new Date(), 'yyyy-MM-dd'));
setCustomerName(reservation.customer_name || '');
setNotes(reservation.notes || '');
}
}, [reservation, cars]);
const handleSubmit = async (e) => {
e.preventDefault();
if (!carId || !startDate || !endDate) return;
if (startDate > endDate) {
alert('開始日は終了日以前に設定してください');
return;
}
try {
setSubmitting(true);
await onSave({
car_id: Number(carId),
start_date: startDate,
end_date: endDate,
customer_name: customerName,
notes,
});
} finally {
setSubmitting(false);
}
};
const handleDelete = async () => {
if (!confirm('この予約を削除しますか?')) return;
try {
setSubmitting(true);
await onDelete(reservation.id);
} finally {
setSubmitting(false);
}
};
return (
<div className={styles.overlay} onMouseDown={(e) => e.target === e.currentTarget && onClose()}>
<div className={styles.modal}>
<div className={styles.modalHeader}>
<h2 className={styles.modalTitle}>
{isEdit ? '予約を編集' : '新しい予約を作成'}
</h2>
<button className={styles.closeBtn} onClick={onClose}></button>
</div>
<form className={styles.form} onSubmit={handleSubmit}>
<div className={styles.field}>
<label className={styles.label}>代車 <span className={styles.required}>*</span></label>
<select
className={styles.select}
value={carId}
onChange={(e) => setCarId(e.target.value)}
required
>
<option value="">選択してください</option>
{cars.map((car) => (
<option key={car.id} value={car.id}>{car.name}</option>
))}
</select>
</div>
<div className={styles.fieldRow}>
<div className={styles.field}>
<label className={styles.label}>開始日 <span className={styles.required}>*</span></label>
<input
type="date"
className={styles.input}
value={startDate}
onChange={(e) => setStartDate(e.target.value)}
required
/>
</div>
<div className={styles.field}>
<label className={styles.label}>終了日 <span className={styles.required}>*</span></label>
<input
type="date"
className={styles.input}
value={endDate}
min={startDate}
onChange={(e) => setEndDate(e.target.value)}
required
/>
</div>
</div>
<div className={styles.field}>
<label className={styles.label}>お客様名</label>
<input
type="text"
className={styles.input}
placeholder="例:山田 太郎"
value={customerName}
onChange={(e) => setCustomerName(e.target.value)}
/>
</div>
<div className={styles.field}>
<label className={styles.label}>備考</label>
<textarea
className={styles.textarea}
placeholder="メモを入力..."
value={notes}
onChange={(e) => setNotes(e.target.value)}
rows={3}
/>
</div>
<div className={styles.actions}>
{isEdit && (
<button
type="button"
className={styles.btnDelete}
onClick={handleDelete}
disabled={submitting}
>
削除
</button>
)}
<div className={styles.rightActions}>
<button type="button" className={styles.btnCancel} onClick={onClose} disabled={submitting}>
キャンセル
</button>
<button type="submit" className={styles.btnSave} disabled={submitting}>
{submitting ? '保存中...' : (isEdit ? '更新' : '作成')}
</button>
</div>
</div>
</form>
</div>
</div>
);
}