diff --git a/src/App.tsx b/src/App.tsx
index af141fb..d24875c 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -25,6 +25,7 @@ import { RoomServicePage } from './pages/RoomServicePage'
import { RentalPage } from './pages/RentalPage'
import { RoomCategoriesPage } from './pages/RoomCategoriesPage'
import { DocumentsPage } from './pages/DocumentsPage'
+import { UsersPage } from './pages/UsersPage'
export default function App() {
return (
@@ -59,6 +60,7 @@ export default function App() {
} />
} />
} />
+ } />
{/* Admin routes */}
diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx
index bc2c360..ef3c0b9 100644
--- a/src/components/layout/Sidebar.tsx
+++ b/src/components/layout/Sidebar.tsx
@@ -1,7 +1,7 @@
import { NavLink, useNavigate } from 'react-router-dom'
import {
CalendarDays, BookOpen, BedDouble, Globe, Settings,
- FileText, LayoutDashboard, Building2, Users, X, Hotel, Puzzle, CalendarRange, Map, LayoutGrid,
+ FileText, LayoutDashboard, Building2, Users, X, Hotel, Puzzle, CalendarRange, Map, LayoutGrid, UserCog,
} from 'lucide-react'
import { useAuth } from '../../contexts/AuthContext'
import { useModules } from '../../contexts/ModulesContext'
@@ -156,7 +156,8 @@ export function Sidebar({ open, onClose }: SidebarProps) {
Управление
-
+
+
{isModuleActive('channel-manager') && (
void }) {
+ const { login } = useAuth()
+ const navigate = useNavigate()
+
+ const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [showPass, setShowPass] = useState(false)
- const [loading, setLoading] = useState(false)
- const [error, setError] = useState('')
-
- if (user) {
- if (user.role === 'super_admin') return
- return
- }
+ const [loading, setLoading] = useState(false)
+ const [error, setError] = useState('')
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
@@ -33,14 +41,438 @@ export function LoginPage() {
setLoading(true)
const loggedIn = await login(email, password)
setLoading(false)
- if (!loggedIn) {
- setError('Неверный email или пароль')
- return
- }
+ if (!loggedIn) { setError('Неверный email или пароль'); return }
if (loggedIn.role === 'super_admin') navigate('/admin')
else navigate('/calendar')
}
+ return (
+ <>
+
+ Добро пожаловать
+
+
+ Войдите в аккаунт для доступа к панели управления
+
+
+
+
+ {/* Demo hint */}
+
+
+ Демо-аккаунты (пароль: demo):
+
+
+ {DEMO_EMAILS.map(e => (
+ { setEmail(e); setPassword('demo'); setError('') }}
+ className={cn(
+ 'text-left text-xs px-2 py-1 rounded transition-colors',
+ email === e
+ ? 'text-brand-700 dark:text-brand-300 bg-brand-50 dark:bg-brand-900/30 font-medium'
+ : 'text-slate-500 dark:text-slate-400 hover:text-brand-600 dark:hover:text-brand-400',
+ )}
+ >{e}
+ ))}
+
+
+
+
+ Нет аккаунта?{' '}
+
+ Зарегистрировать отель
+
+
+ >
+ )
+}
+
+// ── Register form ──────────────────────────────────────────────────────────────
+function RegisterForm({ onSwitch }: { onSwitch: () => void }) {
+ const [step, setStep] = useState<1 | 2 | 'done'>(1)
+
+ // Step 1 — Hotel + Legal entity
+ const [hotelName, setHotelName] = useState('')
+ const [legalType, setLegalType] = useState('ooo')
+ const [legalName, setLegalName] = useState('')
+ const [inn, setInn] = useState('')
+ const [kpp, setKpp] = useState('')
+ const [address, setAddress] = useState('')
+
+ // Step 2 — Account
+ const [firstName, setFirstName] = useState('')
+ const [lastName, setLastName] = useState('')
+ const [email, setEmail] = useState('')
+ const [phone, setPhone] = useState('')
+ const [password, setPassword] = useState('')
+ const [confirm, setConfirm] = useState('')
+ const [showPass, setShowPass] = useState(false)
+ const [showConfirm, setShowConfirm] = useState(false)
+ const [loading, setLoading] = useState(false)
+ const [errors, setErrors] = useState>({})
+
+ const isIp = legalType === 'ip'
+
+ const validateStep1 = () => {
+ const e: Record = {}
+ if (!hotelName.trim()) e.hotelName = 'Введите название отеля'
+ if (!legalName.trim()) e.legalName = 'Введите наименование юрлица'
+ if (!inn.trim()) e.inn = 'Введите ИНН'
+ else if (!/^\d{10,12}$/.test(inn.replace(/\s/g, '')))
+ e.inn = 'ИНН — 10 цифр (юрлицо) или 12 (ИП)'
+ if (!isIp && !kpp.trim()) e.kpp = 'Введите КПП'
+ setErrors(e)
+ return Object.keys(e).length === 0
+ }
+
+ const validateStep2 = () => {
+ const e: Record = {}
+ if (!firstName.trim()) e.firstName = 'Введите имя'
+ if (!lastName.trim()) e.lastName = 'Введите фамилию'
+ if (!email.trim()) e.email = 'Введите email'
+ else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) e.email = 'Некорректный email'
+ if (password.length < 8) e.password = 'Минимум 8 символов'
+ if (password !== confirm) e.confirm = 'Пароли не совпадают'
+ setErrors(e)
+ return Object.keys(e).length === 0
+ }
+
+ const nextStep = () => {
+ if (validateStep1()) setStep(2)
+ }
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault()
+ if (!validateStep2()) return
+ setLoading(true)
+ // Simulate API call
+ await new Promise(r => setTimeout(r, 1200))
+ setLoading(false)
+ setStep('done')
+ }
+
+ const Field = ({
+ label, error, children, hint,
+ }: { label: string; error?: string; children: React.ReactNode; hint?: string }) => (
+
+
{label}
+ {children}
+ {hint && !error &&
{hint}
}
+ {error && (
+
+ {error}
+
+ )}
+
+ )
+
+ if (step === 'done') {
+ return (
+
+
+
+
+
+ Заявка отправлена!
+
+
+ Мы получили данные отеля {hotelName} .
+
+
+ На адрес {email} отправлено
+ письмо с подтверждением. После проверки данных менеджер HotelSync активирует аккаунт в течение одного рабочего дня.
+
+
+ Перейти к входу
+
+
+ )
+ }
+
+ return (
+ <>
+
+
+
+ {step === 1 ? 'Регистрация отеля' : 'Данные аккаунта'}
+
+
Шаг {step} из 2
+
+ {/* Step indicator */}
+
+ {[1, 2].map(s => (
+
+ {s < step ? : s}
+
+ ))}
+
+
+
+ {/* ── Step 1: Hotel + Legal entity ── */}
+ {step === 1 && (
+
+
+ setHotelName(e.target.value)}
+ />
+
+
+
+
+
+ {/* Legal type selector */}
+
+
+ Организационно-правовая форма *
+
+
+ {LEGAL_TYPES.map(lt => (
+ setLegalType(lt.id)}
+ className={cn(
+ 'px-3 py-1.5 rounded-lg text-sm font-medium border transition-colors',
+ legalType === lt.id
+ ? 'bg-brand-600 border-brand-600 text-white'
+ : 'bg-white dark:bg-slate-700 border-slate-200 dark:border-slate-600 text-slate-700 dark:text-slate-300 hover:border-brand-400',
+ )}
+ >
+ {lt.label}
+
+ ))}
+
+
+
+
+
+
+
+ Далее
+
+
+
+
+ Уже есть аккаунт?{' '}
+
+ Войти
+
+
+
+ )}
+
+ {/* ── Step 2: Account ── */}
+ {step === 2 && (
+
+ )}
+ >
+ )
+}
+
+// ── Page ───────────────────────────────────────────────────────────────────────
+export function LoginPage() {
+ const { user } = useAuth()
+ const { theme, toggle } = useTheme()
+ const [mode, setMode] = useState<'login' | 'register'>('login')
+
+ if (user) {
+ if (user.role === 'super_admin') return
+ return
+ }
+
return (
{/* Left panel — branding */}
@@ -63,8 +495,8 @@ export function LoginPage() {
{[
- { value: '500+', label: 'Отелей' },
- { value: '1M+', label: 'Бронирований' },
+ { value: '500+', label: 'Отелей' },
+ { value: '1M+', label: 'Бронирований' },
{ value: '99.9%', label: 'Аптайм' },
].map(s => (
@@ -73,14 +505,28 @@ export function LoginPage() {
))}
+
+ {/* Registration benefits highlight */}
+ {mode === 'register' && (
+
+ {[
+ 'Бесплатный период 14 дней без ввода карты',
+ 'Настройка за 15 минут — импорт из любой PMS',
+ 'Поддержка на русском языке 24/7',
+ ].map(b => (
+
+ ))}
+
+ )}
-
- © 2026 HotelSync · SaaS PMS Platform
-
+ © 2026 HotelSync · SaaS PMS Platform
- {/* Right panel — login form */}
+ {/* Right panel */}
@@ -88,7 +534,7 @@ export function LoginPage() {
-
+
{/* Mobile logo */}
@@ -100,92 +546,10 @@ export function LoginPage() {
-
- Добро пожаловать
-
-
- Войдите в свой аккаунт для доступа к панели управления
-
-
- {/* Form */}
-
-
- {/* Demo hint */}
-
-
- Демо-аккаунты (пароль: demo):
-
-
- {DEMO_EMAILS.map(e => (
- { setEmail(e); setPassword('demo'); setError('') }}
- className={cn(
- 'text-left text-xs px-2 py-1 rounded transition-colors',
- email === e
- ? 'text-brand-700 dark:text-brand-300 bg-brand-50 dark:bg-brand-900/30 font-medium'
- : 'text-slate-500 dark:text-slate-400 hover:text-brand-600 dark:hover:text-brand-400',
- )}
- >
- {e}
-
- ))}
-
-
+ {mode === 'login'
+ ?
setMode('register')} />
+ : setMode('login')} />
+ }
diff --git a/src/pages/UsersPage.tsx b/src/pages/UsersPage.tsx
new file mode 100644
index 0000000..588629b
--- /dev/null
+++ b/src/pages/UsersPage.tsx
@@ -0,0 +1,536 @@
+import { useState } from 'react'
+import {
+ Plus, Pencil, Trash2, Search, Shield, User as UserIcon,
+ Sparkles, Mail, Phone, X as XIcon, Eye, EyeOff, CheckCircle2, AlertCircle,
+} from 'lucide-react'
+import { cn } from '../lib/utils'
+import { Modal } from '../components/ui/Modal'
+
+// ── Types ──────────────────────────────────────────────────────────────────────
+
+type StaffRole = 'hotel_manager' | 'housekeeper' | 'receptionist' | 'accountant' | 'security'
+
+interface StaffUser {
+ id: string
+ firstName: string
+ lastName: string
+ email: string
+ phone?: string
+ role: StaffRole
+ position: string // свободная должность, напр. "Старший администратор"
+ isActive: boolean
+ createdAt: string
+ lastLogin?: string
+ avatarColor: string
+}
+
+// ── Constants ──────────────────────────────────────────────────────────────────
+
+const ROLE_META: Record
= {
+ hotel_manager: { label: 'Менеджер', color: 'bg-brand-100 text-brand-700 dark:bg-brand-900/30 dark:text-brand-300', icon: Shield },
+ receptionist: { label: 'Ресепшн', color: 'bg-sky-100 text-sky-700 dark:bg-sky-900/30 dark:text-sky-300', icon: UserIcon },
+ housekeeper: { label: 'Горничная', color: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-300', icon: Sparkles },
+ accountant: { label: 'Бухгалтер', color: 'bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-300', icon: UserIcon },
+ security: { label: 'Охрана', color: 'bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-300', icon: Shield },
+}
+
+const AVATAR_COLORS = [
+ '#4F46E5', '#059669', '#2563EB', '#7C3AED',
+ '#DC2626', '#D97706', '#DB2777', '#0891B2',
+]
+
+const DEFAULT_POSITIONS: Record = {
+ hotel_manager: ['Управляющий', 'Заместитель управляющего', 'Менеджер смены'],
+ receptionist: ['Старший администратор', 'Администратор', 'Ночной администратор'],
+ housekeeper: ['Старшая горничная', 'Горничная', 'Уборщик'],
+ accountant: ['Главный бухгалтер', 'Бухгалтер', 'Финансовый менеджер'],
+ security: ['Начальник охраны', 'Охранник', 'Контролёр доступа'],
+}
+
+// ── Mock data ──────────────────────────────────────────────────────────────────
+
+const MOCK_USERS: StaffUser[] = [
+ {
+ id: 'u1', firstName: 'Александр', lastName: 'Петров',
+ email: 'manager@grand-palace.ru', phone: '+7 (999) 123-45-67',
+ role: 'hotel_manager', position: 'Управляющий',
+ isActive: true, createdAt: '2025-01-15', lastLogin: '2026-03-11',
+ avatarColor: '#4F46E5',
+ },
+ {
+ id: 'u2', firstName: 'Мария', lastName: 'Сидорова',
+ email: 'reception@grand-palace.ru', phone: '+7 (999) 234-56-78',
+ role: 'receptionist', position: 'Старший администратор',
+ isActive: true, createdAt: '2025-03-01', lastLogin: '2026-03-10',
+ avatarColor: '#059669',
+ },
+ {
+ id: 'u3', firstName: 'Наталья', lastName: 'Козлова',
+ email: 'cleaner@grand-palace.ru',
+ role: 'housekeeper', position: 'Старшая горничная',
+ isActive: true, createdAt: '2025-04-10', lastLogin: '2026-03-11',
+ avatarColor: '#2563EB',
+ },
+ {
+ id: 'u4', firstName: 'Дмитрий', lastName: 'Волков',
+ email: 'night@grand-palace.ru', phone: '+7 (999) 345-67-89',
+ role: 'receptionist', position: 'Ночной администратор',
+ isActive: false, createdAt: '2025-06-20',
+ avatarColor: '#7C3AED',
+ },
+ {
+ id: 'u5', firstName: 'Елена', lastName: 'Морозова',
+ email: 'accounting@grand-palace.ru',
+ role: 'accountant', position: 'Главный бухгалтер',
+ isActive: true, createdAt: '2025-02-01', lastLogin: '2026-03-09',
+ avatarColor: '#D97706',
+ },
+]
+
+// ── User Modal ─────────────────────────────────────────────────────────────────
+
+function UserModal({
+ open, user, onClose, onSave,
+}: {
+ open: boolean
+ user?: StaffUser
+ onClose: () => void
+ onSave: (u: StaffUser) => void
+}) {
+ const [form, setForm] = useState({
+ firstName: user?.firstName ?? '',
+ lastName: user?.lastName ?? '',
+ email: user?.email ?? '',
+ phone: user?.phone ?? '',
+ role: (user?.role ?? 'receptionist') as StaffRole,
+ position: user?.position ?? '',
+ isActive: user?.isActive ?? true,
+ avatarColor: user?.avatarColor ?? AVATAR_COLORS[Math.floor(Math.random() * AVATAR_COLORS.length)],
+ })
+ const [password, setPassword] = useState('')
+ const [showPass, setShowPass] = useState(false)
+ const [errors, setErrors] = useState>({})
+ const isNew = !user
+
+ const set = (k: K, v: typeof form[K]) =>
+ setForm(prev => ({ ...prev, [k]: v }))
+
+ const validate = () => {
+ const e: Record = {}
+ if (!form.firstName.trim()) e.firstName = 'Введите имя'
+ if (!form.lastName.trim()) e.lastName = 'Введите фамилию'
+ if (!form.email.trim()) e.email = 'Введите email'
+ if (!form.position.trim()) e.position = 'Введите должность'
+ if (isNew && password.length < 6) e.password = 'Минимум 6 символов'
+ setErrors(e)
+ return Object.keys(e).length === 0
+ }
+
+ const handleSave = () => {
+ if (!validate()) return
+ onSave({
+ ...form,
+ id: user?.id ?? `u-${Date.now()}`,
+ createdAt: user?.createdAt ?? new Date().toISOString().slice(0, 10),
+ lastLogin: user?.lastLogin,
+ })
+ }
+
+ const suggestions = DEFAULT_POSITIONS[form.role]
+
+ return (
+
+ Отмена
+
+ {isNew ? 'Создать' : 'Сохранить'}
+
+ >
+ }
+ >
+
+ {/* Avatar color + name */}
+
+
+ {(form.firstName[0] ?? '?').toUpperCase()}
+
+
+ {AVATAR_COLORS.map(c => (
+ set('avatarColor', c)}
+ className={cn(
+ 'w-6 h-6 rounded-lg transition-all',
+ form.avatarColor === c && 'ring-2 ring-offset-1 ring-slate-400',
+ )}
+ style={{ backgroundColor: c }}
+ />
+ ))}
+
+
+
+
+
+
Имя *
+
set('firstName', e.target.value)} />
+ {errors.firstName &&
{errors.firstName}
}
+
+
+
Фамилия *
+
set('lastName', e.target.value)} />
+ {errors.lastName &&
{errors.lastName}
}
+
+
+
+
+
+
Email *
+
set('email', e.target.value)} />
+ {errors.email &&
{errors.email}
}
+
+
+ Телефон
+ set('phone', e.target.value)} />
+
+
+
+ {/* Role */}
+
+
Роль в системе *
+
+ {(Object.entries(ROLE_META) as [StaffRole, typeof ROLE_META[StaffRole]][]).map(([id, meta]) => (
+ { set('role', id); set('position', '') }}
+ className={cn(
+ 'flex items-center gap-1.5 px-2.5 py-2 rounded-lg text-xs font-medium border text-left transition-colors',
+ form.role === id
+ ? 'bg-brand-600 border-brand-600 text-white'
+ : 'bg-white dark:bg-slate-700 border-slate-200 dark:border-slate-600 text-slate-600 dark:text-slate-300 hover:border-brand-400',
+ )}
+ >
+
+ {meta.label}
+
+ ))}
+
+
+
+ {/* Position */}
+
+
Должность *
+
set('position', e.target.value)} />
+ {errors.position &&
{errors.position}
}
+
+ {suggestions.map(s => (
+ set('position', s)}
+ className={cn(
+ 'text-xs px-2 py-0.5 rounded-full border transition-colors',
+ form.position === s
+ ? 'bg-brand-600 border-brand-600 text-white'
+ : 'border-slate-200 dark:border-slate-600 text-slate-500 dark:text-slate-400 hover:border-brand-400 hover:text-brand-600',
+ )}
+ >
+ {s}
+
+ ))}
+
+
+
+ {/* Password */}
+
+
+ {isNew ? 'Пароль *' : 'Новый пароль (оставьте пустым, чтобы не менять)'}
+
+
+ setPassword(e.target.value)}
+ />
+ setShowPass(v => !v)}
+ className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-slate-600">
+ {showPass ? : }
+
+
+ {errors.password &&
{errors.password}
}
+
+
+ {/* Active toggle */}
+
+
+
Активный сотрудник
+
Неактивные не могут войти в систему
+
+
set('isActive', !form.isActive)}
+ className={cn('relative w-11 h-6 rounded-full transition-colors', form.isActive ? 'bg-brand-600' : 'bg-slate-200 dark:bg-slate-600')}
+ >
+
+
+
+
+
+ )
+}
+
+// ── Page ───────────────────────────────────────────────────────────────────────
+
+export function UsersPage() {
+ const [users, setUsers] = useState(MOCK_USERS)
+ const [search, setSearch] = useState('')
+ const [roleFilter, setRoleFilter] = useState('all')
+ const [modalOpen, setModalOpen] = useState(false)
+ const [editing, setEditing] = useState()
+ const [deleteId, setDeleteId] = useState(null)
+
+ const filtered = users.filter(u => {
+ const matchSearch = !search ||
+ `${u.firstName} ${u.lastName}`.toLowerCase().includes(search.toLowerCase()) ||
+ u.email.toLowerCase().includes(search.toLowerCase()) ||
+ u.position.toLowerCase().includes(search.toLowerCase())
+ const matchRole = roleFilter === 'all' || u.role === roleFilter
+ return matchSearch && matchRole
+ })
+
+ const openCreate = () => { setEditing(undefined); setModalOpen(true) }
+ const openEdit = (u: StaffUser) => { setEditing(u); setModalOpen(true) }
+
+ const handleSave = (u: StaffUser) => {
+ setUsers(prev => {
+ const idx = prev.findIndex(x => x.id === u.id)
+ if (idx >= 0) { const next = [...prev]; next[idx] = u; return next }
+ return [...prev, u]
+ })
+ setModalOpen(false)
+ }
+
+ const handleDelete = (id: string) => {
+ setUsers(prev => prev.filter(u => u.id !== id))
+ setDeleteId(null)
+ }
+
+ const stats = {
+ total: users.length,
+ active: users.filter(u => u.isActive).length,
+ managers: users.filter(u => u.role === 'hotel_manager').length,
+ }
+
+ return (
+
+ {/* Header */}
+
+
+
Сотрудники
+
{stats.total} пользователей · {stats.active} активных
+
+
+
+ Добавить сотрудника
+
+
+
+ {/* Stats */}
+
+ {(Object.entries(ROLE_META) as [StaffRole, typeof ROLE_META[StaffRole]][]).map(([role, meta]) => {
+ const count = users.filter(u => u.role === role).length
+ return (
+
setRoleFilter(roleFilter === role ? 'all' : role)}>
+
+
+
+ {meta.label}
+
+
+
{count}
+
+ )
+ })}
+
+
+ {/* Filters */}
+
+
+
+ setSearch(e.target.value)} />
+
+
+ setRoleFilter('all')}
+ className={cn('px-3 py-1.5 rounded-lg text-xs font-medium border transition-colors',
+ roleFilter === 'all'
+ ? 'bg-brand-600 text-white border-brand-600'
+ : 'bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-300 border-slate-200 dark:border-slate-600')}
+ >
+ Все
+
+ {(Object.entries(ROLE_META) as [StaffRole, typeof ROLE_META[StaffRole]][]).map(([role, meta]) => (
+ setRoleFilter(roleFilter === role ? 'all' : role)}
+ className={cn('px-3 py-1.5 rounded-lg text-xs font-medium border transition-colors',
+ roleFilter === role
+ ? 'bg-brand-600 text-white border-brand-600'
+ : 'bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-300 border-slate-200 dark:border-slate-600')}
+ >
+ {meta.label}
+
+ ))}
+
+
+
+ {/* Table */}
+
+
+
+
+
+ Сотрудник
+ Роль / Должность
+ Контакты
+ Последний вход
+ Статус
+
+
+
+
+ {filtered.map(u => {
+ const meta = ROLE_META[u.role]
+ return (
+
+
+
+
+ {u.firstName[0]?.toUpperCase()}
+
+
+
+ {u.lastName} {u.firstName}
+
+
{u.email}
+
+
+
+
+
+ {meta.label}
+
+ {u.position}
+
+
+
+
+ {u.email}
+
+ {u.phone && (
+
+ )}
+
+
+
+
+ {u.lastLogin
+ ? new Date(u.lastLogin).toLocaleDateString('ru-RU')
+ : '—'}
+
+
+
+
+ {u.isActive
+ ? <> Активен>
+ : <> Неактивен>}
+
+
+
+
+
openEdit(u)}
+ className="p-1.5 rounded-lg text-slate-400 hover:text-brand-600 hover:bg-brand-50 dark:hover:bg-brand-900/20 transition-colors"
+ >
+
+
+
setDeleteId(u.id)}
+ className="p-1.5 rounded-lg text-slate-400 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors"
+ >
+
+
+
+
+
+ )
+ })}
+
+
+
+ {filtered.length === 0 && (
+
+
+
Сотрудники не найдены
+
+ )}
+
+
+
+ {/* User modal */}
+
setModalOpen(false)}
+ onSave={handleSave}
+ />
+
+ {/* Delete confirm */}
+ {deleteId && (
+
+
+
Удалить сотрудника?
+
+ Сотрудник потеряет доступ к системе. Историю его действий можно будет просмотреть в журнале.
+
+
+ setDeleteId(null)} className="btn-secondary flex-1 justify-center">Отмена
+ handleDelete(deleteId)} className="flex-1 justify-center py-2 px-4 rounded-xl bg-red-600 hover:bg-red-700 text-white text-sm font-medium transition-colors">
+ Удалить
+
+
+
+
+ )}
+
+ )
+}