refactoring

This commit is contained in:
2025-07-26 11:10:28 +04:00
parent 1ee9b235d5
commit a1a9e0dc93
50 changed files with 6197 additions and 2933 deletions

View File

@ -0,0 +1,44 @@
"use client";
import { FC, useEffect, useState } from "react";
import { redirect } from "next/navigation";
import { useSelector } from "react-redux";
import { Button, Text } from "@mantine/core";
import SERVICES from "@/constants/services";
import { ServiceCode } from "@/enums/ServiceCode";
import { RootState } from "@/lib/store";
import ServiceData from "@/types/ServiceData";
const ConsentButton: FC = () => {
const serviceCode = useSelector(
(state: RootState) => state.targetService.serviceCode
);
const [serviceData, setServiceData] = useState<ServiceData>();
useEffect(() => {
if (serviceCode === ServiceCode.UNDEFINED) {
redirect("services");
}
setServiceData(SERVICES[serviceCode]);
}, [serviceCode]);
const confirmAccess = () => {};
return (
<>
<Button
onClick={() => confirmAccess()}
variant={"filled"}>
Войти
</Button>
<Text
fz={"h4"}
ta="center">
Сервис {serviceData?.name} получит{" "}
{serviceData?.requiredAccesses}
</Text>
</>
);
};
export default ConsentButton;

View File

@ -0,0 +1,14 @@
.container {
@media (min-width: 48em) {
max-width: 400px;
}
}
.gray-text {
@mixin dark {
color: #807e7e;
}
@mixin light {
color: gray;
}
}

View File

@ -0,0 +1,22 @@
import { FC } from "react";
import { Stack, Text } from "@mantine/core";
import ConfirmAccessButton from "@/app/consent/components/ConsentButton/ConsentButton";
import styles from "./ConsentForm.module.css";
const ConsentForm: FC = () => {
return (
<Stack
align={"center"}
className={styles.container}>
<ConfirmAccessButton />
<Text
className={styles["gray-text"]}
ta="center">
Данные из LogiDex ID передаются в другой сервис и обрабатываются
в соответствии с правилами этого сервиса
</Text>
</Stack>
);
};
export default ConsentForm;

16
src/app/consent/page.tsx Normal file
View File

@ -0,0 +1,16 @@
import PageContainer from "@/components/PageContainer/PageContainer";
import PageItem from "@/components/PageBlock/PageItem";
import Logo from "@/components/Logo/Logo";
import ConsentForm from "@/app/consent/components/ConsentForm/ConsentForm";
export default function ConfirmAccessPage() {
return (
<PageContainer center>
<PageItem fullScreenMobile>
<Logo title={"Вход с помощью LogiDex ID"} />
<ConsentForm />
</PageItem>
</PageContainer>
)
}

View File

@ -0,0 +1,15 @@
import LoginForm from "@/components/LoginForm/LoginForm";
import Logo from "@/components/Logo/Logo";
import PageItem from "@/components/PageBlock/PageItem";
import PageContainer from "@/components/PageContainer/PageContainer";
export default function CreateIdPage() {
return (
<PageContainer center>
<PageItem fullScreenMobile>
<Logo title={"Создание аккаунта"} />
<LoginForm isCreatingId />
</PageItem>
</PageContainer>
);
}

5
src/app/global.css Normal file
View File

@ -0,0 +1,5 @@
body {
@mixin light {
background-color: whitesmoke;
}
}

57
src/app/layout.tsx Normal file
View File

@ -0,0 +1,57 @@
import "@mantine/core/styles.css";
import React, { ReactNode } from "react";
import {
ColorSchemeScript,
mantineHtmlProps,
MantineProvider,
} from "@mantine/core";
import Header from "@/components/Header/Header";
import { theme } from "@/theme";
import "@/app/global.css";
import Footer from "@/components/Footer/Footer";
import ReduxProvider from "@/providers/ReduxProvider";
export const metadata = {
title: "LogiDex ID",
description: "LogiDex ID",
};
type Props = {
children: ReactNode;
};
export default function RootLayout({ children }: Props) {
return (
<html
lang="ru"
{...mantineHtmlProps}>
<head>
<ColorSchemeScript defaultColorScheme={"auto"} />
<link
rel="shortcut icon"
href="/favicon.svg"
/>
<link
rel="stylesheet"
href="global.css"
/>
<meta
name="viewport"
content="minimum-scale=1, initial-scale=1, width=device-width, user-scalable=no"
/>
<title />
</head>
<body>
<MantineProvider
theme={theme}
defaultColorScheme={"auto"}>
<ReduxProvider>
<Header />
{children}
<Footer />
</ReduxProvider>
</MantineProvider>
</body>
</html>
);
}

15
src/app/page.tsx Normal file
View File

@ -0,0 +1,15 @@
import LoginForm from "@/components/LoginForm/LoginForm";
import Logo from "@/components/Logo/Logo";
import PageItem from "@/components/PageBlock/PageItem";
import PageContainer from "@/components/PageContainer/PageContainer";
export default function MainPage() {
return (
<PageContainer center>
<PageItem fullScreenMobile>
<Logo title={"Вход"} />
<LoginForm />
</PageItem>
</PageContainer>
);
}

View File

@ -0,0 +1,7 @@
.container {
width: 400px;
@media (max-width: 48em) {
width: 100%;
}
}

View File

