diff --git a/src/components/admin/ui-kit-section.tsx b/src/components/admin/ui-kit-section.tsx index 00fe61c..c871f80 100644 --- a/src/components/admin/ui-kit-section.tsx +++ b/src/components/admin/ui-kit-section.tsx @@ -16,6 +16,7 @@ import { StatesDemo } from './ui-kit/states-demo' import { MediaDemo } from './ui-kit/media-demo' import { InteractiveDemo } from './ui-kit/interactive-demo' import { BusinessDemo } from './ui-kit/business-demo' +import { TimesheetDemo } from './ui-kit/timesheet-demo' export function UIKitSection() { return ( @@ -69,6 +70,9 @@ export function UIKitSection() { Бизнес + + Табель + @@ -126,6 +130,10 @@ export function UIKitSection() { + + + + ) diff --git a/src/components/admin/ui-kit/business-demo.tsx b/src/components/admin/ui-kit/business-demo.tsx index 7e10287..9431579 100644 --- a/src/components/admin/ui-kit/business-demo.tsx +++ b/src/components/admin/ui-kit/business-demo.tsx @@ -1,20 +1,21 @@ -"use client" +"use client"; -import { useState } from 'react' -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' -import { Badge } from '@/components/ui/badge' -import { Button } from '@/components/ui/button' -import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' -import { Progress } from '@/components/ui/progress' -import { Input } from '@/components/ui/input' +import { useState } from "react"; +import { TimesheetDemo } from "./timesheet-demo"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { Progress } from "@/components/ui/progress"; +import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, -} from '@/components/ui/select' -import { +} from "@/components/ui/select"; +import { Calendar, Check, X, @@ -43,69 +44,71 @@ import { ChevronRight, Hash, Package2, - Truck -} from 'lucide-react' + Truck, +} from "lucide-react"; export function BusinessDemo() { - const [selectedProduct] = useState(null) - const [cartQuantity, setCartQuantity] = useState(1) - const [expandedSeller, setExpandedSeller] = useState(false) + const [selectedProduct] = useState(null); + const [cartQuantity, setCartQuantity] = useState(1); + const [expandedSeller, setExpandedSeller] = useState(false); // Данные для демонстрации const scheduleData = Array.from({ length: 30 }, (_, i) => ({ day: i + 1, - status: ['work', 'work', 'work', 'work', 'work', 'weekend', 'weekend'][i % 7], - hours: [8, 8, 8, 8, 8, 0, 0][i % 7] - })) + status: ["work", "work", "work", "work", "work", "weekend", "weekend"][ + i % 7 + ], + hours: [8, 8, 8, 8, 8, 0, 0][i % 7], + })); const products = [ { - id: '1', - name: 'iPhone 15 Pro Max 256GB', - article: 'APL-IP15PM-256', + id: "1", + name: "iPhone 15 Pro Max 256GB", + article: "APL-IP15PM-256", price: 89990, oldPrice: 99990, quantity: 45, - category: 'Электроника', - brand: 'Apple', + category: "Электроника", + brand: "Apple", rating: 4.8, reviews: 1234, - image: '/placeholder-phone.jpg', - seller: 'TechStore Moscow', + image: "/placeholder-phone.jpg", + seller: "TechStore Moscow", isNew: true, - inStock: true + inStock: true, }, { - id: '2', - name: 'Беспроводные наушники AirPods Pro', - article: 'APL-APP-PRO', + id: "2", + name: "Беспроводные наушники AirPods Pro", + article: "APL-APP-PRO", price: 24990, quantity: 23, - category: 'Аксессуары', - brand: 'Apple', + category: "Аксессуары", + brand: "Apple", rating: 4.6, reviews: 856, - image: '/placeholder-headphones.jpg', - seller: 'Audio Expert', + image: "/placeholder-headphones.jpg", + seller: "Audio Expert", isNew: false, - inStock: true + inStock: true, }, { - id: '3', - name: 'Ноутбук MacBook Air M2', - article: 'APL-MBA-M2', + id: "3", + name: "Ноутбук MacBook Air M2", + article: "APL-MBA-M2", price: 0, quantity: 0, - category: 'Компьютеры', - brand: 'Apple', + category: "Компьютеры", + brand: "Apple", rating: 4.9, reviews: 445, - image: '/placeholder-laptop.jpg', - seller: 'Digital World', + image: "/placeholder-laptop.jpg", + seller: "Digital World", isNew: false, - inStock: false - } - ] + inStock: false, + }, + ]; // Данные для поставки фулфилмента const fulfillmentSupply = { @@ -128,106 +131,129 @@ export function BusinessDemo() { logisticsPartnerId: "log1", status: "planned", totalValue: 2500000, - } + }; const employees = [ - { id: "emp1", firstName: "Иван", lastName: "Петров", position: "Менеджер склада" }, - { id: "emp2", firstName: "Мария", lastName: "Сидорова", position: "Логист" } - ] + { + id: "emp1", + firstName: "Иван", + lastName: "Петров", + position: "Менеджер склада", + }, + { + id: "emp2", + firstName: "Мария", + lastName: "Сидорова", + position: "Логист", + }, + ]; const logisticsPartners = [ { id: "log1", name: "ТК Энергия", fullName: "ООО ТК Энергия" }, - { id: "log2", name: "СДЭК", fullName: "ООО СДЭК" } - ] + { id: "log2", name: "СДЭК", fullName: "ООО СДЭК" }, + ]; const wholesalers = [ { - id: '1', - name: 'ТехноОпт Москва', + id: "1", + name: "ТехноОпт Москва", fullName: 'ООО "Технологии Оптом"', - inn: '7735123456', - type: 'WHOLESALE', - avatar: '/placeholder-company.jpg', + inn: "7735123456", + type: "WHOLESALE", + avatar: "/placeholder-company.jpg", rating: 4.8, reviewsCount: 2345, productsCount: 15670, completedOrders: 8934, - responseTime: '2 часа', - categories: ['Электроника', 'Компьютеры', 'Аксессуары'], - location: 'Москва, Россия', - workingSince: '2018', - verifiedBadges: ['verified', 'premium', 'fast-delivery'], - description: 'Крупнейший поставщик электроники и компьютерной техники в России', + responseTime: "2 часа", + categories: ["Электроника", "Компьютеры", "Аксессуары"], + location: "Москва, Россия", + workingSince: "2018", + verifiedBadges: ["verified", "premium", "fast-delivery"], + description: + "Крупнейший поставщик электроники и компьютерной техники в России", specialOffers: 3, - minOrder: 50000 + minOrder: 50000, }, { - id: '2', - name: 'СтройБаза Регион', - fullName: 'ИП Строительные материалы', - inn: '7735987654', - type: 'WHOLESALE', - avatar: '/placeholder-construction.jpg', + id: "2", + name: "СтройБаза Регион", + fullName: "ИП Строительные материалы", + inn: "7735987654", + type: "WHOLESALE", + avatar: "/placeholder-construction.jpg", rating: 4.5, reviewsCount: 1876, productsCount: 8430, completedOrders: 5621, - responseTime: '4 часа', - categories: ['Стройматериалы', 'Инструменты', 'Сантехника'], - location: 'Екатеринбург, Россия', - workingSince: '2015', - verifiedBadges: ['verified', 'eco-friendly'], - description: 'Надежный поставщик строительных материалов по всей России', + responseTime: "4 часа", + categories: ["Стройматериалы", "Инструменты", "Сантехника"], + location: "Екатеринбург, Россия", + workingSince: "2015", + verifiedBadges: ["verified", "eco-friendly"], + description: "Надежный поставщик строительных материалов по всей России", specialOffers: 1, - minOrder: 30000 - } - ] + minOrder: 30000, + }, + ]; const getStatusColor = (status: string) => { switch (status) { - case 'work': return 'bg-green-500' - case 'weekend': return 'bg-gray-400' - case 'vacation': return 'bg-blue-500' - case 'sick': return 'bg-yellow-500' - case 'absent': return 'bg-red-500' - default: return 'bg-gray-400' + case "work": + return "bg-green-500"; + case "weekend": + return "bg-gray-400"; + case "vacation": + return "bg-blue-500"; + case "sick": + return "bg-yellow-500"; + case "absent": + return "bg-red-500"; + default: + return "bg-gray-400"; } - } + }; const getStatusText = (status: string) => { switch (status) { - case 'work': return 'Работа' - case 'weekend': return 'Выходной' - case 'vacation': return 'Отпуск' - case 'sick': return 'Больничный' - case 'absent': return 'Прогул' - default: return 'Неизвестно' + case "work": + return "Работа"; + case "weekend": + return "Выходной"; + case "vacation": + return "Отпуск"; + case "sick": + return "Больничный"; + case "absent": + return "Прогул"; + default: + return "Неизвестно"; } - } + }; const formatPrice = (price: number) => { - return new Intl.NumberFormat('ru-RU', { - style: 'currency', - currency: 'RUB', - minimumFractionDigits: 0 - }).format(price) - } + return new Intl.NumberFormat("ru-RU", { + style: "currency", + currency: "RUB", + minimumFractionDigits: 0, + }).format(price); + }; const formatCurrency = (amount: number) => { return new Intl.NumberFormat("ru-RU", { style: "currency", currency: "RUB", minimumFractionDigits: 0, - }).format(amount) - } + }).format(amount); + }; const formatDate = (dateString: string) => { return new Date(dateString).toLocaleDateString("ru-RU", { day: "2-digit", month: "2-digit", year: "numeric", - }) - } + }); + }; const getStatusBadge = (status: string) => { const statusConfig = { @@ -247,17 +273,17 @@ export function BusinessDemo() { color: "text-purple-300 border-purple-400/30", label: "Обрабатывается", }, - } + }; const config = - statusConfig[status as keyof typeof statusConfig] || statusConfig.planned + statusConfig[status as keyof typeof statusConfig] || statusConfig.planned; return ( {config.label} - ) - } + ); + }; return (
@@ -272,11 +298,15 @@ export function BusinessDemo() {
- ИИ + + ИИ +

Иванов Иван Иванович

-

Менеджер по продажам • Март 2024

+

+ Менеджер по продажам • Март 2024 +

@@ -296,21 +326,29 @@ export function BusinessDemo() {
Сб
Вс
- +
{scheduleData.map((day, index) => (
-
{day.day}
-
+
+ {day.day} +
+
{day.hours > 0 && ( -
{day.hours}ч
+
+ {day.hours}ч +
)}
))} @@ -371,29 +409,44 @@ export function BusinessDemo() {
{products.map((product) => ( -
+
{/* Изображение товара */}
- + {/* Бейджи */}
{product.isNew && ( - Новинка + + Новинка + )} {product.oldPrice && ( - Скидка + + Скидка + )}
{/* Кнопки действий */}
- -
@@ -402,25 +455,39 @@ export function BusinessDemo() { {/* Информация о товаре */}
-

{product.name}

-

Артикул: {product.article}

+

+ {product.name} +

+

+ Артикул: {product.article} +

{/* Рейтинг и отзывы */}
- {product.rating} + + {product.rating} +
- ({product.reviews} отзывов) + + ({product.reviews} отзывов) +
{/* Категория и бренд */}
- + {product.category} - + {product.brand}
@@ -429,17 +496,25 @@ export function BusinessDemo() {
{product.price > 0 ? (
- {formatPrice(product.price)} + + {formatPrice(product.price)} + {product.oldPrice && ( - {formatPrice(product.oldPrice)} + + {formatPrice(product.oldPrice)} + )}
) : ( - Нет в наличии + + Нет в наличии + )} - + {product.inStock && product.quantity > 0 && ( -

В наличии: {product.quantity} шт.

+

+ В наличии: {product.quantity} шт. +

)}
@@ -453,21 +528,37 @@ export function BusinessDemo() { {product.inStock && product.price > 0 ? ( <>
- - {cartQuantity} -
- ) : ( - )} @@ -482,7 +573,9 @@ export function BusinessDemo() { {/* Карточка поставки фулфилмента */} - Карточка поставки фулфилмента + + Карточка поставки фулфилмента + @@ -641,10 +734,10 @@ export function BusinessDemo() { {/* Ответственный сотрудник */}
-

- Ответственный -

- @@ -685,7 +778,9 @@ export function BusinessDemo() { >
- {partner.name || partner.fullName || "Без названия"} + {partner.name || + partner.fullName || + "Без названия"} Логистический партнер @@ -719,7 +814,10 @@ export function BusinessDemo() {
{wholesalers.map((wholesaler) => ( -
+
{/* Заголовок карточки */}
@@ -728,27 +826,39 @@ export function BusinessDemo() { {wholesaler.name.charAt(0)} - +
-

{wholesaler.name}

- {wholesaler.verifiedBadges.includes('verified') && ( - Проверен +

+ {wholesaler.name} +

+ {wholesaler.verifiedBadges.includes("verified") && ( + + Проверен + )} - {wholesaler.verifiedBadges.includes('premium') && ( - Premium + {wholesaler.verifiedBadges.includes("premium") && ( + + Premium + )}
- -

{wholesaler.fullName}

-

ИНН: {wholesaler.inn}

- + +

+ {wholesaler.fullName} +

+

+ ИНН: {wholesaler.inn} +

+ {/* Рейтинг и статистика */}
{wholesaler.rating} - ({wholesaler.reviewsCount}) + + ({wholesaler.reviewsCount}) +
{wholesaler.completedOrders} заказов @@ -769,15 +879,19 @@ export function BusinessDemo() { Товаров
-
{wholesaler.productsCount.toLocaleString()}
+
+ {wholesaler.productsCount.toLocaleString()} +
- +
Ответ
-
{wholesaler.responseTime}
+
+ {wholesaler.responseTime} +
@@ -786,7 +900,11 @@ export function BusinessDemo() {

Категории:

{wholesaler.categories.map((category, index) => ( - + {category} ))} @@ -827,10 +945,16 @@ export function BusinessDemo() { Смотреть товары - -
@@ -839,6 +963,9 @@ export function BusinessDemo() {
+ + {/* Космически-галактические табели рабочего времени */} +
- ) -} \ No newline at end of file + ); +} diff --git a/src/components/admin/ui-kit/timesheet-demo.tsx b/src/components/admin/ui-kit/timesheet-demo.tsx new file mode 100644 index 0000000..ad7a16b --- /dev/null +++ b/src/components/admin/ui-kit/timesheet-demo.tsx @@ -0,0 +1,3751 @@ +"use client"; + +import React, { useState, useEffect } from "react"; + +interface CalendarDay { + day: number; + status: string; + hours: number; + overtime: number; + workType: string | null; + mood: string | null; + efficiency: number | null; + tasks: number; + breaks: number; +} +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { Progress } from "@/components/ui/progress"; + +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { + Clock, + Star, + Award, + ChevronLeft, + ChevronRight, + Settings, + Download, + Filter, + MoreHorizontal, + MapPin, + CheckCircle, + XCircle, + Coffee, + Home, + Plane, + Heart, + Zap, + Moon, + Activity, + Eye, + Plus, + X, +} from "lucide-react"; + +export function TimesheetDemo() { + const [selectedVariant, setSelectedVariant] = useState< + | "galaxy" + | "cosmic" + | "custom" + | "compact" + | "interactive" + | "multi-employee" + >("galaxy"); + const [selectedEmployee, setSelectedEmployee] = useState("employee1"); + const [selectedMonth, setSelectedMonth] = useState(new Date().getMonth()); + const [selectedYear, setSelectedYear] = useState(new Date().getFullYear()); + const [animatedStats, setAnimatedStats] = useState(false); + const [editableCalendarData, setEditableCalendarData] = useState< + CalendarDay[] + >([]); + const [calendarData, setCalendarData] = useState([]); + + // Данные сотрудников + const employees = [ + { + id: "employee1", + name: "Алексей Космонавтов", + position: "Senior Frontend Developer", + avatar: "/placeholder-employee-1.jpg", + department: "Отдел разработки", + level: "Senior", + experience: "5 лет", + efficiency: 95, + totalHours: 176, + workDays: 22, + overtime: 8, + projects: 3, + }, + { + id: "employee2", + name: "Мария Звездочетова", + position: "UX/UI Designer", + avatar: "/placeholder-employee-2.jpg", + department: "Дизайн-студия", + level: "Middle", + experience: "3 года", + efficiency: 88, + totalHours: 168, + workDays: 21, + overtime: 4, + projects: 5, + }, + { + id: "employee3", + name: "Иван Галактический", + position: "DevOps Engineer", + avatar: "/placeholder-employee-3.jpg", + department: "Инфраструктура", + level: "Lead", + experience: "7 лет", + efficiency: 92, + totalHours: 184, + workDays: 23, + overtime: 12, + projects: 2, + }, + ]; + + // Состояние для универсального табеля + const [employeesList, setEmployeesList] = useState(employees); + const [showAddForm, setShowAddForm] = useState(false); + const [newEmployee, setNewEmployee] = useState({ + name: '', + position: '', + department: '', + level: 'Junior' + }); + + // Генерируем данные календаря для всех сотрудников + const generateEmployeeCalendarData = () => { + const daysInMonth = new Date(selectedYear, selectedMonth + 1, 0).getDate(); + const employeeData: { [key: string]: CalendarDay[] } = {}; + + employeesList.forEach(employee => { + employeeData[employee.id] = Array.from({ length: daysInMonth }, (_, i) => { + const dayOfWeek = (new Date(selectedYear, selectedMonth, 1).getDay() === 0 ? 6 : new Date(selectedYear, selectedMonth, 1).getDay() - 1 + i) % 7; + const isWeekend = dayOfWeek >= 5; + + return { + day: i + 1, + status: isWeekend + ? "weekend" + : Math.random() > 0.95 + ? "sick" + : Math.random() > 0.9 + ? "vacation" + : "work", + hours: isWeekend ? 0 : Math.floor(Math.random() * 3) + 7, + overtime: Math.random() > 0.8 ? Math.floor(Math.random() * 3) + 1 : 0, + workType: isWeekend ? null : ["office", "remote", "hybrid"][Math.floor(Math.random() * 3)], + mood: isWeekend ? null : ["excellent", "good", "normal", "tired"][Math.floor(Math.random() * 4)], + efficiency: isWeekend ? null : Math.floor(Math.random() * 30) + 70, + tasks: isWeekend ? 0 : Math.floor(Math.random() * 8) + 2, + breaks: isWeekend ? 0 : Math.floor(Math.random() * 3) + 1, + }; + }); + }); + + return employeeData; + }; + + const [allEmployeesData, setAllEmployeesData] = useState(generateEmployeeCalendarData()); + + // Добавление нового сотрудника + const handleAddEmployee = () => { + if (newEmployee.name && newEmployee.position) { + const newEmp = { + id: `employee${Date.now()}`, + name: newEmployee.name, + position: newEmployee.position, + department: newEmployee.department, + level: newEmployee.level, + avatar: `/placeholder-employee-${employeesList.length + 1}.jpg`, + experience: "Новый сотрудник", + efficiency: Math.floor(Math.random() * 20) + 80, + totalHours: 0, + workDays: 0, + overtime: 0, + projects: Math.floor(Math.random() * 5) + 1, + }; + setEmployeesList([...employeesList, newEmp]); + setNewEmployee({ name: '', position: '', department: '', level: 'Junior' }); + setShowAddForm(false); + } + }; + + // Удаление сотрудника + const handleRemoveEmployee = (employeeId: string) => { + setEmployeesList(employeesList.filter(emp => emp.id !== employeeId)); + }; + + // Получение цвета для сотрудника + const getEmployeeColor = (index: number) => { + const colors = [ + 'from-cyan-500 to-blue-500', + 'from-pink-500 to-purple-500', + 'from-emerald-500 to-teal-500', + 'from-orange-500 to-red-500', + 'from-yellow-500 to-amber-500', + 'from-indigo-500 to-purple-500', + 'from-green-500 to-lime-500', + 'from-rose-500 to-pink-500' + ]; + return colors[index % colors.length]; + }; + + // Получение статуса дня для конкретного сотрудника + const getDayStatus = (employeeId: string, dayIndex: number) => { + return allEmployeesData[employeeId]?.[dayIndex] || null; + }; + + // Подсчет работающих сотрудников в конкретный день + const getWorkingEmployeesCount = (dayIndex: number) => { + return employeesList.filter(emp => { + const dayData = getDayStatus(emp.id, dayIndex); + return dayData?.status === 'work'; + }).length; + }; + + // Анимация статистики + useEffect(() => { + const timer = setTimeout(() => setAnimatedStats(true), 500); + return () => clearTimeout(timer); + }, []); + + // Обновляем данные при изменении списка сотрудников или месяца + useEffect(() => { + setAllEmployeesData(generateEmployeeCalendarData()); + }, [employeesList, selectedMonth, selectedYear]); + + // Инициализация данных календаря для интерактивного режима + useEffect(() => { + if (editableCalendarData.length === 0 && calendarData.length > 0) { + setEditableCalendarData([...calendarData]); + } + }, [calendarData, editableCalendarData.length]); + + // Подсчет статистики на основе редактируемых данных + const interactiveStats = React.useMemo(() => { + if (editableCalendarData.length === 0) { + return { + totalHours: 0, + workDays: 0, + vacation: 0, + sick: 0, + overtime: 0, + avgEfficiency: 0, + }; + } + + const workDays = editableCalendarData.filter( + (day) => day.status === "work" + ).length; + const totalHours = editableCalendarData.reduce( + (sum, day) => sum + day.hours, + 0 + ); + const vacation = editableCalendarData.filter( + (day) => day.status === "vacation" + ).length; + const sick = editableCalendarData.filter( + (day) => day.status === "sick" + ).length; + const overtime = editableCalendarData.reduce( + (sum, day) => sum + day.overtime, + 0 + ); + const avgEfficiency = + workDays > 0 + ? Math.round( + editableCalendarData.reduce( + (sum, day) => sum + (day.efficiency || 0), + 0 + ) / workDays + ) + : 0; + + return { + totalHours, + workDays, + vacation, + sick, + overtime, + avgEfficiency, + }; + }, [editableCalendarData]); + + // Функция для изменения статуса дня + const toggleDayStatus = (dayIndex: number) => { + const statuses = ["work", "weekend", "vacation", "sick", "absent"]; + const currentDay = editableCalendarData[dayIndex]; + if (!currentDay) return; + + const currentStatusIndex = statuses.indexOf(currentDay.status); + const nextStatusIndex = (currentStatusIndex + 1) % statuses.length; + const newStatus = statuses[nextStatusIndex]; + + const updatedData = [...editableCalendarData]; + updatedData[dayIndex] = { + ...currentDay, + status: newStatus, + hours: newStatus === "work" ? 8 : 0, + overtime: newStatus === "work" ? Math.floor(Math.random() * 3) : 0, + }; + + setEditableCalendarData(updatedData); + }; + + const currentEmployee = + employees.find((emp) => emp.id === selectedEmployee) || employees[0]; + + // Обновление данных при изменении месяца/года + useEffect(() => { + const generateData = () => { + const daysInMonth = new Date( + selectedYear, + selectedMonth + 1, + 0 + ).getDate(); + const firstDay = new Date(selectedYear, selectedMonth, 1).getDay(); + const adjustedFirstDay = firstDay === 0 ? 6 : firstDay - 1; + + const workTypes = ["office", "remote", "hybrid"]; + const moods = ["excellent", "good", "normal", "tired"]; + + return Array.from({ length: daysInMonth }, (_, i) => { + const dayOfWeek = (adjustedFirstDay + i) % 7; + const isWeekend = dayOfWeek >= 5; + + return { + day: i + 1, + status: isWeekend + ? "weekend" + : Math.random() > 0.95 + ? "sick" + : Math.random() > 0.9 + ? "vacation" + : "work", + hours: isWeekend ? 0 : Math.floor(Math.random() * 3) + 7, + overtime: Math.random() > 0.8 ? Math.floor(Math.random() * 3) + 1 : 0, + workType: isWeekend + ? null + : workTypes[Math.floor(Math.random() * workTypes.length)], + mood: isWeekend + ? null + : moods[Math.floor(Math.random() * moods.length)], + efficiency: isWeekend ? null : Math.floor(Math.random() * 30) + 70, + tasks: isWeekend ? 0 : Math.floor(Math.random() * 8) + 2, + breaks: isWeekend ? 0 : Math.floor(Math.random() * 3) + 1, + }; + }); + }; + + setCalendarData(generateData()); + setAnimatedStats(false); + const timer = setTimeout(() => setAnimatedStats(true), 300); + return () => clearTimeout(timer); + }, [selectedMonth, selectedYear]); + + const getStatusColor = (status: string) => { + switch (status) { + case "work": + return "bg-gradient-to-r from-emerald-500 to-green-500"; + case "weekend": + return "bg-gradient-to-r from-slate-500 to-gray-500"; + case "vacation": + return "bg-gradient-to-r from-blue-500 to-cyan-500"; + case "sick": + return "bg-gradient-to-r from-amber-500 to-orange-500"; + case "absent": + return "bg-gradient-to-r from-red-500 to-rose-500"; + default: + return "bg-gradient-to-r from-slate-500 to-gray-500"; + } + }; + + const getWorkTypeIcon = (workType: string | null) => { + switch (workType) { + case "office": + return ; + case "remote": + return ; + case "hybrid": + return ; + default: + return null; + } + }; + + const getMoodIcon = (mood: string | null) => { + switch (mood) { + case "excellent": + return ; + case "good": + return ; + case "normal": + return ; + case "tired": + return ; + default: + return null; + } + }; + + const monthNames = [ + "Январь", + "Февраль", + "Март", + "Апрель", + "Май", + "Июнь", + "Июль", + "Август", + "Сентябрь", + "Октябрь", + "Ноябрь", + "Декабрь", + ]; + + const dayNames = ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"]; + + // Статистика + const stats = { + totalHours: calendarData.reduce((sum, day) => sum + day.hours, 0), + workDays: calendarData.filter((day) => day.status === "work").length, + weekends: calendarData.filter((day) => day.status === "weekend").length, + vacation: calendarData.filter((day) => day.status === "vacation").length, + sick: calendarData.filter((day) => day.status === "sick").length, + overtime: calendarData.reduce((sum, day) => sum + day.overtime, 0), + avgEfficiency: Math.round( + calendarData + .filter((day) => day.efficiency) + .reduce( + (sum, day, _, arr) => sum + (day.efficiency || 0) / arr.length, + 0 + ) + ), + totalTasks: calendarData.reduce((sum, day) => sum + day.tasks, 0), + }; + + const renderGalaxyVariant = () => ( + + {/* Космический фон с анимацией */} +
+
+ + {/* Плавающие частицы */} +
+
+
+
+
+ + +
+
+
+ + + + {currentEmployee.name + .split(" ") + .map((n) => n[0]) + .join("")} + + +
+
+
+
+ +
+

+ {currentEmployee.name} +

+

+ {currentEmployee.position} +

+
+ {currentEmployee.department} + + + {currentEmployee.level} + + + {currentEmployee.experience} +
+
+
+ +
+
+ {animatedStats ? stats.totalHours : 0}ч +
+

+ Отработано в {monthNames[selectedMonth].toLowerCase()} +

+
+
+ + + {currentEmployee.efficiency}% + +
+
+
+
+ + {/* Навигация по месяцам */} +
+
+ +
+ +
+ + +
+ {monthNames[selectedMonth]} {selectedYear} +
+ + +
+ +
+ + +
+
+
+ + + {/* Статистические карты */} +
+
+
+ +
+
+ {animatedStats ? stats.totalHours : 0} +
+
Часов
+
+
+ +
+ +
+
+ +
+
+ {animatedStats ? stats.workDays : 0} +
+
Рабочих дней
+
+
+ +
+ +
+
+ +
+
+ {animatedStats ? stats.vacation : 0} +
+
Отпуск
+
+
+ +
+ +
+
+ +
+
+ {animatedStats ? stats.sick : 0} +
+
Больничный
+
+
+ +
+ +
+
+ +
+
+ {animatedStats ? stats.overtime : 0} +
+
Переработка
+
+
+ +
+ +
+
+ +
+
+ {animatedStats ? stats.avgEfficiency : 0}% +
+
Эффективность
+
+
+ +
+
+ + {/* Календарь */} +
+ {/* Заголовки дней недели */} +
+ {dayNames.map((day) => ( +
+ {day} +
+ ))} +
+ + {/* Дни месяца */} +
+ {/* Пустые ячейки для начала месяца */} + {Array.from({ + length: + new Date(selectedYear, selectedMonth, 1).getDay() === 0 + ? 6 + : new Date(selectedYear, selectedMonth, 1).getDay() - 1, + }).map((_, index) => ( +
+ ))} + + {/* Дни месяца */} + {calendarData.map((day, index) => ( +
+
+
+ + {day.day} + + {day.workType && ( +
+ {getWorkTypeIcon(day.workType)} +
+ )} +
+ + {day.status === "work" && ( +
+
+ + {day.hours}ч + + {day.overtime > 0 && ( + + +{day.overtime} + + )} +
+ +
+ {getMoodIcon(day.mood)} + {day.efficiency && ( + + {day.efficiency}% + + )} +
+
+ )} + + {day.status !== "work" && day.status !== "weekend" && ( +
+
+
+ )} +
+
+ ))} +
+
+ + {/* Легенда */} +
+
+
+ Работа +
+
+
+ Выходной +
+
+
+ Отпуск +
+
+
+ Больничный +
+
+
+ Прогул +
+
+
+
+ ); + + const renderCosmicVariant = () => ( + + {/* Космический фон с эффектом туманности */} +
+
+
+ + {/* Звездное поле */} +
+
+
+
+
+
+
+ + +
+
+
+
+ + + + {currentEmployee.name + .split(" ") + .map((n) => n[0]) + .join("")} + + + + {/* Орбитальные элементы */} +
+ +
+ +
+ +
+
+ +
+

+ {currentEmployee.name} +

+

+ {currentEmployee.position} +

+
+ + {currentEmployee.department} + + + {currentEmployee.level} + + + {currentEmployee.experience} опыта + +
+
+
+ +
+
+ {animatedStats ? stats.totalHours : 0} +
+

+ часов в {monthNames[selectedMonth].toLowerCase()} +

+ +
+
+ + + {currentEmployee.efficiency}% + +
+
+ + + {currentEmployee.projects} + +
+
+
+
+ + {/* Панель управления */} +
+
+ +
+ +
+ + +
+ {monthNames[selectedMonth]} {selectedYear} +
+ + +
+ +
+ + + +
+
+
+ + + {/* Круговая статистика */} +
+
+
+
+
+
+ + + + +
+ +
+
+
+ {animatedStats ? stats.totalHours : 0} +
+
Часов
+
+
+
+ +
+
+
+
+
+ + + + +
+ +
+
+
+ {animatedStats ? stats.workDays : 0} +
+
Рабочих
+
+
+
+ +
+
+
+
+
+ + + + +
+ +
+
+
+ {animatedStats ? stats.vacation : 0} +
+
Отпуск
+
+
+
+ +
+
+
+
+
+ + + + +
+ +
+
+
+ {animatedStats ? stats.sick : 0} +
+
Больничный
+
+
+
+ +
+
+
+
+
+ + + + +
+ +
+
+
+ {animatedStats ? stats.overtime : 0} +
+
Переработка
+
+
+
+ +
+
+
+
+
+ + + + +
+ +
+
+
+ {animatedStats ? stats.avgEfficiency : 0}% +
+
КПД
+
+
+
+
+ + {/* Календарь в виде гексагональной сетки */} +
+ {/* Заголовки дней недели */} +
+
+ {dayNames.map((day) => ( +
+ {day} +
+ ))} +
+
+ + {/* Календарная сетка */} +
+
+ {/* Пустые ячейки для начала месяца */} + {Array.from({ + length: + new Date(selectedYear, selectedMonth, 1).getDay() === 0 + ? 6 + : new Date(selectedYear, selectedMonth, 1).getDay() - 1, + }).map((_, index) => ( +
+ ))} + + {/* Дни месяца */} + {calendarData.map((day, index) => ( +
+ {/* Эффект свечения */} +
+ +
+
+ + {day.day} + + {day.workType && ( +
+ {getWorkTypeIcon(day.workType)} +
+ )} +
+ + {day.status === "work" && ( +
+
+ + {day.hours}ч + + {day.overtime > 0 && ( + + +{day.overtime} + + )} +
+ +
+ {getMoodIcon(day.mood)} + {day.efficiency && ( +
+
+ {day.efficiency}% +
+
+
+
+
+ )} +
+
+ )} + + {day.status !== "work" && day.status !== "weekend" && ( +
+
+
+ )} +
+
+ ))} +
+
+
+ + {/* Расширенная легенда */} +
+

+ Легенда статусов +

+
+
+
+ +
+ Работа + + Обычный рабочий день + +
+ +
+
+ +
+ + Выходной + + + Суббота/Воскресенье + +
+ +
+
+ +
+ Отпуск + + Оплачиваемый отпуск + +
+ +
+
+ +
+ + Больничный + + + По болезни + +
+ +
+
+ +
+ Прогул + + Неявка без причины + +
+
+
+
+ + {/* SVG градиенты для круговых диаграмм */} + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ); + + const renderCustomVariant = () => ( + + {/* Космический фон с плавающими частицами и звездным полем (из Галактического) */} +
+
+ + {/* Плавающие частицы */} +
+
+
+
+
+
+ + {/* Звездное поле */} +
+ {Array.from({ length: 20 }).map((_, i) => ( +
+ ))} +
+
+ + + {/* Заголовок сотрудника */} +
+
+ + + + {currentEmployee.name + .split(" ") + .map((n) => n[0]) + .join("")} + + + +
+

+ {currentEmployee.name} +

+

+ {currentEmployee.position} +

+
+ {currentEmployee.department} + + {currentEmployee.level} + + {currentEmployee.experience} +
+
+ + {/* Круговые диаграммы статистики (из Космического) */} +
+
+
+ + + + +
+ + {animatedStats ? stats.totalHours : 0} + +
+
+

Часов

+
+ +
+
+ + + + +
+ + {currentEmployee.efficiency}% + +
+
+

Эффективность

+
+
+
+
+ + {/* Навигация и управление */} +
+
+ +
+ +
+ + +
+
+ {monthNames[selectedMonth]} {selectedYear} +
+
+ + +
+ +
+ + +
+
+ + {/* Статистика с круговыми диаграммами (из Космического) */} +
+
+
+
+
+
+ + + + +
+ +
+
+
+ {animatedStats ? stats.totalHours : 0} +
+

Часов

+
+
+
+ +
+
+
+
+
+ + + + +
+ +
+
+
+ {animatedStats ? stats.workDays : 0} +
+

Рабочих

+
+
+
+ +
+
+
+
+
+ + + + +
+ +
+
+
+ {animatedStats ? stats.vacation : 0} +
+

Отпуск

+
+
+
+ +
+
+
+
+
+ + + + +
+ +
+
+
+ {animatedStats ? stats.sick : 0} +
+

+ Больничный +

+
+
+
+ +
+
+
+
+
+ + + + +
+ +
+
+
+ {animatedStats ? stats.overtime : 0} +
+

+ Переработка +

+
+
+
+ +
+
+
+
+
+ + + + +
+ +
+
+
+ {animatedStats ? stats.avgEfficiency : 0}% +
+

КПД

+
+
+
+
+ + {/* Гексагональная календарная сетка */} +
+ {/* Заголовки дней недели */} +
+ {dayNames.map((day) => ( +
+ {day} +
+ ))} +
+ + {/* Календарная сетка */} +
+ {/* Пустые ячейки для начала месяца */} + {Array.from({ + length: + new Date(selectedYear, selectedMonth, 1).getDay() === 0 + ? 6 + : new Date(selectedYear, selectedMonth, 1).getDay() - 1, + }).map((_, index) => ( +
+ ))} + + {/* Дни месяца */} + {calendarData.map((day) => ( +
+ {/* Эффект свечения */} +
+ +
+
+ + {day.day} + + {day.workType && ( +
+ {getWorkTypeIcon(day.workType)} +
+ )} +
+ + {day.status === "work" && ( +
+
+ + {day.hours}ч + + {day.overtime > 0 && ( + + +{day.overtime} + + )} +
+ +
+ {getMoodIcon(day.mood)} + {day.efficiency && ( +
+
+ {day.efficiency}% +
+
+
+
+
+ )} +
+
+ )} + + {day.status !== "work" && day.status !== "weekend" && ( +
+
+
+ )} +
+
+ ))} +
+
+
+ + {/* SVG градиенты для круговых диаграмм */} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); + + // Компактный вариант для 13-дюймовых экранов + const renderCompactVariant = () => ( + + {/* Космический фон с плавающими частицами и звездным полем (из Галактического) */} +
+
+ + {/* Плавающие частицы */} +
+
+
+
+ + {/* Звездное поле */} +
+ {Array.from({ length: 15 }).map((_, i) => ( +
+ ))} +
+
+ + + {/* Компактный заголовок сотрудника */} +
+
+
+ + + + {currentEmployee.name + .split(" ") + .map((n) => n[0]) + .join("")} + + + +
+

+ {currentEmployee.name} +

+

+ {currentEmployee.position} +

+
+ {currentEmployee.department} + + {currentEmployee.level} +
+
+
+ + {/* Компактная навигация */} +
+ + + + +
+
+
+ + {/* Компактная статистика в одну строку */} +
+
+
+
+
+ + + + +
+ +
+
+
+ {animatedStats ? stats.totalHours : 0} +
+

Часов

+
+
+ +
+
+
+
+ + + + +
+ +
+
+
+ {animatedStats ? stats.workDays : 0} +
+

Рабочих

+
+
+ +
+
+
+
+ + + + +
+ +
+
+
+ {animatedStats ? stats.vacation : 0} +
+

Отпуск

+
+
+ +
+
+
+
+ + + + +
+ +
+
+
+ {animatedStats ? stats.sick : 0} +
+

Больничный

+
+
+ +
+
+
+
+ + + + +
+ +
+
+
+ {animatedStats ? stats.overtime : 0} +
+

Переработка

+
+
+ +
+
+
+
+ + + + +
+ +
+
+
+ {animatedStats ? stats.avgEfficiency : 0}% +
+

КПД

+
+
+
+ + {/* Компактная календарная сетка */} +
+ {/* Заголовки дней недели */} +
+
ПН
+
ВТ
+
СР
+
ЧТ
+
ПТ
+
СБ
+
ВС
+
+ + {/* Календарная сетка */} +
+ {calendarData.map((day, index) => ( +
+
+ + {day.day} + + + {day.status === "work" && ( +
+ {day.hours}ч + {day.overtime > 0 && ( + +{day.overtime} + )} +
+ )} + + {day.status !== "work" && day.status !== "weekend" && ( +
+
+
+ )} +
+
+ ))} +
+
+ + {/* Компактная легенда */} +
+
+
+ Работа +
+
+
+ Выходной +
+
+
+ Отпуск +
+
+
+ Больничный +
+
+
+ Прогул +
+
+
+ + {/* SVG градиенты */} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); + + // Интерактивный вариант с яркими цветами и кликабельными датами + const renderInteractiveVariant = () => ( + + {/* Космический фон с плавающими частицами и звездным полем */} +
+
+ + {/* Более яркие плавающие частицы */} +
+
+
+
+ + {/* Более яркое звездное поле */} +
+ {Array.from({ length: 20 }).map((_, i) => ( +
+ ))} +
+
+ + + {/* Компактный заголовок сотрудника с яркими цветами */} +
+
+
+ + + + {currentEmployee.name + .split(" ") + .map((n) => n[0]) + .join("")} + + + +
+

+ {currentEmployee.name} +

+

+ {currentEmployee.position} +

+
+ {currentEmployee.department} + + {currentEmployee.level} +
+
+
+ + {/* Компактная навигация с яркими цветами */} +
+ + + + +
+
+
+ + {/* Яркая статистика в одну строку */} +
+
+
+
+
+ + + + +
+ +
+
+
+ {animatedStats ? interactiveStats.totalHours : 0} +
+

Часов

+
+
+ +
+
+
+
+ + + + +
+ +
+
+
+ {animatedStats ? interactiveStats.workDays : 0} +
+

Рабочих

+
+
+ +
+
+
+
+ + + + +
+ +
+
+
+ {animatedStats ? interactiveStats.vacation : 0} +
+

Отпуск

+
+
+ +
+
+
+
+ + + + +
+ +
+
+
+ {animatedStats ? interactiveStats.sick : 0} +
+

Больничный

+
+
+ +
+
+
+
+ + + + +
+ +
+
+
+ {animatedStats ? interactiveStats.overtime : 0} +
+

Переработка

+
+
+ +
+
+
+
+ + + + +
+ +
+
+
+ {animatedStats ? interactiveStats.avgEfficiency : 0}% +
+

КПД

+
+
+
+ + {/* Интерактивная календарная сетка с яркими цветами */} +
+ {/* Заголовки дней недели */} +
+
ПН
+
ВТ
+
СР
+
ЧТ
+
ПТ
+
СБ
+
ВС
+
+ + {/* Интерактивная календарная сетка */} +
+ {editableCalendarData.map((day, index) => ( +
toggleDayStatus(index)} + className={`relative group cursor-pointer transition-all duration-300 transform hover:scale-105 ${ + day.status === "work" + ? "bg-gradient-to-br from-emerald-400/30 to-green-400/30 border-emerald-400/50 hover:border-emerald-300/70 shadow-lg shadow-emerald-500/20" + : day.status === "weekend" + ? "bg-gradient-to-br from-slate-400/30 to-gray-400/30 border-slate-400/50 hover:border-slate-300/70 shadow-lg shadow-slate-500/20" + : day.status === "vacation" + ? "bg-gradient-to-br from-blue-400/30 to-cyan-400/30 border-blue-400/50 hover:border-blue-300/70 shadow-lg shadow-blue-500/20" + : day.status === "sick" + ? "bg-gradient-to-br from-amber-400/30 to-orange-400/30 border-amber-400/50 hover:border-amber-300/70 shadow-lg shadow-amber-500/20" + : "bg-gradient-to-br from-red-400/30 to-rose-400/30 border-red-400/50 hover:border-red-300/70 shadow-lg shadow-red-500/20" + } rounded-xl border backdrop-blur-sm p-2 h-16`} + > +
+ + {day.day} + + + {day.status === "work" && ( +
+ {day.hours}ч + {day.overtime > 0 && ( + +{day.overtime} + )} +
+ )} + + {day.status !== "work" && day.status !== "weekend" && ( +
+
+
+ )} +
+ + {/* Индикатор интерактивности */} +
+
+
+
+ ))} +
+
+ + {/* Яркая легенда с подсказкой */} +
+
+
+
+ Работа +
+
+
+ Выходной +
+
+
+ Отпуск +
+
+
+ Больничный +
+
+
+ Прогул +
+
+
+ 💡 Кликните на дату, чтобы изменить статус +
+
+
+ + {/* Яркие SVG градиенты */} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); + + // Интерактивный вариант для нескольких сотрудников с яркими цветами + const renderMultiEmployeeInteractiveVariant = () => { + + const daysInMonth = new Date(selectedYear, selectedMonth + 1, 0).getDate(); + + return ( +
+ {/* Заголовок */} + +
+
+
+ + +
+
+

+ Универсальный табель учета рабочего времени +

+

+ {monthNames[selectedMonth]} {selectedYear} •{" "} + {employeesList.length} сотрудников +

+
+ +
+ + +
+ {monthNames[selectedMonth]} {selectedYear} +
+ + + + + + +
+
+ + {/* Форма добавления сотрудника */} + {showAddForm && ( +
+

+ Добавить нового сотрудника +

+
+ + setNewEmployee({ ...newEmployee, name: e.target.value }) + } + className="px-3 py-2 bg-white/10 border border-white/20 rounded-lg text-white placeholder-white/50 focus:outline-none focus:border-cyan-400/50" + /> + + setNewEmployee({ + ...newEmployee, + position: e.target.value, + }) + } + className="px-3 py-2 bg-white/10 border border-white/20 rounded-lg text-white placeholder-white/50 focus:outline-none focus:border-cyan-400/50" + /> + + setNewEmployee({ + ...newEmployee, + department: e.target.value, + }) + } + className="px-3 py-2 bg-white/10 border border-white/20 rounded-lg text-white placeholder-white/50 focus:outline-none focus:border-cyan-400/50" + /> + +
+
+ + +
+
+ )} +
+
+ + {/* Основной табель */} + +
+
+
+ + +
+ + {/* Заголовок таблицы */} + + + + {Array.from({ length: daysInMonth }, (_, i) => { + const date = new Date(selectedYear, selectedMonth, i + 1); + const dayOfWeek = date.getDay(); + const isWeekend = dayOfWeek === 0 || dayOfWeek === 6; + const workingCount = getWorkingEmployeesCount(i); + + return ( + + ); + })} + + + + + {/* Строки сотрудников */} + + {employeesList.map((employee, employeeIndex) => { + const employeeData = allEmployeesData[employee.id] || []; + const totalHours = employeeData.reduce( + (sum, day) => sum + day.hours, + 0 + ); + const workDays = employeeData.filter( + (day) => day.status === "work" + ).length; + const colorGradient = getEmployeeColor(employeeIndex); + + return ( + + {/* Информация о сотруднике */} + + + {/* Дни месяца */} + {employeeData.map((day, dayIndex) => { + const date = new Date( + selectedYear, + selectedMonth, + day.day + ); + const isWeekend = + date.getDay() === 0 || date.getDay() === 6; + + return ( + + ); + })} + + {/* Итого */} + + + ); + })} + + + {/* Итоговая строка */} + + + + {Array.from({ length: daysInMonth }, (_, dayIndex) => { + const workingCount = getWorkingEmployeesCount(dayIndex); + const totalHours = employeesList.reduce((sum, emp) => { + const dayData = getDayStatus(emp.id, dayIndex); + return sum + (dayData?.hours || 0); + }, 0); + + return ( + + ); + })} + + + +
+ Сотрудник + +
+ {dayNames[dayOfWeek === 0 ? 6 : dayOfWeek - 1]} +
+
+ {i + 1} +
+ {workingCount > 0 && ( +
+ {workingCount} чел. +
+ )} +
+ Итого +
+
+
+ + + + {employee.name + .split(" ") + .map((n) => n[0]) + .join("")} + + +
+
+ {employee.name} +
+
+ {employee.position} +
+
+ {employee.department} +
+
+
+ +
+
+
+ {day.status === "work" && ( + <> + + {day.hours}ч + + {day.overtime > 0 && ( + + +{day.overtime} + + )} + + )} + {day.status === "weekend" && ( + Вых + )} + {day.status === "vacation" && ( + Отп + )} + {day.status === "sick" && ( + Б/Л + )} + {day.status === "absent" && ( + Пр + )} +
+
+
+ {totalHours}ч +
+
+ {workDays} дней +
+
+ Итого по дням: + + {workingCount > 0 && ( +
+ {totalHours}ч +
+ )} + {workingCount > 0 && ( +
+ {workingCount} чел +
+ )} +
+
+ {employeesList.reduce((sum, emp) => { + const empData = allEmployeesData[emp.id] || []; + return ( + sum + + empData.reduce( + (daySum, day) => daySum + day.hours, + 0 + ) + ); + }, 0)} + ч +
+
+
+
+
+ + {/* Легенда */} + +
+
+
+ + +

+ Легенда статусов +

+
+
+
+ +
+
+ Работа +

Рабочий день

+
+
+ +
+
+ Вых +
+
+ Выходной +

Суббота/Воскресенье

+
+
+ +
+
+ Отп +
+
+ Отпуск +

Оплачиваемый отпуск

+
+
+ +
+
+ Б/Л +
+
+ + Больничный + +

По болезни

+
+
+ +
+
+ Пр +
+
+ Прогул +

Неявка

+
+
+
+ +
+

+ 💡 В заголовках дней показано количество работающих сотрудников +

+

+ 📊 В итоговой строке показаны общие часы и количество + сотрудников по дням +

+
+
+
+
+ ); + }; + + return ( +
+ {/* Селектор вариантов */} + + +
+ + Табель учета рабочего времени + +
+ +
+
+
+
+ + {/* Отображение выбранного варианта */} + {selectedVariant === "galaxy" && renderGalaxyVariant()} + {selectedVariant === "cosmic" && renderCosmicVariant()} + {selectedVariant === "custom" && renderCustomVariant()} + {selectedVariant === "compact" && renderCompactVariant()} + {selectedVariant === "interactive" && renderInteractiveVariant()} + {selectedVariant === "multi-employee" && + renderMultiEmployeeInteractiveVariant()} +
+ ); +}