248 lines
12 KiB
JavaScript

import React, { useState } from 'react';
import { Head, router } from '@inertiajs/react';
import AdminLayout from '@/Layouts/AdminLayout';
import {
Plus,
Search,
GripVertical,
Edit2,
Trash2,
Info,
Wrench
} from 'lucide-react';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogFooter,
} from "@/components/ui/dialog";
export default function WorkerSkills({ skills }) {
const [searchTerm, setSearchTerm] = useState('');
const [isDialogOpen, setIsDialogOpen] = useState(false);
const [editingSkill, setEditingSkill] = useState(null);
const [skillName, setSkillName] = useState('');
const [error, setError] = useState('');
const handleOpenAdd = () => {
setEditingSkill(null);
setSkillName('');
setError('');
setIsDialogOpen(true);
};
const handleOpenEdit = (skill) => {
setEditingSkill(skill);
setSkillName(skill.name);
setError('');
setIsDialogOpen(true);
};
const handleSubmit = (e) => {
e.preventDefault();
if (!skillName.trim()) {
setError('Skill name is required.');
return;
}
if (editingSkill) {
router.post(`/admin/master-data/skills/${editingSkill.id}/update`, {
name: skillName.trim()
}, {
onSuccess: () => {
setIsDialogOpen(false);
},
onError: (err) => {
setError(err.name || 'Failed to update skill.');
}
});
} else {
router.post('/admin/master-data/skills', {
name: skillName.trim()
}, {
onSuccess: () => {
setIsDialogOpen(false);
},
onError: (err) => {
setError(err.name || 'Failed to add skill.');
}
});
}
};
const handleDelete = (id) => {
if (confirm('Are you sure you want to delete this skill? It will be removed from all associated worker profiles.')) {
router.delete(`/admin/master-data/skills/${id}`);
}
};
const filteredSkills = skills.filter(skill =>
skill.name.toLowerCase().includes(searchTerm.toLowerCase())
);
return (
<AdminLayout title="Worker Skills">
<Head title="Master Data - Skills" />
<div className="max-w-4xl mx-auto space-y-6">
{/* Header Actions */}
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<div className="relative flex-1 max-w-sm">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400" />
<input
type="text"
placeholder="Find skill..."
value={searchTerm}
onChange={e => setSearchTerm(e.target.value)}
className="w-full pl-10 pr-4 py-2.5 bg-white border border-slate-200 rounded-xl text-sm font-medium focus:ring-4 focus:ring-teal-500/10 outline-none transition-all"
/>
</div>
<button
onClick={handleOpenAdd}
className="bg-[#0F6E56] text-white px-6 py-2.5 rounded-xl text-sm font-bold flex items-center justify-center space-x-2 hover:bg-[#085041] transition-all shadow-lg shadow-teal-500/10 cursor-pointer"
>
<Plus className="w-4 h-4" />
<span>Add Skill</span>
</button>
</div>
{/* Skills Card */}
<div className="bg-white rounded-[24px] border border-slate-200 shadow-sm overflow-hidden">
<div className="p-6 border-b border-slate-50 bg-slate-50/30">
<div className="flex items-center space-x-3">
<div className="p-2 bg-teal-50 rounded-lg">
<Wrench className="w-5 h-5 text-[#0F6E56]" />
</div>
<div>
<h2 className="text-sm font-black text-slate-900 uppercase tracking-tight">Worker Skills</h2>
<p className="text-[10px] text-slate-400 font-bold uppercase tracking-widest mt-0.5">Manage skills workers can choose for their profiles</p>
</div>
</div>
</div>
<Table>
<TableHeader>
<TableRow className="bg-white hover:bg-white">
<TableHead className="w-12"></TableHead>
<TableHead className="font-bold text-slate-400 text-[10px] uppercase tracking-widest h-12">Skill Name</TableHead>
<TableHead className="font-bold text-slate-400 text-[10px] uppercase tracking-widest h-12">Worker Count</TableHead>
<TableHead className="font-bold text-slate-400 text-[10px] uppercase tracking-widest h-12">Status</TableHead>
<TableHead className="font-bold text-slate-400 text-[10px] uppercase tracking-widest h-12 text-right pr-6">Action</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{filteredSkills.length > 0 ? (
filteredSkills.map((skill) => (
<TableRow key={skill.id} className="hover:bg-slate-50/50 transition-colors group">
<TableCell className="pl-4">
<GripVertical className="w-4 h-4 text-slate-300 cursor-grab active:cursor-grabbing" />
</TableCell>
<TableCell className="py-4 font-bold text-slate-900 text-sm capitalize">
{skill.name}
</TableCell>
<TableCell>
<div className="flex items-center space-x-2">
<span className="text-sm font-black text-slate-600">{skill.worker_count}</span>
<span className="text-[10px] font-bold text-slate-400 uppercase">Workers</span>
</div>
</TableCell>
<TableCell>
<span className="inline-flex items-center px-2 py-0.5 bg-emerald-50 text-emerald-600 rounded text-[10px] font-black uppercase tracking-tight">
{skill.status}
</span>
</TableCell>
<TableCell className="text-right pr-6">
<div className="flex items-center justify-end space-x-1 opacity-0 group-hover:opacity-100 transition-opacity">
<button
onClick={() => handleOpenEdit(skill)}
className="p-2 text-slate-400 hover:text-[#0F6E56] hover:bg-teal-50 rounded-lg transition-all cursor-pointer"
>
<Edit2 className="w-4 h-4" />
</button>
<button
onClick={() => handleDelete(skill.id)}
className="p-2 text-slate-400 hover:text-rose-500 hover:bg-rose-50 rounded-lg transition-all cursor-pointer"
>
<Trash2 className="w-4 h-4" />
</button>
</div>
</TableCell>
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={5} className="py-8 text-center text-slate-400 font-bold text-xs">
No skills match your search.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
<div className="p-6 bg-slate-50/50 border-t border-slate-100">
<div className="flex items-start space-x-3 text-slate-400">
<Info className="w-4 h-4 shrink-0 mt-0.5" />
<p className="text-[10px] font-bold uppercase tracking-wide leading-relaxed">
Tip: These skills are populated in the mobile application configuration so workers can select their specific expertise when setting up or updating their profiles.
</p>
</div>
</div>
</div>
</div>
{/* Skill Modal */}
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
<DialogContent className="sm:max-w-[400px] rounded-[24px] bg-white">
<DialogHeader>
<DialogTitle className="text-xl font-black text-slate-900 tracking-tight">
{editingSkill ? 'Edit Skill' : 'New Skill'}
</DialogTitle>
</DialogHeader>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="py-4 space-y-4">
<div className="space-y-2">
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest ml-1">Skill Name</label>
<input
className="w-full px-4 py-3 bg-slate-50 border border-slate-100 rounded-xl text-sm font-bold focus:ring-4 focus:ring-teal-500/10 outline-none"
placeholder="e.g. cooking"
value={skillName}
onChange={e => setSkillName(e.target.value)}
/>
{error && (
<p className="text-xs text-rose-600 font-bold ml-1">{error}</p>
)}
</div>
</div>
<DialogFooter className="sm:justify-end gap-3 pt-4 border-t border-slate-100">
<button
type="button"
onClick={() => setIsDialogOpen(false)}
className="px-6 py-2.5 rounded-xl text-sm font-bold text-slate-400 uppercase tracking-widest hover:bg-slate-50 transition-colors"
>
Cancel
</button>
<button
type="submit"
className="bg-[#0F6E56] text-white px-8 py-2.5 rounded-xl text-sm font-bold uppercase tracking-widest shadow-lg shadow-teal-500/20 active:scale-95 transition-all cursor-pointer"
>
{editingSkill ? 'Update' : 'Add Skill'}
</button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
</AdminLayout>
);
}