@ -0,0 +1,66 @@
"use client";
import { useMemo } from "react";
import { redirect } from "next/navigation";
import { Button, Stack, Title } from "@mantine/core";
import styles from "@/app/services/components/ServicesList/ServicesList.module.css";
import TitleWithLines from "@/components/TitleWithLines/TitleWithLines";
import SERVICES from "@/constants/services";
import { ServiceCode } from "@/enums/ServiceCode";
import { setTargetService } from "@/lib/features/targetService/targetServiceSlice";
import { useAppDispatch } from "@/lib/store";
import ServiceData from "@/types/ServiceData";
const ServicesList = () => {
const dispatch = useAppDispatch();
const services = useMemo(
() =>
Object.entries(SERVICES)
.filter(([key]) => key !== ServiceCode.UNDEFINED)
.map(([, value]) => value),
[SERVICES]
);
const onServiceClick = (service: ServiceData) => {
dispatch(setTargetService(service.code));
redirect("consent");
};
const getServiceButton = (service: ServiceData, key: number) => {
return (
<Button
key={key}
size={"xl"}
onClick={() => onServiceClick(service)}>
<Stack gap={0}>
<Title order={4}>{service.name}</Title>
</Stack>
</Button>
);
};
const getServiceInDevelopment = (title: string) => {
return (
<Button
size={"xl"}
onClick={() => {}}
disabled>
<Stack gap={0}>
<Title order={4}>{title}</Title>
</Stack>
</Button>
);
};
return (
<Stack
className={styles.container}
gap={"lg"}>
{services.map((service, i) => getServiceButton(service, i))}
<TitleWithLines title="Скоро будет" />
{getServiceInDevelopment("Analytics")}
</Stack>
);
};
export default ServicesList;

15
src/app/services/page.tsx Normal file
View File

@ -0,0 +1,15 @@
import Logo from "@/components/Logo/Logo";
import PageItem from "@/components/PageBlock/PageItem";
import PageContainer from "@/components/PageContainer/PageContainer";
import ServicesList from "@/app/services/components/ServicesList/ServicesList";
export default function ServicesPage() {
return (
<PageContainer center>
<PageItem fullScreenMobile>
<Logo title={"Сервисы LogiDex"} />
<ServicesList />
</PageItem>
</PageContainer>
);
}

View File

@ -0,0 +1,59 @@
"use client";
import { FC, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Button } from "@mantine/core";
import { setLastSendTime } from "@/lib/features/verification/verificationSlice";
import { RootState } from "@/lib/store";
import { MAX_COUNTDOWN } from "@/constants/verification";
const ResendVerificationCode: FC = () => {
const [countdown, setCountdown] = useState(0);
const dispatch = useDispatch();
const lastSendTime = useSelector(
(state: RootState) => state.verification.lastSendTime
);
useEffect(() => {
dispatch(setLastSendTime(Date.now()));
setCountdown(MAX_COUNTDOWN);
}, []);
useEffect(() => {
if (lastSendTime) {
const elapsed = Math.floor((Date.now() - lastSendTime) / 1000);
const remaining = Math.max(0, MAX_COUNTDOWN - elapsed);
setCountdown(remaining);
}
}, [lastSendTime]);
useEffect(() => {
if (countdown > 0) {
const timer = setTimeout(() => setCountdown(countdown - 1), 1000);
return () => clearTimeout(timer);
}
}, [countdown]);
const sendCode = () => {
if (countdown > 0) return;
};
const handleResend = () => {
sendCode();
dispatch(setLastSendTime(Date.now()));
setCountdown(MAX_COUNTDOWN);
};
return (
<Button
variant={"outline"}
disabled={countdown > 0}
onClick={handleResend}>
{countdown > 0
? `Отправить код через ${countdown}с`
: "Отправить код"}
</Button>
);
};
export default ResendVerificationCode;

View File

@ -0,0 +1,11 @@
.pin-input-root {
width: 100%;
justify-content: space-between;
margin-top: 10px;
margin-bottom: 15px;
}
.pin-input {
font-size: 17px;
border-radius: 10px;
}

View File

@ -0,0 +1,63 @@
"use client";
import { FC } from "react";
import { redirect } from "next/navigation";
import { Button, PinInput, Stack } from "@mantine/core";
import { useForm } from "@mantine/form";
import ResendVerificationCode from "@/app/verify-phone/components/ResendVerificationCode/ResendVerificationCode";
import style from "@/app/verify-phone/components/VerifyPhoneForm/VerifyPhone.module.css";
type VerifyNumberForm = {
code: string;
};
const VerifyPhoneForm: FC = () => {
const form = useForm<VerifyNumberForm>({
initialValues: {
code: "",
},
validate: {
code: code => code.length !== 6 && "Введите весь код",
},
});
const handleSubmit = (values: VerifyNumberForm) => {
console.log(values);
redirect("/services");
};
const navigateToLogin = () => redirect("/");
return (
<form onSubmit={form.onSubmit(handleSubmit)}>
<Stack>
<PinInput
length={6}
placeholder="_"
oneTimeCode
size={"md"}
classNames={{
root: style["pin-input-root"],
input: style["pin-input"],
}}
{...form.getInputProps("code")}
/>
<Button
variant={"filled"}
type={"submit"}
disabled={form.values.code.length !== 6}>
Подтвердить
</Button>
<ResendVerificationCode />
<Button
variant={"outline"}
onClick={navigateToLogin}>
Назад
</Button>
</Stack>
</form>
);
};
export default VerifyPhoneForm;

View File

@ -0,0 +1,15 @@
import Logo from "@/components/Logo/Logo";
import PageItem from "@/components/PageBlock/PageItem";
import PageContainer from "@/components/PageContainer/PageContainer";
import VerifyPhoneForm from "@/app/verify-phone/components/VerifyPhoneForm/VerifyPhoneForm";
export default function CreateIdPage() {
return (
<PageContainer center>
<PageItem fullScreenMobile>
<Logo title={"Введите код, отправленный на номер"} />
<VerifyPhoneForm />
</PageItem>
</PageContainer>
);
}