Files
GSB-Construction/Modules/ProjectManagement/resources/js/Pages/Projects/Team.tsx

133 lines
7.3 KiB
TypeScript

import ProjectLayout from '../../Layouts/ProjectLayout';
import { Card, CardContent, CardHeader, CardTitle } from '@/Components/ui/card';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/Components/ui/table';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/Components/ui/dialog';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/Components/ui/select';
import { Form, FormField } from '@/Components/ui/form';
import { Button } from '@/Components/ui/button';
import { Badge } from '@/Components/ui/badge';
import { UserPlus, UserMinus } from 'lucide-react';
import { useForm, router } from '@inertiajs/react';
import { FormEvent, useMemo, useState } from 'react';
import { PageProps } from '@/types';
interface PersonnelItem {
id: number; ulid: string; name: string; email: string;
pivot?: { role: string };
}
interface ProjectData {
id: number; ulid: string; name: string; code: string; status: string;
personnel: PersonnelItem[];
}
interface Props extends PageProps {
project: ProjectData;
employees: { id: number; ulid: string; name: string }[];
}
const statusLabel = (s: string) => s.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
const roleItems = [{ value: 'pm', label: 'Project Manager' }, { value: 'engineer', label: 'Engineer' }, { value: 'laborer', label: 'Laborer' }, { value: 'member', label: 'Member' }];
export default function Team({ project, employees }: Props) {
const [addMemberOpen, setAddMemberOpen] = useState(false);
const memberForm = useForm({ user_id: '', role: 'member' });
const employeeSelectItems = useMemo(() => employees.map(e => ({ value: e.ulid, label: e.name })), [employees]);
const handleAddMember = (e: FormEvent) => {
e.preventDefault();
memberForm.post(route('projects.personnel.add', project.ulid), {
onSuccess: () => { memberForm.reset(); setAddMemberOpen(false); },
});
};
const handleRemoveMember = (userUlid: string) => {
if (confirm('Remove this team member?')) {
router.delete(route('projects.personnel.remove', [project.ulid, userUlid]));
}
};
return (
<ProjectLayout project={project} currentTab="team">
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<CardTitle>Team Members</CardTitle>
<Dialog open={addMemberOpen} onOpenChange={setAddMemberOpen}>
<DialogTrigger>
<Button size="sm">
<UserPlus className="mr-2 h-4 w-4" /> Add Member
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader><DialogTitle>Add Team Member</DialogTitle></DialogHeader>
<Form onSubmit={handleAddMember}>
<FormField label="Employee" required>
<Select value={memberForm.data.user_id} onValueChange={(v) => { if (v) memberForm.setData('user_id', v); }} items={employeeSelectItems}>
<SelectTrigger><SelectValue placeholder="Select employee" /></SelectTrigger>
<SelectContent>
{employees.map((e) => (
<SelectItem key={e.id} value={e.ulid}>{e.name}</SelectItem>
))}
</SelectContent>
</Select>
</FormField>
<FormField label="Role">
<Select value={memberForm.data.role} onValueChange={(v) => { if (v) memberForm.setData('role', v); }} items={roleItems}>
<SelectTrigger><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="pm">Project Manager</SelectItem>
<SelectItem value="engineer">Engineer</SelectItem>
<SelectItem value="laborer">Laborer</SelectItem>
<SelectItem value="member">Member</SelectItem>
</SelectContent>
</Select>
</FormField>
<div className="flex justify-end">
<Button type="submit" disabled={memberForm.processing}><UserPlus className="mr-2 h-4 w-4" /> Add</Button>
</div>
</Form>
</DialogContent>
</Dialog>
</div>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>Email</TableHead>
<TableHead>Role</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{project.personnel.length === 0 ? (
<TableRow><TableCell colSpan={4} className="text-center text-gray-500 py-8">No team members assigned.</TableCell></TableRow>
) : (
project.personnel.map((member) => (
<TableRow key={member.id}>
<TableCell className="font-medium">{member.name}</TableCell>
<TableCell className="text-gray-500">{member.email}</TableCell>
<TableCell>
<Badge variant={member.pivot?.role === 'pm' ? 'default' : 'outline'}>
{member.pivot?.role === 'pm' ? 'Project Manager' : statusLabel(member.pivot?.role || 'member')}
</Badge>
</TableCell>
<TableCell className="text-right">
<Button variant="ghost" size="icon-sm" title="Remove" onClick={() => handleRemoveMember(member.ulid)}>
<UserMinus className="h-4 w-4 text-red-500" />
</Button>
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
</CardContent>
</Card>
</ProjectLayout>
);
}