Files
vip-coordinator/frontend/src/components/DriverForm.tsx
kyle f2b3f34a72 refactor: code efficiency improvements (Issues #9-13, #15, #17-20)
Backend:
- Extract shared hard-delete authorization utility (#9)
- Extract Prisma include constants per entity (#11)
- Fix N+1 query pattern in events findAll (#12)
- Extract shared date utility functions (#13)
- Move vehicle utilization filtering to DB query (#15)
- Add ParseBooleanPipe for query params
- Add CurrentDriver decorator + ResolveDriverInterceptor (#20)

Frontend:
- Extract shared form utilities (toDatetimeLocal) and enum labels (#17)
- Replace browser confirm() with styled ConfirmModal (#18)
- Add centralized query-keys.ts constants (#19)
- Clean up unused imports, add useMemo where needed (#19)
- Standardize filter button styling across list pages

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 16:07:19 +01:00

164 lines
5.3 KiB
TypeScript

import { useState } from 'react';
import { X } from 'lucide-react';
import { DEPARTMENT_LABELS } from '@/lib/enum-labels';
interface DriverFormProps {
driver?: Driver | null;
onSubmit: (data: DriverFormData) => void;
onCancel: () => void;
isSubmitting: boolean;
}
interface Driver {
id: string;
name: string;
phone: string;
department: string | null;
userId: string | null;
}
export interface DriverFormData {
name: string;
phone: string;
department?: string;
userId?: string;
}
export function DriverForm({ driver, onSubmit, onCancel, isSubmitting }: DriverFormProps) {
const [formData, setFormData] = useState<DriverFormData>({
name: driver?.name || '',
phone: driver?.phone || '',
department: driver?.department || 'OFFICE_OF_DEVELOPMENT',
userId: driver?.userId || '',
});
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
// Clean up the data - remove empty strings for optional fields
const cleanedData = {
...formData,
department: formData.department || undefined,
userId: formData.userId || undefined,
};
onSubmit(cleanedData);
};
const handleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>
) => {
const { name, value } = e.target;
setFormData((prev) => ({
...prev,
[name]: value,
}));
};
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50">
<div className="bg-card rounded-lg shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto">
<div className="flex items-center justify-between p-6 border-b border-border">
<h2 className="text-2xl font-bold text-foreground">
{driver ? 'Edit Driver' : 'Add New Driver'}
</h2>
<button
onClick={onCancel}
className="text-muted-foreground hover:text-foreground"
>
<X className="h-6 w-6" />
</button>
</div>
<form onSubmit={handleSubmit} className="p-6 space-y-4">
{/* Name */}
<div>
<label className="block text-sm font-medium text-foreground mb-1">
Full Name *
</label>
<input
type="text"
name="name"
required
value={formData.name}
onChange={handleChange}
className="w-full px-3 py-2 border border-input rounded-md bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary"
/>
</div>
{/* Phone */}
<div>
<label className="block text-sm font-medium text-foreground mb-1">
Phone Number *
</label>
<input
type="tel"
name="phone"
required
value={formData.phone}
onChange={handleChange}
className="w-full px-3 py-2 border border-input rounded-md bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary"
/>
</div>
{/* Department */}
<div>
<label className="block text-sm font-medium text-foreground mb-1">
Department
</label>
<select
name="department"
value={formData.department}
onChange={handleChange}
className="w-full px-3 py-2 border border-input rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary"
>
<option value="">Select Department</option>
{Object.entries(DEPARTMENT_LABELS).map(([value, label]) => (
<option key={value} value={value}>
{label}
</option>
))}
</select>
</div>
{/* User ID (optional, for linking driver to user account) */}
<div>
<label className="block text-sm font-medium text-foreground mb-1">
User Account ID (Optional)
</label>
<input
type="text"
name="userId"
value={formData.userId}
onChange={handleChange}
placeholder="Leave blank for standalone driver"
className="w-full px-3 py-2 border border-input rounded-md bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary"
/>
<p className="mt-1 text-xs text-muted-foreground">
Link this driver to a user account for login access
</p>
</div>
{/* Actions */}
<div className="flex gap-3 pt-4">
<button
type="submit"
disabled={isSubmitting}
className="flex-1 bg-primary text-white py-2 px-4 rounded-md hover:bg-primary/90 disabled:opacity-50"
>
{isSubmitting ? 'Saving...' : driver ? 'Update Driver' : 'Create Driver'}
</button>
<button
type="button"
onClick={onCancel}
className="flex-1 bg-muted text-foreground py-2 px-4 rounded-md hover:bg-muted/80"
>
Cancel
</button>
</div>
</form>
</div>
</div>
);
}