Commit cb76da5b by asranov0003

feat: add pincode

parent cebef8e7
......@@ -19,6 +19,11 @@
"terms": "terms of service",
"privacyPolicy": "privacy policy"
},
"pincode": {
"enterTitle": "Enter your PIN Code",
"createTitle": "Create your PIN Code",
"confirmTitle": "Confirm your PIN Code"
},
"button": {
"login": "Login",
"register": "Register",
......
......@@ -19,6 +19,11 @@
"terms": "условия использования",
"privacyPolicy": "политику конфиденциальности"
},
"pincode": {
"enterTitle": "Введите PIN-код",
"createTitle": "Создайте PIN-код",
"confirmTitle": "Подтвердите PIN-код"
},
"button": {
"login": "Войти",
"register": "Зарегистрироваться",
......
.pincode {
height: 100vh;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.pincode__title {
text-align: center;
margin-bottom: 2rem;
}
.pincode__circles {
display: flex;
justify-content: center;
gap: 1rem;
margin-bottom: 2rem;
}
.pincode__circle {
width: 16px;
height: 16px;
border-radius: 50%;
background-color: lightgray;
transition: background-color 0.2s;
}
.pincode__circle.filled {
background-color: var(--primary-color);
}
.pincode__circle.error {
background-color: var(--danger-color);
}
.pincode__keyboard__row {
display: flex;
justify-content: space-between;
gap: 1rem;
margin: 1rem 0;
}
.pincode__keyboard__key {
width: 100%;
height: 100px;
padding: 1rem;
font-size: 1.5rem;
text-align: center;
cursor: pointer;
border-radius: 10px;
border: none;
}
\ No newline at end of file
import React, { useState, useEffect } from "react";
import "./Pincode.css";
import { FiDelete } from "react-icons/fi";
import { useNavigate } from "react-router-dom";
import { TG_USER_ID } from "../../constants/constants";
import { useTranslation } from "react-i18next";
const Pincode: React.FC = () => {
const [pin, setPin] = useState("");
const [step, setStep] = useState<"create" | "confirm" | "enter">("create");
const [firstPin, setFirstPin] = useState<string | null>(null);
const [error, setError] = useState(false);
const navigate = useNavigate();
const { t } = useTranslation();
useEffect(() => {
const storedPin = localStorage.getItem(`pincode-${TG_USER_ID}`);
if (storedPin) {
setStep("enter");
}
}, []);
const handleDigit = (digit: string) => {
if (pin.length < 4) setPin(pin + digit);
};
const handleDelete = () => {
setPin(pin.slice(0, -1));
};
useEffect(() => {
if (pin.length === 4) {
if (step === "enter") {
const storedPin = localStorage.getItem(`pincode-${TG_USER_ID}`);
if (pin === storedPin) {
sessionStorage.setItem(`pincode-${TG_USER_ID}`, pin);
navigate("/home");
} else {
setError(true);
setTimeout(() => {
setPin("");
setError(false);
}, 1000);
}
return;
}
if (!firstPin) {
setFirstPin(pin);
setPin("");
setStep("confirm");
} else {
if (firstPin === pin) {
localStorage.setItem(`pincode-${TG_USER_ID}`, pin);
sessionStorage.setItem(`pincode-${TG_USER_ID}`, pin);
navigate("/home");
} else {
setError(true);
setTimeout(() => {
setPin("");
setFirstPin(null);
setStep("create");
setError(false);
}, 1000);
}
}
}
}, [pin, step, firstPin, navigate]);
return (
<div className="pincode wrapper">
<div>
<h2 className="pincode__title">
{
{
create: t("pincode.createTitle"),
confirm: t("pincode.confirmTitle"),
enter: t("pincode.enterTitle"),
}[step]
}
</h2>
<div className="pincode__circles">
{[0, 1, 2, 3].map((i) => (
<span
key={i}
className={`pincode__circle ${
i < pin.length ? (error ? "error" : "filled") : ""
}`}
/>
))}
</div>
</div>
<div className="pincode__keyboards">
{[
["1", "2", "3"],
["4", "5", "6"],
["7", "8", "9"],
["", "0", "del"],
].map((row, idx) => (
<div className="pincode__keyboard__row" key={idx}>
{row.map((val) =>
val === "del" ? (
<button
key="del"
className="pincode__keyboard__key"
onClick={handleDelete}
>
<FiDelete />
</button>
) : val === "" ? (
<button
key="empty"
className="pincode__keyboard__key"
style={{ opacity: 0, pointerEvents: "none" }}
>
.
</button>
) : (
<button
key={val}
className="pincode__keyboard__key"
onClick={() => handleDigit(val)}
>
{val}
</button>
)
)}
</div>
))}
</div>
</div>
);
};
export default Pincode;
export { default } from "./Pincode";
import { type JSX } from "react";
import { Navigate } from "react-router-dom";
import { useAuth } from "../contexts/AuthContext/AuthContext";
const AuthGuardReverse = ({ component }: { component: JSX.Element }) => {
const { isAuthenticated } = useAuth();
return isAuthenticated ? <Navigate to="/home" replace /> : component;
};
export default AuthGuardReverse;
import React from "react";
import { Navigate } from "react-router-dom";
import { useAuth } from "../contexts/AuthContext/AuthContext";
const AuthRedirect: React.FC = () => {
const { isAuthenticated } = useAuth();
return <Navigate to={isAuthenticated ? "/home" : "/auth/login"} replace />;
};
export default AuthRedirect;
import React from "react";
import { Navigate } from "react-router-dom";
import { useAuth } from "../contexts/AuthContext/AuthContext";
import { TG_USER_ID } from "../constants/constants";
interface ProtectedRouteProps {
element: React.ReactElement;
......@@ -11,9 +11,14 @@ const ProtectedRoute: React.FC<ProtectedRouteProps> = ({
element,
redirectTo,
}) => {
const { isAuthenticated } = useAuth();
const isAuthenticated = !!localStorage.getItem(`token-${TG_USER_ID}`);
const pincode = !!sessionStorage.getItem(`pincode-${TG_USER_ID}`);
return isAuthenticated ? element : <Navigate to={redirectTo} replace />;
if (isAuthenticated && !pincode) {
return <Navigate to="/pincode" />;
}
return isAuthenticated ? element : <Navigate to={redirectTo} />;
};
export default ProtectedRoute;
import React from "react";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import {
createBrowserRouter,
Navigate,
RouterProvider,
} from "react-router-dom";
import NotFound from "../pages/NotFound";
import Login from "../pages/Auth/Login";
import Register from "../pages/Auth/Register";
......@@ -10,59 +14,64 @@ import Home from "../pages/Home";
import Notifications from "../pages/Notifications";
import Subscription from "../pages/Subscription";
import Settings from "../pages/Settings";
import AuthRedirect from "./AuthRedirect";
import AuthGuardReverse from "./AuthGuardReverse";
import Pincode from "../pages/Pincode";
import { TG_USER_ID } from "../constants/constants";
const router = createBrowserRouter([
{
path: "/",
element: <AuthRedirect />,
},
{
path: "/auth",
children: [
{
path: "login",
element: <AuthGuardReverse component={<Login />} />,
},
{
path: "register",
element: <AuthGuardReverse component={<Register />} />,
},
{
path: "recover",
element: <AuthGuardReverse component={<Recover />} />,
},
],
},
{
path: "/",
element: <ProtectedRoute element={<Layout />} redirectTo="/auth/login" />,
children: [
{
path: "home",
element: <Home />,
},
{
path: "notifications",
element: <Notifications />,
},
{
path: "subscription",
element: <Subscription />,
},
{
path: "settings",
element: <Settings />,
},
],
},
{
path: "*",
element: <NotFound />,
},
]);
const Router: React.FC = () => {
const isAuthenticated = !!localStorage.getItem(`token-${TG_USER_ID}`);
const Router: React.FC = () => <RouterProvider router={router} />;
const router = createBrowserRouter([
{
path: "/",
element: isAuthenticated && <Navigate to={"/home"} />,
children: [
{
path: "/auth/login",
element: <Login />,
},
{
path: "/auth/register",
element: <Register />,
},
{
path: "/auth/recover",
element: <Recover />,
},
],
},
{
path: "/",
element: <ProtectedRoute element={<Layout />} redirectTo="/" />,
children: [
{
path: "home",
element: <Home />,
},
{
path: "notifications",
element: <Notifications />,
},
{
path: "subscription",
element: <Subscription />,
},
{
path: "settings",
element: <Settings />,
},
],
},
{
path: "/pincode",
element: <Pincode />,
},
{
path: "*",
element: <NotFound />,
},
]);
return <RouterProvider router={router} />;
};
export default Router;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment