Commit of Current Status
This commit is contained in:
182
components/GroupManager.tsx
Normal file
182
components/GroupManager.tsx
Normal file
@@ -0,0 +1,182 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { VehicleGroup, Vehicle, createGroup, updateGroup, assignVehicleToGroup } from '@/lib/api';
|
||||
import { Users, Plus, Edit2, X, Check } from 'lucide-react';
|
||||
|
||||
interface GroupManagerProps {
|
||||
groups: VehicleGroup[];
|
||||
vehicles: Vehicle[];
|
||||
onGroupChanged: () => void;
|
||||
}
|
||||
|
||||
export function GroupManager({ groups, vehicles, onGroupChanged }: GroupManagerProps) {
|
||||
const [isCreating, setIsCreating] = useState(false);
|
||||
const [editingId, setEditingId] = useState<number | null>(null);
|
||||
|
||||
const [name, setName] = useState('');
|
||||
const [description, setDescription] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
// Vehicle Assignment State
|
||||
const [assignVin, setAssignVin] = useState('');
|
||||
|
||||
const startCreate = () => {
|
||||
setIsCreating(true);
|
||||
setEditingId(null);
|
||||
setName('');
|
||||
setDescription('');
|
||||
setAssignVin('');
|
||||
};
|
||||
|
||||
const startEdit = (group: VehicleGroup) => {
|
||||
setIsCreating(false);
|
||||
setEditingId(group.id);
|
||||
setName(group.name);
|
||||
setDescription(group.description || '');
|
||||
setAssignVin('');
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
setIsCreating(false);
|
||||
setEditingId(null);
|
||||
setName('');
|
||||
setDescription('');
|
||||
setAssignVin('');
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!name) return;
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
if (isCreating) {
|
||||
await createGroup(name, description);
|
||||
} else if (editingId) {
|
||||
await updateGroup(editingId, name, description);
|
||||
}
|
||||
onGroupChanged();
|
||||
cancel();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
alert('Operation failed');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleAssignVehicle = async () => {
|
||||
if (!editingId || !assignVin) return;
|
||||
setLoading(true);
|
||||
try {
|
||||
await assignVehicleToGroup(editingId, assignVin);
|
||||
onGroupChanged();
|
||||
alert('Vehicle assigned');
|
||||
setAssignVin('');
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
alert('Failed to assign vehicle');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-zinc-900/50 backdrop-blur-md border border-zinc-800 rounded-xl p-6 shadow-xl">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h2 className="text-xl font-semibold text-white flex items-center gap-2">
|
||||
<Users className="w-5 h-5 text-orange-400" />
|
||||
Vehicle Groups
|
||||
</h2>
|
||||
<button
|
||||
onClick={startCreate}
|
||||
className="text-xs bg-zinc-800 hover:bg-zinc-700 text-white px-3 py-1.5 rounded-lg transition-colors flex items-center gap-1"
|
||||
>
|
||||
<Plus className="w-3 h-3" /> New Group
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{(isCreating || editingId) && (
|
||||
<div className="mb-4 bg-zinc-950/50 p-4 rounded-lg border border-zinc-800/50">
|
||||
<form onSubmit={handleSubmit} className="space-y-3 mb-4">
|
||||
<input
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={e => setName(e.target.value)}
|
||||
placeholder="Group Name"
|
||||
className="w-full bg-zinc-900 border border-zinc-700 rounded-lg px-3 py-2 text-white text-sm outline-none focus:ring-1 focus:ring-orange-500"
|
||||
required
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
value={description}
|
||||
onChange={e => setDescription(e.target.value)}
|
||||
placeholder="Description (optional)"
|
||||
className="w-full bg-zinc-900 border border-zinc-700 rounded-lg px-3 py-2 text-white text-sm outline-none focus:ring-1 focus:ring-orange-500"
|
||||
/>
|
||||
<div className="flex items-center gap-2 pt-1">
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="flex-1 bg-orange-500 hover:bg-orange-600 text-white text-xs font-medium py-1.5 rounded transition-colors flex items-center justify-center gap-1"
|
||||
>
|
||||
<Check className="w-3 h-3" /> Save
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={cancel}
|
||||
className="flex-1 bg-zinc-800 hover:bg-zinc-700 text-zinc-300 text-xs font-medium py-1.5 rounded transition-colors flex items-center justify-center gap-1"
|
||||
>
|
||||
<X className="w-3 h-3" /> Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{editingId && (
|
||||
<div className="border-t border-zinc-800 pt-3">
|
||||
<h4 className="text-xs font-medium text-zinc-400 mb-2">Assign Vehicle</h4>
|
||||
<div className="flex gap-2">
|
||||
<select
|
||||
value={assignVin}
|
||||
onChange={e => setAssignVin(e.target.value)}
|
||||
className="flex-1 bg-zinc-900 border border-zinc-700 rounded px-2 py-1 text-xs text-white outline-none"
|
||||
>
|
||||
<option value="">Select...</option>
|
||||
{vehicles.map(v => (
|
||||
<option key={v.vin} value={v.vin}>{v.vin} {v.groupId === editingId ? '(Already in group)' : ''}</option>
|
||||
))}
|
||||
</select>
|
||||
<button
|
||||
onClick={handleAssignVehicle}
|
||||
disabled={loading || !assignVin}
|
||||
className="bg-zinc-800 hover:bg-zinc-700 text-white px-2 py-1 rounded text-xs"
|
||||
>
|
||||
<Plus className="w-3 h-3" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-2 max-h-[300px] overflow-y-auto pr-1">
|
||||
{groups.map(group => (
|
||||
<div key={group.id} className="group flex items-center justify-between p-3 bg-zinc-900/30 border border-zinc-800/30 rounded-lg hover:border-zinc-700 transition-colors">
|
||||
<div>
|
||||
<h3 className="text-zinc-200 font-medium text-sm">{group.name}</h3>
|
||||
<p className="text-zinc-500 text-xs">{group.vehicles?.length || 0} vehicles</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => startEdit(group)}
|
||||
className="opacity-0 group-hover:opacity-100 p-1.5 text-zinc-500 hover:text-white hover:bg-zinc-800 rounded transition-all"
|
||||
>
|
||||
<Edit2 className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
{groups.length === 0 && <p className="text-center text-zinc-500 text-sm py-4">No groups created.</p>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user