import React from 'react'; import type { ReactElement } from 'react'; import Head from 'next/head'; import Link from 'next/link'; import { Field, Form, Formik } from 'formik'; import { mdiArrowLeft, mdiEye, mdiEyeOff, mdiInformation, mdiMapMarker, mdiShieldAccount } from '@mdi/js'; import { toast, ToastContainer } from 'react-toastify'; import BaseButton from '../components/BaseButton'; import BaseButtons from '../components/BaseButtons'; import BaseDivider from '../components/BaseDivider'; import BaseIcon from '../components/BaseIcon'; import CardBox from '../components/CardBox'; import FormCheckRadio from '../components/FormCheckRadio'; import FormField from '../components/FormField'; import LayoutGuest from '../layouts/Guest'; import { getPageTitle } from '../config'; import { findMe, loginUser, resetAction } from '../stores/authSlice'; import { useAppDispatch, useAppSelector } from '../stores/hooks'; import { useRouter } from 'next/router'; type JourneyContext = { roleId: string; roleTitle: string; city: string; contractor: string; }; const journeyStorageKey = 'roadTalentJourney'; const roleLabels: Record = { school: 'Школа', college: 'СПО и ВО', contractor: 'Подрядные организации', government: 'Государственные институты', }; export default function Login() { const router = useRouter(); const dispatch = useAppDispatch(); const textColor = useAppSelector((state) => state.style.linkColor); const iconsColor = useAppSelector((state) => state.style.iconsColor); const notify = (type: 'success' | 'error', msg: string) => toast(msg, { type }); const [showPassword, setShowPassword] = React.useState(false); const [journeyContext, setJourneyContext] = React.useState(null); const { currentUser, isFetching, errorMessage, token, notify: notifyState } = useAppSelector((state) => state.auth); const [initialValues, setInitialValues] = React.useState({ email: 'admin@flatlogic.com', password: '26ac88a4', remember: true, }); React.useEffect(() => { if (token) { dispatch(findMe()); } }, [token, dispatch]); React.useEffect(() => { if (currentUser?.id) { router.push('/dashboard'); } }, [currentUser?.id, router]); React.useEffect(() => { if (errorMessage) { notify('error', errorMessage); } }, [errorMessage]); React.useEffect(() => { if (notifyState?.showNotification) { notify('success', notifyState?.textNotification); dispatch(resetAction()); } }, [notifyState?.showNotification, notifyState?.textNotification, dispatch]); React.useEffect(() => { if (!router.isReady) { return; } const roleFromQuery = typeof router.query.role === 'string' ? router.query.role : ''; const cityFromQuery = typeof router.query.city === 'string' ? router.query.city : ''; const contractorFromQuery = typeof router.query.contractor === 'string' ? router.query.contractor : ''; if (roleFromQuery || cityFromQuery || contractorFromQuery) { const context = { roleId: roleFromQuery, roleTitle: roleLabels[roleFromQuery] || '', city: cityFromQuery, contractor: contractorFromQuery, }; setJourneyContext(context); localStorage.setItem(journeyStorageKey, JSON.stringify(context)); return; } const storedJourney = localStorage.getItem(journeyStorageKey); if (!storedJourney) { setJourneyContext(null); return; } try { const parsedJourney = JSON.parse(storedJourney) as JourneyContext; setJourneyContext(parsedJourney); } catch (error) { console.error('Failed to parse road talent journey from storage', error); setJourneyContext(null); } }, [router.isReady, router.query.city, router.query.contractor, router.query.role]); const handleSubmit = async (value: { email: string; password: string; remember: boolean }) => { const { remember, ...rest } = value; await dispatch(loginUser(rest)); }; const setLogin = (target: HTMLElement) => { setInitialValues((prev) => ({ ...prev, email: target.innerText.trim(), password: target.dataset.password ?? '', })); }; const title = 'Кадровый суверенитет Дороги'; return ( <> {getPageTitle('Login')}

вход в систему

{title}

Назад к карте

контекст маршрута

Путь пользователя сохраняется до входа

Мы переносим выбранные на лендинге город, подрядчика и роль в экран авторизации, чтобы вход ощущался частью цельного сценария, а не отдельной формой.

{journeyContext ? (

Выбранный маршрут

{journeyContext.roleTitle || 'Роль не выбрана'}

Город

{journeyContext.city || 'Не выбран'}

Подрядчик

{journeyContext.contractor || 'Не выбран'}

После авторизации этот контекст появится на обзорной странице и поможет быстро перейти к справочникам городов и подрядчиков в админ-интерфейсе.

) : (

Маршрут ещё не выбран

Вернитесь на карту России, чтобы выбрать город и роль. Вход доступен и без этого шага, но сценарий будет менее полным.

)}

Демо-доступ для MVP

Для тестирования используйте стандартные аккаунты. Выбор роли на лендинге — это контекст пользовательского входа, а не замена существующей системной авторизации.

Демо-учётные записи

Быстрый вход

Подставьте готовую учётную запись кликом по email или введите собственные данные.

Администратор

setLogin(event.target as HTMLElement)} > admin@flatlogic.com {' '} / 26ac88a4

Пользователь

setLogin(event.target as HTMLElement)} > client@hello.com {' '} / 779ee45a8363

handleSubmit(values)}>
Забыли пароль?

Нет аккаунта?{' '} Создать новый

© 2026 {title}. Все права защищены.

Privacy Policy Вернуться на интерактивную карту
); } Login.getLayout = function getLayout(page: ReactElement) { return {page}; };