diff --git a/.env.example b/.env.example
deleted file mode 100644
index ee09a5b..0000000
--- a/.env.example
+++ /dev/null
@@ -1,6 +0,0 @@
-VITE_FIREBASE_API_KEY=your_api_key
-VITE_FIREBASE_AUTH_DOMAIN=your_project.firebaseapp.com
-VITE_FIREBASE_PROJECT_ID=your_project_id
-VITE_FIREBASE_STORAGE_BUCKET=your_project.appspot.com
-VITE_FIREBASE_MESSAGING_SENDER_ID=your_sender_id
-VITE_FIREBASE_APP_ID=your_app_id
diff --git a/.vibe42-run.pid b/.vibe42-run.pid
index e1be92e..5bf2252 100644
--- a/.vibe42-run.pid
+++ b/.vibe42-run.pid
@@ -1 +1 @@
-799
+1444
diff --git a/index.html b/index.html
index 7181020..efa5afb 100644
--- a/index.html
+++ b/index.html
@@ -1,13 +1,639 @@
-
+
-
-
-
Путь стажёра
-
- {stages.map((stage, index) => (
-
-
= stage.id
- ? 'bg-primary-100 text-primary-700'
- : 'bg-gray-100 text-gray-400'
- }`}
- >
- {stage.icon}
- {stage.name}
-
- {index < stages.length - 1 && (
-
stage.id ? 'bg-primary-500' : 'bg-gray-300'
- }`}
- />
- )}
-
- ))}
-
-
- )
-}
diff --git a/src/context/StorageContext.jsx b/src/context/StorageContext.jsx
deleted file mode 100644
index 569de5e..0000000
--- a/src/context/StorageContext.jsx
+++ /dev/null
@@ -1,234 +0,0 @@
-import { createContext, useContext, useState, useEffect } from 'react'
-import { auth, signInWithEmailAndPassword, signOut as firebaseSignOut, onAuthStateChanged } from '../firebase'
-import { db, collection, getDocs, doc, setDoc, updateDoc } from 'firebase/firestore'
-
-const StorageContext = createContext(null)
-
-const CITIES = [
- 'Алматы', 'Астана', 'Шымкент', 'Караганда', 'Актобе', 'Тараз', 'Павлодар',
- 'Усть-Каменогорск', 'Семей', 'Атырау', 'Орал', 'Кызылорда', 'Костанай',
- 'Петропавловск', 'Актау', 'Темиртау', 'Другой'
-]
-
-const DEFAULT_DATA = {
- program: {
- title: 'Адаптационная программа стажёров',
- description: 'Программа профессиональной адаптации для новых сотрудников',
- startDate: '',
- endDate: '',
- collectionOpen: false,
- groupsFormed: false,
- examEnabled: false,
- numberOfGroups: 2
- },
- trainers: [
- { id: 1, name: 'Тренер 1', login: 'trainer1', password: 'pass1' },
- { id: 2, name: 'Тренер 2', login: 'trainer2', password: 'pass2' }
- ],
- programRules: 'Правила программы:\n1. Посещаемость обязательна\n2. Домашние задания сдавать вовремя\n3. Активно участвовать в обсуждениях',
- examRules: 'Правила экзамена:\n1. 10 вопросов\n2. Порог сдачи: 9 правильных ответов (90%)\n3. Время выполнения: 60 минут',
- sessions: [],
- trainees: []
-}
-
-export function StorageProvider({ children }) {
- const [user, setUser] = useState(null)
- const [data, setData] = useState(DEFAULT_DATA)
- const [loading, setLoading] = useState(true)
-
- useEffect(() => {
- const unsubscribe = onAuthStateChanged(auth, (user) => {
- setUser(user)
- setLoading(false)
- })
- return unsubscribe
- }, [])
-
- useEffect(() => {
- loadData()
- }, [])
-
- const loadData = async () => {
- try {
- const stored = localStorage.getItem('internAdaptationData')
- if (stored) {
- setData(JSON.parse(stored))
- }
- } catch (error) {
- console.error('Error loading data:', error)
- }
- }
-
- const saveData = (newData) => {
- setData(newData)
- localStorage.setItem('internAdaptationData', JSON.stringify(newData))
- }
-
- const updateProgram = (updates) => {
- const newData = { ...data, program: { ...data.program, ...updates } }
- saveData(newData)
- }
-
- const updateTrainers = (trainers) => {
- const newData = { ...data, trainers }
- saveData(newData)
- }
-
- const updateProgramRules = (rules) => {
- const newData = { ...data, programRules: rules }
- saveData(newData)
- }
-
- const updateExamRules = (rules) => {
- const newData = { ...data, examRules: rules }
- saveData(newData)
- }
-
- const addSession = (session) => {
- const newSessions = [...data.sessions, { ...session, id: Date.now() }]
- const newData = { ...data, sessions: newSessions }
- saveData(newData)
- }
-
- const removeSession = (sessionId) => {
- const newSessions = data.sessions.filter(s => s.id !== sessionId)
- const newData = { ...data, sessions: newSessions }
- saveData(newData)
- }
-
- const addTrainee = (trainee) => {
- const newTrainees = [...data.trainees, { ...trainee, id: Date.now(), groupId: null, attendance: {}, homeworkStatus: {}, examScore: null }]
- const newData = { ...data, trainees: newTrainees }
- saveData(newData)
- }
-
- const updateTrainee = (id, updates) => {
- const newTrainees = data.trainees.map(t => t.id === id ? { ...t, ...updates } : t)
- const newData = { ...data, trainees: newTrainees }
- saveData(newData)
- }
-
- const deleteTrainee = (id) => {
- const newTrainees = data.trainees.filter(t => t.id !== id)
- const newData = { ...data, trainees: newTrainees }
- saveData(newData)
- }
-
- const formGroups = () => {
- const { numberOfGroups } = data.program
- const trainees = [...data.trainees]
-
- // Группировка по подразделениям и городам
- const groups = Array.from({ length: numberOfGroups }, () => [])
-
- // Сортировка для равномерного распределения
- trainees.sort((a, b) => {
- if (a.division !== b.division) return a.division.localeCompare(b.division)
- return a.city.localeCompare(b.city)
- })
-
- // Распределение по группам
- trainees.forEach((trainee, index) => {
- groups[index % numberOfGroups].push(trainee.id)
- })
-
- // Обновление trainees с groupId
- const updatedTrainees = data.trainees.map(t => {
- const groupIndex = groups.findIndex(g => g.includes(t.id))
- return { ...t, groupId: groupIndex >= 0 ? groupIndex + 1 : null }
- })
-
- const newData = {
- ...data,
- trainees: updatedTrainees,
- program: { ...data.program, groupsFormed: true, collectionOpen: false }
- }
- saveData(newData)
- }
-
- const resetGroups = () => {
- const updatedTrainees = data.trainees.map(t => ({
- ...t,
- groupId: null,
- attendance: {},
- homeworkStatus: {},
- examScore: null
- }))
- const newData = {
- ...data,
- trainees: updatedTrainees,
- program: { ...data.program, groupsFormed: false, examEnabled: false }
- }
- saveData(newData)
- }
-
- const login = async (login, password) => {
- const trainer = data.trainers.find(t => t.login === login && t.password === password)
- if (!trainer) {
- throw new Error('Неверный логин или пароль')
- }
- // Используем демо-режим с localStorage
- localStorage.setItem('demoUser', JSON.stringify(trainer))
- window.dispatchEvent(new Event('storage'))
- }
-
- const logout = () => {
- localStorage.removeItem('demoUser')
- window.dispatchEvent(new Event('storage'))
- }
-
- const value = {
- user,
- data,
- loading,
- CITIES,
- updateProgram,
- updateTrainers,
- updateProgramRules,
- updateExamRules,
- addSession,
- removeSession,
- addTrainee,
- updateTrainee,
- deleteTrainee,
- formGroups,
- resetGroups,
- login,
- logout
- }
-
- return (
-
- {children}
-
- )
-}
-
-export function useStorage() {
- const context = useContext(StorageContext)
- if (!context) {
- throw new Error('useStorage must be used within StorageProvider')
- }
- return context
-}
-
-export function useAuth() {
- const [demoUser, setDemoUser] = useState(null)
-
- useEffect(() => {
- const user = localStorage.getItem('demoUser')
- if (user) {
- setDemoUser(JSON.parse(user))
- }
-
- const handler = () => {
- const user = localStorage.getItem('demoUser')
- setDemoUser(user ? JSON.parse(user) : null)
- }
-
- window.addEventListener('storage', handler)
- return () => window.removeEventListener('storage', handler)
- }, [])
-
- return { user: demoUser, loading: false }
-}
diff --git a/src/firebase.js b/src/firebase.js
deleted file mode 100644
index 25fdb87..0000000
--- a/src/firebase.js
+++ /dev/null
@@ -1,16 +0,0 @@
-// Демо-режим: Firebase не подключён, используется localStorage
-// Для подключения Firebase добавь зависимости и настрой firebaseConfig
-
-export const db = null
-export const auth = null
-
-export function collection() { return null }
-export function addDoc() { return null }
-export function getDocs() { return null }
-export function updateDoc() { return null }
-export function deleteDoc() { return null }
-export function doc() { return null }
-export function setDoc() { return null }
-export function signInWithEmailAndPassword() { return null }
-export function signOut() { return null }
-export function onAuthStateChanged() { return () => {} }
diff --git a/src/index.css b/src/index.css
deleted file mode 100644
index cfdb981..0000000
--- a/src/index.css
+++ /dev/null
@@ -1,43 +0,0 @@
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
-
-body {
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
-}
-
-.btn-primary {
- @apply bg-primary-600 hover:bg-primary-700 text-white font-medium py-2 px-4 rounded-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed;
-}
-
-.btn-secondary {
- @apply bg-gray-200 hover:bg-gray-300 text-gray-800 font-medium py-2 px-4 rounded-lg transition-colors duration-200;
-}
-
-.input-field {
- @apply w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500 outline-none transition-all;
-}
-
-.card {
- @apply bg-white rounded-xl shadow-md p-6;
-}
-
-.table-container {
- @apply overflow-x-auto rounded-lg border border-gray-200;
-}
-
-.table {
- @apply min-w-full divide-y divide-gray-200;
-}
-
-.table th {
- @apply px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider;
-}
-
-.table td {
- @apply px-6 py-4 whitespace-nowrap text-sm text-gray-900;
-}
-
-.table tr:hover {
- @apply bg-gray-50;
-}
diff --git a/src/main.jsx b/src/main.jsx
deleted file mode 100644
index 885679a..0000000
--- a/src/main.jsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import React from 'react'
-import ReactDOM from 'react-dom/client'
-import { BrowserRouter } from 'react-router-dom'
-import App from './App'
-import { StorageProvider } from './context/StorageContext'
-import './index.css'
-
-ReactDOM.createRoot(document.getElementById('root')).render(
-
-
-
-
-
-
-
-)
diff --git a/src/pages/Exam.jsx b/src/pages/Exam.jsx
deleted file mode 100644
index ee209c5..0000000
--- a/src/pages/Exam.jsx
+++ /dev/null
@@ -1,122 +0,0 @@
-import { useStorage } from '../context/StorageContext'
-
-export default function Exam() {
- const { data, updateProgram, updateExamRules, updateTrainee } = useStorage()
- const { program, trainees } = data
-
- const passed = trainees.filter(t => t.examScore !== null && t.examScore >= 9).length
- const totalWithScore = trainees.filter(t => t.examScore !== null).length
-
- const handleScoreChange = (traineeId, score) => {
- const numScore = parseInt(score) || null
- updateTrainee(traineeId, { examScore: numScore })
- }
-
- const getExamStatus = (score) => {
- if (score === null) return { text: 'Не оценён', color: 'text-gray-500' }
- if (score >= 9) return { text: 'Сдан', color: 'text-green-600' }
- return { text: 'Не сдан', color: 'text-red-600' }
- }
-
- return (
-
-
Итоговый экзамен
-
-
-
Настройки экзамена
-
-
- updateProgram({ examEnabled: e.target.checked })}
- disabled={!program.groupsFormed}
- className="w-4 h-4 text-primary-600 rounded"
- />
-
-
-
-
-
-
-
-
-
-
Статистика
-
-
-
{trainees.length}
-
Всего стажёров
-
-
-
{totalWithScore}
-
Сдано экзамен
-
-
-
{passed}
-
Прошли порог (≥9)
-
-
-
-
-
-
- )
-}
diff --git a/src/pages/Form.jsx b/src/pages/Form.jsx
deleted file mode 100644
index ae1a6b9..0000000
--- a/src/pages/Form.jsx
+++ /dev/null
@@ -1,261 +0,0 @@
-import { useState } from 'react'
-import { useNavigate } from 'react-router-dom'
-import { useStorage } from '../context/StorageContext'
-
-const initialForm = {
- firstName: '',
- lastName: '',
- email: '',
- division: '',
- position: '',
- phone: '',
- supervisorName: '',
- supervisorContact: '',
- city: ''
-}
-
-export default function Form() {
- const navigate = useNavigate()
- const { data, addTrainee, CITIES } = useStorage()
- const [formData, setFormData] = useState(initialForm)
- const [honeypot, setHoneypot] = useState('')
- const [submitted, setSubmitted] = useState(false)
- const [error, setError] = useState('')
-
- const isOpen = data.program.collectionOpen && !data.program.groupsFormed
-
- const handleChange = (e) => {
- const { name, value } = e.target
- setFormData(prev => ({ ...prev, [name]: value }))
- }
-
- const handleSubmit = (e) => {
- e.preventDefault()
-
- if (honeypot) {
- return
- }
-
- if (!isOpen) {
- setError('Приём анкет закрыт')
- return
- }
-
- try {
- addTrainee(formData)
- setSubmitted(true)
- setFormData(initialForm)
- } catch (err) {
- setError('Ошибка при сохранении анкеты')
- }
- }
-
- if (!isOpen) {
- return (
-
-
-
- 🔴 Приём анкет закрыт
-
-
- К сожалению, сбор анкет на данный момент не проводится.
-
-
-
-
- )
- }
-
- if (submitted) {
- return (
-
-
-
✅
-
- Анкета успешно отправлена!
-
-
- Данные сохранены. Тренер рассмотрит вашу заявку.
-
-
-
-
- )
- }
-
- return (
-
-
-
- Анкета стажёра
-
-
-
-
-
- )
-}
diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx
deleted file mode 100644
index 8920b6f..0000000
--- a/src/pages/Home.jsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import Hero from '../components/Hero'
-import TraineePath from '../components/TraineePath'
-import StatusCard from '../components/StatusCard'
-import InfoCards from '../components/InfoCards'
-import GroupsList from '../components/GroupsList'
-import { useStorage } from '../context/StorageContext'
-
-export default function Home() {
- const { data } = useStorage()
- const { program } = data
-
- const getCurrentStage = () => {
- if (program.examEnabled) return 4
- if (program.groupsFormed) return 3
- if (program.collectionOpen) return 1
- return 2
- }
-
- return (
-
- )
-}
diff --git a/src/pages/Progress.jsx b/src/pages/Progress.jsx
deleted file mode 100644
index 60a537b..0000000
--- a/src/pages/Progress.jsx
+++ /dev/null
@@ -1,142 +0,0 @@
-import { useState } from 'react'
-import { useStorage } from '../context/StorageContext'
-
-export default function Progress() {
- const { data, addSession, removeSession, updateTrainee } = useStorage()
- const [newSession, setNewSession] = useState({ name: '', date: '' })
-
- const handleAddSession = () => {
- if (newSession.name && newSession.date) {
- addSession(newSession)
- setNewSession({ name: '', date: '' })
- }
- }
-
- const handleAttendance = (traineeId, sessionId, present) => {
- const trainee = data.trainees.find(t => t.id === traineeId)
- const newAttendance = { ...(trainee?.attendance || {}), [sessionId]: present }
- updateTrainee(traineeId, { attendance: newAttendance })
- }
-
- const handleHomework = (traineeId, sessionId, status) => {
- const trainee = data.trainees.find(t => t.id === traineeId)
- const newHomework = { ...(trainee?.homeworkStatus || {}), [sessionId]: status }
- updateTrainee(traineeId, { homeworkStatus: newHomework })
- }
-
- return (
-
-
Прогресс
-
-
-
- {data.sessions.length > 0 && (
-
-
Список занятий
-
- {data.sessions.map(session => (
-
-
-
{session.name}
-
{new Date(session.date).toLocaleDateString('ru-RU')}
-
-
-
- ))}
-
-
-
-
- )}
-
- )
-}
diff --git a/src/pages/Trainer.jsx b/src/pages/Trainer.jsx
deleted file mode 100644
index 369b4ae..0000000
--- a/src/pages/Trainer.jsx
+++ /dev/null
@@ -1,325 +0,0 @@
-import { useState } from 'react'
-import { useStorage } from '../context/StorageContext'
-
-export default function Trainer() {
- const { user, login, data, updateProgram, updateTrainers, updateProgramRules, formGroups, resetGroups } = useStorage()
- const [activeTab, setActiveTab] = useState('settings')
- const [loginForm, setLoginForm] = useState({ login: '', password: '' })
- const [error, setError] = useState('')
-
- const handleLogin = (e) => {
- e.preventDefault()
- try {
- login(loginForm.login, loginForm.password)
- setError('')
- } catch (err) {
- setError(err.message)
- }
- }
-
- if (!user) {
- return (
-
-
-
- Вход для тренеров
-
-
-
-
- )
- }
-
- const tabs = [
- { id: 'settings', name: 'Настройки сбора' },
- { id: 'trainers', name: 'Тренеры' },
- { id: 'rules', name: 'Правила программы' },
- { id: 'trainees', name: 'Список стажёров' }
- ]
-
- return (
-
-
Кабинет тренера
-
-
- {tabs.map(tab => (
-
- ))}
-
-
- {activeTab === 'settings' && (
-
-
Настройки сбора
-
-
-
- updateProgram({ title: e.target.value })}
- className="input-field"
- />
-
-
-
-
-
-
-
-
-
-
- updateProgram({ numberOfGroups: parseInt(e.target.value) || 1 })}
- className="input-field w-32"
- />
-
-
-
- updateProgram({ collectionOpen: e.target.checked })}
- disabled={data.program.groupsFormed}
- className="w-4 h-4 text-primary-600 rounded"
- />
-
-
-
-
- {!data.program.groupsFormed ? (
-
- ) : (
-
- )}
-
-
- )}
-
- {activeTab === 'trainers' && (
-
- )}
-
- {activeTab === 'rules' && (
-
-
Правила программы
-
- )}
-
- {activeTab === 'trainees' && (
-
-
- Список стажёров ({data.trainees.length})
-
-
-
-
-
- | ФИО |
- Почта |
- Подразделение |
- Должность |
- Город |
- Группа |
- Действия |
-
-
-
- {data.trainees.map(trainee => (
-
- | {trainee.lastName} {trainee.firstName} |
- {trainee.email} |
- {trainee.division} |
- {trainee.position} |
- {trainee.city} |
- {trainee.groupId ? `Группа ${trainee.groupId}` : '-'} |
-
-
- |
-
- ))}
- {data.trainees.length === 0 && (
-
- |
- Анкет пока нет
- |
-
- )}
-
-
-
-
- )}
-
- )
-}
diff --git a/src/pages/WhatsApp.jsx b/src/pages/WhatsApp.jsx
deleted file mode 100644
index 8410bdb..0000000
--- a/src/pages/WhatsApp.jsx
+++ /dev/null
@@ -1,103 +0,0 @@
-import { useState } from 'react'
-import { useStorage } from '../context/StorageContext'
-
-export default function WhatsApp() {
- const { data, updateProgram } = useStorage()
- const [template, setTemplate] = useState(
- 'Здравствуйте, {name}! Вы зачислены в группу {group} программы "{title}". Ссылка на чат: https://chat.whatsapp.com/...'
- )
-
- const generateMessage = (trainee) => {
- return template
- .replace(/{name}/g, `${trainee.lastName} ${trainee.firstName}`)
- .replace(/{group}/g, trainee.groupId ? `Группа ${trainee.groupId}` : 'Не распределён')
- .replace(/{title}/g, data.program.title)
- }
-
- const openWhatsApp = (trainee) => {
- const message = generateMessage(trainee)
- const phone = trainee.phone.replace(/\D/g, '')
- const url = `https://wa.me/${phone}?text=${encodeURIComponent(message)}`
- window.open(url, '_blank')
- }
-
- if (!data.program.groupsFormed) {
- return (
-
-
-
- ⚠️ Группы ещё не сформированы
-
-
- Сначала сформируйте группы в кабинете тренера (вкладка "Настройки сбора").
-
-
-
- )
- }
-
- return (
-
-
Рассылка WhatsApp
-
-
-
Шаблон сообщения
-
- Доступные подстановки: {'{name}'}, {'{group}'}, {'{title}'}
-
-
-
-
-
- Стажёры ({data.trainees.length})
-
-
-
-
-
- | ФИО |
- Группа |
- Телефон |
- Предпросмотр |
- Действие |
-
-
-
- {data.trainees.map(trainee => (
-
- | {trainee.lastName} {trainee.firstName} |
- {trainee.groupId ? `Группа ${trainee.groupId}` : '-'} |
- {trainee.phone} |
-
- {generateMessage(trainee).slice(0, 50)}...
- |
-
-
- |
-
- ))}
- {data.trainees.length === 0 && (
-
- |
- Стажёров пока нет
- |
-
- )}
-
-
-
-
-
- )
-}
diff --git a/tailwind.config.js b/tailwind.config.js
deleted file mode 100644
index ad3f295..0000000
--- a/tailwind.config.js
+++ /dev/null
@@ -1,26 +0,0 @@
-/** @type {import('tailwindcss').Config} */
-export default {
- content: [
- "./index.html",
- "./src/**/*.{js,ts,jsx,tsx}",
- ],
- theme: {
- extend: {
- colors: {
- primary: {
- 50: '#e6f0ff',
- 100: '#cce0ff',
- 200: '#99c2ff',
- 300: '#66a3ff',
- 400: '#3385ff',
- 500: '#0066ff',
- 600: '#0052cc',
- 700: '#003d99',
- 800: '#002966',
- 900: '#001433',
- },
- },
- },
- },
- plugins: [],
-}
diff --git a/vite.config.js b/vite.config.js
deleted file mode 100644
index 7eb2b54..0000000
--- a/vite.config.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import { defineConfig } from 'vite'
-import react from '@vitejs/plugin-react'
-
-export default defineConfig({
- plugins: [react()],
- server: {
- port: 3000,
- host: true
- },
- base: './'
-})