Добавлены модели и функциональность для управления сотрудниками, включая создание, обновление и удаление сотрудников через GraphQL. Обновлены компоненты для отображения списка сотрудников и их расписания, улучшен интерфейс взаимодействия с пользователем. Реализованы функции экспорта отчетов в CSV и TXT форматах, добавлены новые интерфейсы и типы данных для сотрудников.

This commit is contained in:
Bivekich
2025-07-17 23:55:11 +03:00
parent 3e2a03da8c
commit d361364716
13 changed files with 3444 additions and 428 deletions

View File

@ -6,6 +6,7 @@ import { Button } from '@/components/ui/button'
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar'
import { Badge } from '@/components/ui/badge'
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from '@/components/ui/alert-dialog'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
@ -18,7 +19,9 @@ import {
User,
Briefcase,
Save,
X
X,
Trash2,
UserX
} from 'lucide-react'
// Интерфейс сотрудника
@ -27,7 +30,7 @@ interface Employee {
firstName: string
lastName: string
position: string
department: string
department?: string
phone: string
email: string
avatar?: string
@ -95,10 +98,12 @@ const mockEmployees: Employee[] = [
interface EmployeesListProps {
searchQuery: string
employees: Employee[]
onEditEmployee: (employee: Employee) => void
onDeleteEmployee: (employeeId: string) => void
}
export function EmployeesList({ searchQuery }: EmployeesListProps) {
const [employees, setEmployees] = useState<Employee[]>(mockEmployees)
export function EmployeesList({ searchQuery, employees, onEditEmployee, onDeleteEmployee }: EmployeesListProps) {
const [selectedEmployee, setSelectedEmployee] = useState<Employee | null>(null)
const [isEditModalOpen, setIsEditModalOpen] = useState(false)
@ -106,7 +111,7 @@ export function EmployeesList({ searchQuery }: EmployeesListProps) {
const filteredEmployees = employees.filter(employee =>
`${employee.firstName} ${employee.lastName}`.toLowerCase().includes(searchQuery.toLowerCase()) ||
employee.position.toLowerCase().includes(searchQuery.toLowerCase()) ||
employee.department.toLowerCase().includes(searchQuery.toLowerCase())
(employee.department && employee.department.toLowerCase().includes(searchQuery.toLowerCase()))
)
const getStatusBadge = (status: Employee['status']) => {
@ -133,18 +138,11 @@ export function EmployeesList({ searchQuery }: EmployeesListProps) {
}
const handleEditEmployee = (employee: Employee) => {
setSelectedEmployee(employee)
setIsEditModalOpen(true)
onEditEmployee(employee)
}
const handleSaveEmployee = () => {
if (selectedEmployee) {
setEmployees(prev =>
prev.map(emp => emp.id === selectedEmployee.id ? selectedEmployee : emp)
)
setIsEditModalOpen(false)
setSelectedEmployee(null)
}
const handleDeleteEmployee = (employee: Employee) => {
onDeleteEmployee(employee.id)
}
return (
@ -214,14 +212,48 @@ export function EmployeesList({ searchQuery }: EmployeesListProps) {
<h3 className="text-white font-semibold text-lg truncate">
{employee.firstName} {employee.lastName}
</h3>
<Button
size="sm"
variant="ghost"
className="text-white/60 hover:text-white hover:bg-white/10 h-8 w-8 p-0"
onClick={() => handleEditEmployee(employee)}
>
<Edit className="h-4 w-4" />
</Button>
<div className="flex gap-1">
<Button
size="sm"
variant="ghost"
className="text-white/60 hover:text-white hover:bg-white/10 h-8 w-8 p-0"
onClick={() => handleEditEmployee(employee)}
>
<Edit className="h-4 w-4" />
</Button>
<AlertDialog>
<AlertDialogTrigger asChild>
<Button
size="sm"
variant="ghost"
className="text-red-400/60 hover:text-red-300 hover:bg-red-500/10 h-8 w-8 p-0"
>
<UserX className="h-4 w-4" />
</Button>
</AlertDialogTrigger>
<AlertDialogContent className="glass-card border-white/10">
<AlertDialogHeader>
<AlertDialogTitle className="text-white">Уволить сотрудника?</AlertDialogTitle>
<AlertDialogDescription className="text-white/70">
Вы уверены, что хотите уволить {employee.firstName} {employee.lastName}?
Это действие изменит статус сотрудника на &quot;Неактивен&quot; и удалит его из активного списка.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel className="glass-secondary text-white hover:text-white">
Отмена
</AlertDialogCancel>
<AlertDialogAction
onClick={() => handleDeleteEmployee(employee)}
className="bg-red-600 hover:bg-red-700 text-white"
>
Уволить
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
</div>
<p className="text-purple-300 font-medium mb-1">{employee.position}</p>
@ -389,11 +421,11 @@ export function EmployeesList({ searchQuery }: EmployeesListProps) {
<div className="flex gap-2 pt-4">
<Button
onClick={handleSaveEmployee}
onClick={() => setIsEditModalOpen(false)}
className="glass-button flex-1"
>
<Save className="h-4 w-4 mr-2" />
Сохранить
Закрыть
</Button>
<Button
variant="outline"