Personel Sayfası ve Uygulama renk değişiklikleri

This commit is contained in:
2026-03-18 00:08:39 +03:00
parent eb7dee7705
commit b354412cb8
19 changed files with 836 additions and 349 deletions
+211
View File
@@ -0,0 +1,211 @@
'use client';
import { useState } from 'react';
import {
UsersIcon,
TrashIcon,
PencilSquareIcon,
PlusIcon,
MagnifyingGlassIcon
} from '@heroicons/react/24/outline';
import { deleteEmployee } from '@/app/employees/actions';
import EmployeeModal from '@/components/employees/EmployeeModal';
interface EmployeeTableProps {
initialEmployees: any[];
companies: any[];
roles: any[];
}
export default function EmployeeTable({ initialEmployees, companies, roles }: EmployeeTableProps) {
const [employees, setEmployees] = useState(initialEmployees);
const [searchTerm, setSearchTerm] = useState('');
const [statusFilter, setStatusFilter] = useState('all');
const [isModalOpen, setIsModalOpen] = useState(false);
const [editingEmployee, setEditingEmployee] = useState<any>(null);
const filteredEmployees = employees.filter(emp => {
const userData = Array.isArray(emp.users) ? emp.users[0] : emp.users;
const fullName = `${userData?.first_name || ''} ${userData?.last_name || ''}`.toLowerCase();
const email = (userData?.email || '').toLowerCase();
const matchesSearch = fullName.includes(searchTerm.toLowerCase()) || email.includes(searchTerm.toLowerCase());
const matchesStatus = statusFilter === 'all' || emp.status === statusFilter;
return matchesSearch && matchesStatus;
});
const handleEdit = (emp: any) => {
setEditingEmployee(emp);
setIsModalOpen(true);
};
const handleAdd = () => {
setEditingEmployee(null);
setIsModalOpen(true);
};
const handleDelete = async (id: string) => {
if (confirm('Bu personeli silmek istediğinize emin misiniz?')) {
const result = await deleteEmployee(id);
if (result.error) {
alert(result.error);
}
}
};
return (
<div className="space-y-8">
{/* Header with Add Button */}
<div className="sm:flex sm:items-center justify-between gap-4">
<div className="sm:flex-auto">
<div className="flex items-center gap-4">
<div className="w-1.5 h-10 bg-indigo-500 rounded-full" />
<div>
<h1 className="text-3xl font-bold text-slate-900 dark:text-white tracking-tight">Personeller</h1>
<p className="text-sm font-medium text-slate-500 dark:text-slate-400 mt-1">
Şirketinizdeki tüm personel kayıtlarını yönetin
</p>
</div>
</div>
</div>
<button
onClick={handleAdd}
className="group flex items-center gap-2 bg-[#173363] hover:bg-[#CE0515] text-white px-8 py-3.5 rounded-full font-black shadow-lg shadow-blue-900/20 transition-all duration-500 active:scale-95"
>
<PlusIcon className="w-5 h-5 transition-transform group-hover:rotate-90" />
YENİ PERSONEL
</button>
</div>
{/* Filter & Search Bar */}
<div className="flex flex-col md:flex-row gap-4 items-center justify-between bg-white p-6 rounded-[2rem] border border-slate-100 shadow-sm">
<div className="relative w-full md:w-96 group">
<MagnifyingGlassIcon className="absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-slate-400 group-focus-within:text-[#CE0515] transition-colors" />
<input
type="text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="İsim veya e-posta ile ara..."
className="w-full pl-12 pr-4 py-3 bg-slate-50 border-none rounded-2xl text-sm font-medium focus:ring-2 focus:ring-[#CE0515] transition-all outline-none placeholder:text-slate-400"
/>
</div>
<div className="flex gap-2 w-full md:w-auto p-1.5 bg-slate-50 rounded-full border border-slate-100">
<button
onClick={() => setStatusFilter('all')}
className={`flex-1 md:flex-none px-8 py-2.5 rounded-full text-xs font-black uppercase tracking-wider transition-all duration-500 ${statusFilter === 'all' ? 'bg-[#173363] text-white shadow-md' : 'text-slate-400 hover:text-slate-600'}`}
>
Tümü
</button>
<button
onClick={() => setStatusFilter('active')}
className={`flex-1 md:flex-none px-8 py-2.5 rounded-full text-xs font-black uppercase tracking-wider transition-all duration-500 ${statusFilter === 'active' ? 'bg-emerald-600 text-white shadow-md' : 'text-slate-400 hover:text-slate-600'}`}
>
Aktif
</button>
<button
onClick={() => setStatusFilter('inactive')}
className={`flex-1 md:flex-none px-8 py-2.5 rounded-full text-xs font-black uppercase tracking-wider transition-all duration-500 ${statusFilter === 'inactive' ? 'bg-[#CE0515] text-white shadow-md' : 'text-slate-400 hover:text-slate-600'}`}
>
Pasif
</button>
</div>
</div>
{/* Table Section */}
<div className="bg-white shadow-sm border border-slate-100 rounded-[2.5rem] overflow-hidden">
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-slate-50">
<thead className="bg-slate-50/50">
<tr>
<th className="py-6 pl-8 pr-3 text-left text-[10px] font-black uppercase tracking-[0.2em] text-slate-400">Personel</th>
<th className="px-3 py-6 text-left text-[10px] font-black uppercase tracking-[0.2em] text-slate-400">Şirket / Rol</th>
<th className="px-3 py-6 text-left text-[10px] font-black uppercase tracking-[0.2em] text-slate-400">Departman / Ünvan</th>
<th className="px-3 py-6 text-left text-[10px] font-black uppercase tracking-[0.2em] text-slate-400">Durum</th>
<th className="relative py-6 pl-3 pr-8 text-right">
<span className="sr-only">İşlemler</span>
</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-50">
{filteredEmployees.map((emp) => {
const userData = Array.isArray(emp.users) ? emp.users[0] : emp.users;
const companyData = Array.isArray(emp.companies) ? emp.companies[0] : emp.companies;
const roleData = Array.isArray(emp.roles) ? emp.roles[0] : emp.roles;
return (
<tr key={emp.id} className="group hover:bg-slate-50/50 transition-all duration-500">
<td className="whitespace-nowrap py-6 pl-8 pr-3">
<div className="flex items-center gap-4">
<div className="w-12 h-12 rounded-2xl bg-[#173363] flex items-center justify-center text-white font-black text-sm shadow-lg shadow-blue-900/10 group-hover:bg-[#CE0515] transition-colors duration-500">
{userData?.first_name?.[0]}{userData?.last_name?.[0]}
</div>
<div>
<p className="text-sm font-black text-[#173363] group-hover:text-black transition-colors">
{userData?.first_name} {userData?.last_name}
</p>
<p className="text-xs text-slate-400 font-bold">{userData?.email}</p>
</div>
</div>
</td>
<td className="whitespace-nowrap px-3 py-6">
<p className="text-sm font-black text-[#173363]">{companyData?.name}</p>
<p className="text-[10px] text-[#CE0515] font-black uppercase tracking-widest mt-1">{roleData?.description}</p>
</td>
<td className="whitespace-nowrap px-3 py-6">
<p className="text-sm font-black text-slate-700">{emp.department || '-'}</p>
<p className="text-xs text-slate-400 font-bold mt-1">{emp.title || '-'}</p>
</td>
<td className="whitespace-nowrap px-3 py-6">
<span className={`inline-flex items-center gap-2 px-4 py-1.5 rounded-full text-[10px] font-black uppercase tracking-[0.15em] border ${
emp.status === 'active'
? 'bg-emerald-50 text-emerald-700 border-emerald-100 shadow-sm shadow-emerald-100'
: 'bg-slate-50 text-slate-500 border-slate-100'
}`}>
<span className={`w-2 h-2 rounded-full animate-pulse ${emp.status === 'active' ? 'bg-emerald-500' : 'bg-slate-400'}`} />
{emp.status === 'active' ? 'Aktif' : 'Pasif'}
</span>
</td>
<td className="whitespace-nowrap py-6 pl-3 pr-8 text-right">
<div className="flex items-center justify-end gap-3 translate-x-4 opacity-0 group-hover:translate-x-0 group-hover:opacity-100 transition-all duration-500">
<button
onClick={() => handleEdit(emp)}
className="p-3 rounded-2xl text-slate-400 hover:text-white hover:bg-[#173363] hover:shadow-lg hover:shadow-blue-900/20 transition-all duration-300"
title="Düzenle"
>
<PencilSquareIcon className="w-5 h-5" />
</button>
<button
onClick={() => handleDelete(emp.id)}
className="p-3 rounded-2xl text-slate-400 hover:text-white hover:bg-[#CE0515] hover:shadow-lg hover:shadow-red-900/20 transition-all duration-300"
title="Sil"
>
<TrashIcon className="w-5 h-5" />
</button>
</div>
</td>
</tr>
);
})}
{filteredEmployees.length === 0 && (
<tr>
<td colSpan={5} className="py-20 text-center">
<UsersIcon className="mx-auto h-16 w-16 text-slate-200 dark:text-zinc-800 mb-4" />
<p className="text-slate-500 dark:text-slate-400 font-bold">Herhangi bir personel kaydı bulunamadı.</p>
</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
{/* Modal */}
<EmployeeModal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
companies={companies}
roles={roles}
editingEmployee={editingEmployee}
/>
</div>
);
}