Compare commits

...

5 Commits

48 changed files with 152 additions and 159 deletions

1
.env.example Normal file
View File

@ -0,0 +1 @@
NEXT_PUBLIC_API_URL=http://your.api/api

2
.gitignore vendored
View File

@ -122,7 +122,7 @@ dist
.vscode-test
# yarn v2
.yarn
.DS_Store
.idea

Binary file not shown.

6
build_docker.sh Normal file → Executable file
View File

@ -1,4 +1,2 @@
sudo docker build -t git.logidex.ru/aserbin/logidex-id-frontend:latest .
sudo docker images git.logidex.ru/aserbin/logidex-id-frontend:latest
sudo docker login git.logidex.ru
sudo docker push git.logidex.ru/aserbin/logidex-id-frontend:latest
docker build -t git.logidex.ru/fakz9/logidex-id-frontend:latest .
docker push git.logidex.ru/fakz9/logidex-id-frontend:latest

View File

@ -8,7 +8,7 @@ import { Auth } from "@/client";
import SCOPES from "@/constants/scopes";
import { Scopes } from "@/enums/Scopes";
import { notifications } from "@/lib/notifications";
import { RootState } from "@/lib/store";
import { RootState } from "@/lib/store/store";
const ConsentButton: FC = () => {
const searchParams = useSearchParams();
@ -27,17 +27,7 @@ const ConsentButton: FC = () => {
};
const requestConsent = () => {
// if (!auth.loginChallenge || auth.scope.length === 0) return;
// new AuthService()
// .requestConsent(auth.loginChallenge)
// .then(response => response.data)
// .then(({ clientName }) => {
// setClientName(clientName);
// })
// .catch(error => {
// console.error(error);
// notifications.error({ message: error.toString() });
// });
};
useEffect(() => {
@ -46,13 +36,21 @@ const ConsentButton: FC = () => {
}, []);
const confirmAccess = () => {
const phoneNumber = auth.phoneNumber;
if (!phoneNumber) {
console.error("Phone number is not set");
return;
}
const consentChallenge = searchParams.get("consent_challenge");
if (!consentChallenge) {
console.error("Consent challenge is missing in the URL");
return;
}
Auth.postAuthConsentAccept({
body: { consent_challenge: consentChallenge },
body: {
consent_challenge: consentChallenge,
phone_number: phoneNumber,
},
})
.then(response => response.data)
.then(response => {
@ -69,17 +67,7 @@ const ConsentButton: FC = () => {
console.error("Redirect URL is missing in the response");
}
});
// if (!auth.loginChallenge) return;
// new AuthService()
// .approveConsent(auth.loginChallenge)
// .then(response => response.data)
// .then(({ redirectUrl }) => {
// window.location.href = redirectUrl;
// })
// .catch(error => {
// console.error(error);
// notifications.error({ message: error.toString() });
// });
};
return (

View File

@ -1,6 +1,6 @@
import PageContainer from "@/components/PageContainer/PageContainer";
import PageItem from "@/components/PageBlock/PageItem";
import Logo from "@/components/Logo/Logo";
import PageContainer from "@/components/layout/PageContainer/PageContainer";
import PageItem from "@/components/layout/PageBlock/PageItem";
import Logo from "@/components/ui/Logo/Logo";
import ConsentForm from "@/app/consent/components/ConsentForm/ConsentForm";

View File

@ -1,7 +1,7 @@
import LoginForm from "@/components/LoginForm/LoginForm";
import Logo from "@/components/Logo/Logo";
import PageItem from "@/components/PageBlock/PageItem";
import PageContainer from "@/components/PageContainer/PageContainer";
import LoginForm from "@/components/features/LoginForm/LoginForm";
import Logo from "@/components/ui/Logo/Logo";
import PageItem from "@/components/layout/PageBlock/PageItem";
import PageContainer from "@/components/layout/PageContainer/PageContainer";
export default function CreateIdPage() {
return (

View File

@ -6,11 +6,11 @@ import {
mantineHtmlProps,
MantineProvider,
} from "@mantine/core";
import Header from "@/components/Header/Header";
import Header from "@/components/layout/Header/Header";
import { theme } from "@/theme";
import "@/app/global.css";
import { Notifications } from "@mantine/notifications";
import Footer from "@/components/Footer/Footer";
import Footer from "@/components/layout/Footer/Footer";
import ReduxProvider from "@/providers/ReduxProvider";
export const metadata = {

View File

@ -1,18 +1,20 @@
import LoginForm from "@/components/LoginForm/LoginForm";
import Logo from "@/components/Logo/Logo";
import PageItem from "@/components/PageBlock/PageItem";
import PageContainer from "@/components/PageContainer/PageContainer";
import LoginForm from "@/components/features/LoginForm/LoginForm";
import PageItem from "@/components/layout/PageBlock/PageItem";
import PageContainer from "@/components/layout/PageContainer/PageContainer";
import Logo from "@/components/ui/Logo/Logo";
interface LoginPageProps {
searchParams: { login_challenge?: string };
searchParams: Promise<{ login_challenge?: string }>;
}
export default function LoginPage({ searchParams }: LoginPageProps) {
export default async function LoginPage({ searchParams }: LoginPageProps) {
const params = await searchParams;
return (
<PageContainer center>
<PageItem fullScreenMobile>
<Logo title={"Вход"} />
<LoginForm loginChallenge={searchParams.login_challenge} />
<LoginForm loginChallenge={params.login_challenge} />
</PageItem>
</PageContainer>
);

View File

@ -4,13 +4,17 @@ 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 TitleWithLines from "@/components/ui/TitleWithLines/TitleWithLines";
import SCOPES from "@/constants/scopes";
import { Scopes } from "@/enums/Scopes";
import { setTargetService } from "@/lib/features/targetService/targetServiceSlice";
import { useAppDispatch } from "@/lib/store";
import ServiceData from "@/types/ServiceData";
import { setTargetService } from "@/lib/store/features/targetService/targetServiceSlice";
import { useAppDispatch } from "@/lib/store/store";
type ServiceData = {
id: number;
code: Scopes;
name: string;
};
const ServicesList = () => {
const dispatch = useAppDispatch();
const services = useMemo(
@ -56,7 +60,9 @@ const ServicesList = () => {
<Stack
className={styles.container}
gap={"lg"}>
{services.map((service, i) => getServiceButton(service, i))}
{services.map((service, i) =>
getServiceButton(service as unknown as ServiceData, i)
)}
<TitleWithLines title="Скоро будет" />
{getServiceInDevelopment("Analytics")}
</Stack>

View File

@ -1,6 +1,6 @@
import Logo from "@/components/Logo/Logo";
import PageItem from "@/components/PageBlock/PageItem";
import PageContainer from "@/components/PageContainer/PageContainer";
import Logo from "@/components/ui/Logo/Logo";
import PageItem from "@/components/layout/PageBlock/PageItem";
import PageContainer from "@/components/layout/PageContainer/PageContainer";
import ServicesList from "@/app/services/components/ServicesList/ServicesList";
export default function ServicesPage() {

View File

@ -3,9 +3,9 @@
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";
import { setLastSendTime } from "@/lib/store/features/verification/verificationSlice";
import { RootState } from "@/lib/store/store";
const ResendVerificationCode: FC = () => {
const [countdown, setCountdown] = useState(0);

View File

@ -9,7 +9,7 @@ import ResendVerificationCode from "@/app/verify-phone/components/ResendVerifica
import style from "@/app/verify-phone/components/VerifyPhoneForm/VerifyPhone.module.css";
import { Auth } from "@/client";
import { notifications } from "@/lib/notifications";
import { RootState } from "@/lib/store";
import { RootState } from "@/lib/store/store";
type VerifyNumberForm = {
code: string;
@ -33,7 +33,7 @@ const VerifyPhoneForm: FC = () => {
Auth.postAuthOtpVerify({
body: {
phone_number: authState.phoneNumber.replace(" ", ""),
phone_number: authState.phoneNumber,
login_challenge: authState.loginChallenge,
otp: values.code,
},

View File

@ -1,7 +1,7 @@
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";
import PageItem from "@/components/layout/PageBlock/PageItem";
import PageContainer from "@/components/layout/PageContainer/PageContainer";
import Logo from "@/components/ui/Logo/Logo";
export default function CreateIdPage() {
return (

View File

@ -285,6 +285,7 @@ export const mergeHeaders = (
const iterator =
header instanceof Headers ? header.entries() : Object.entries(header);
// @ts-ignore
for (const [key, value] of iterator) {
if (value === null) {
mergedHeaders.delete(key);

View File

@ -42,6 +42,10 @@ export type VerifyOtpResponse = {
* Status of the verification
*/
ok: boolean;
/**
* Confirmation message
*/
message: string;
};
export type AcceptConsentRequest = {
@ -49,6 +53,10 @@ export type AcceptConsentRequest = {
* The consent challenge to accept
*/
consent_challenge: string;
/**
* Phone number associated with the consent
*/
phone_number: string;
};
export type AcceptConsentResponse = {

View File

@ -19,11 +19,13 @@ export const zVerifyOtpRequest = z.object({
export const zVerifyOtpResponse = z.object({
redirect_url: z.string(),
ok: z.boolean()
ok: z.boolean(),
message: z.string()
});
export const zAcceptConsentRequest = z.object({
consent_challenge: z.string()
consent_challenge: z.string(),
phone_number: z.string().max(15)
});
export const zAcceptConsentResponse = z.object({

View File

@ -1,12 +0,0 @@
'use client';
import { motion, MotionProps } from 'framer-motion';
import { ReactNode } from 'react';
interface MotionWrapperProps extends MotionProps {
children: ReactNode;
}
export function MotionWrapper({ children, ...props }: MotionWrapperProps) {
return <motion.div {...props}>{children}</motion.div>;
}

View File

@ -5,13 +5,13 @@ import { useRouter } from "next/navigation";
import { Button, Stack } from "@mantine/core";
import { useForm } from "@mantine/form";
import { Auth } from "@/client";
import PhoneInput from "@/components/PhoneInput/PhoneInput";
import PhoneInput from "@/components/ui/PhoneInput/PhoneInput";
import {
setLoginChallenge,
setPhoneNumber,
} from "@/lib/features/auth/authSlice";
} from "@/lib/store/features/auth/authSlice";
import { notifications } from "@/lib/notifications";
import { useAppDispatch } from "@/lib/store";
import { useAppDispatch } from "@/lib/store/store";
type LoginForm = {
phoneNumber: string;
@ -62,19 +62,6 @@ const LoginForm: FC<Props> = ({ loginChallenge, isCreatingId = false }) => {
router.push("/verify-phone");
}
});
// new AuthService().requestLogin(values.phoneNumber)
// .then(response => response.data)
// .then(({ ok, message }) => {
// if (!ok) {
// notifications.error({ message });
// } else {
// router.push("/verify-phone");
// }
// })
// .catch(error => {
// console.error(error);
// notifications.error({ message: error.toString() });
// })
};
const navigateToCreateId = () => router.push("/create-id");

View File

@ -1,5 +1,5 @@
import { Group } from "@mantine/core";
import { ColorSchemeToggle } from "@/components/ColorSchemeToggle/ColorSchemeToggle";
import { ColorSchemeToggle } from "@/components/ui/ColorSchemeToggle/ColorSchemeToggle";
const Header = () => {
return (

View File

@ -0,0 +1,12 @@
"use client";
import { ReactNode } from "react";
import { motion, MotionProps } from "framer-motion";
interface MotionWrapperProps extends MotionProps {
children: ReactNode;
}
export function MotionWrapper({ children, ...props }: MotionWrapperProps) {
return <motion.div {...props}>{children}</motion.div>;
}

View File

@ -1,6 +1,6 @@
import { CSSProperties, FC, ReactNode } from "react";
import classNames from "classnames";
import { MotionWrapper } from "@/components/MotionWrapper/MotionWrapper";
import { MotionWrapper } from "@/components/layout/MotionWrapper/MotionWrapper";
import styles from "./PageItem.module.css";
type Props = {

View File

@ -7,7 +7,7 @@ import {
useComputedColorScheme,
useMantineColorScheme,
} from "@mantine/core";
import style from "@/components/ColorSchemeToggle/ColorSchemeToggle.module.css";
import style from "./ColorSchemeToggle.module.css";
export function ColorSchemeToggle() {
const { setColorScheme } = useMantineColorScheme();

View File

@ -7,10 +7,8 @@ import {
type InputBaseProps,
type PolymorphicComponentProps,
} from "@mantine/core";
import CountrySelect from "@/components/PhoneInput/components/CountrySelect";
import { Country } from "@/components/PhoneInput/types";
import getInitialDataFromValue from "@/components/PhoneInput/utils/getInitialDataFromValue";
import getPhoneMask from "@/components/PhoneInput/utils/getPhoneMask";
import { Country } from "@/components/ui/PhoneInput/types";
import getInitialDataFromValue from "@/components/ui/PhoneInput/utils/getInitialDataFromValue";
type AdditionalProps = {
onChange: (value: string | null) => void;
@ -68,20 +66,21 @@ const PhoneInput = ({
component={IMaskInput}
inputRef={inputRef}
leftSection={
<CountrySelect
disabled={disabled || readOnly}
country={country}
setCountry={country => {
setCountry(country);
setPhoneMask(getPhoneMask(country.code), country);
setValue("");
if (inputRef.current) {
inputRef.current.focus();
}
}}
leftSectionWidth={leftSectionWidth}
inputWidth={dropdownWidth}
/>
<></>
// <CountrySelect
// disabled={disabled || readOnly}
// country={country}
// setCountry={country => {
// setCountry(country);
// setPhoneMask(getPhoneMask(country.code), country);
// setValue("");
// if (inputRef.current) {
// inputRef.current.focus();
// }
// }}
// leftSectionWidth={leftSectionWidth}
// inputWidth={dropdownWidth}
// />
}
leftSectionWidth={leftSectionWidth}
styles={{

View File

@ -10,9 +10,10 @@ import {
Text,
useCombobox,
} from "@mantine/core";
import style from "@/components/PhoneInput/PhoneInput.module.css";
import { Country } from "@/components/PhoneInput/types";
import countryOptionsDataMap from "@/components/PhoneInput/utils/countryOptionsDataMap";
import style from "@/components/ui/PhoneInput/PhoneInput.module.css";
import { Country } from "@/components/ui/PhoneInput/types";
import countryOptionsDataMap from "@/components/ui/PhoneInput/utils/countryOptionsDataMap";
const countryOptionsData = Object.values(countryOptionsDataMap);

View File

@ -1,9 +1,9 @@
import countries from "i18n-iso-countries";
import ru from "i18n-iso-countries/langs/ru.json";
import { type CountryCode, getCountries } from "libphonenumber-js";
import getFlagEmoji from "@/components/PhoneInput/utils/getFlagEmoji";
import getFlagEmoji from "@/components/ui/PhoneInput/utils/getFlagEmoji";
import { getCountryCallingCode } from "libphonenumber-js/max";
import { Country } from "@/components/PhoneInput/types";
import { Country } from "@/components/ui/PhoneInput/types";
countries.registerLocale(ru);

View File

@ -1,7 +1,7 @@
import { CountryCode } from "libphonenumber-js";
import { Country } from "@/components/PhoneInput/types";
import countryOptionsDataMap from "@/components/PhoneInput/utils/countryOptionsDataMap";
import getPhoneMask from "@/components/PhoneInput/utils/getPhoneMask";
import { Country } from "@/components/ui/PhoneInput/types";
import countryOptionsDataMap from "@/components/ui/PhoneInput/utils/countryOptionsDataMap";
import getPhoneMask from "@/components/ui/PhoneInput/utils/getPhoneMask";
type InitialDataFromValue = {
country: Country;

View File

@ -2,5 +2,5 @@ import type { CreateClientConfig } from './client/client.gen';
export const createClientConfig: CreateClientConfig = (config) => ({
...config,
baseUrl: 'http://id.logidex.ru/api',
baseUrl: process.env.NEXT_PUBLIC_API_URL,
});

View File

@ -1,12 +0,0 @@
import { combineReducers } from "@reduxjs/toolkit";
import targetServiceReducer from "@/lib/features/targetService/targetServiceSlice";
import verificationReducer from "@/lib/features/verification/verificationSlice";
import authReducer from "@/lib/features/auth/authSlice";
const rootReducer = combineReducers({
targetService: targetServiceReducer,
verification: verificationReducer,
auth: authReducer,
});
export default rootReducer;

View File

@ -0,0 +1,12 @@
import { combineReducers } from "@reduxjs/toolkit";
import authReducer from "@/lib/store/features/auth/authSlice";
import targetServiceReducer from "@/lib/store/features/targetService/targetServiceSlice";
import verificationReducer from "@/lib/store/features/verification/verificationSlice";
const rootReducer = combineReducers({
targetService: targetServiceReducer,
verification: verificationReducer,
auth: authReducer,
});
export default rootReducer;

View File

@ -2,7 +2,7 @@ import { configureStore } from "@reduxjs/toolkit";
import { useDispatch } from "react-redux";
import { persistReducer, persistStore } from "redux-persist";
import storage from "redux-persist/lib/storage";
import rootReducer from "@/lib/features/rootReducer";
import rootReducer from "@/lib/store/features/rootReducer";
const persistConfig = {
key: "root",

View File

@ -3,7 +3,7 @@
import { ReactNode } from "react";
import { Provider } from "react-redux";
import { PersistGate } from "redux-persist/integration/react";
import { persistor, store } from "@/lib/store";
import { persistor, store } from "@/lib/store/store";
type Props = {
children: ReactNode;

View File

@ -1,7 +1,7 @@
{
"compilerOptions": {
"types": ["node", "jest", "@testing-library/jest-dom"],
"target": "es5",
"target": "es2015",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
@ -21,5 +21,5 @@
"plugins": [{ "name": "next" }]
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
"exclude": ["node_modules", "src/client/*"]
}

View File

@ -11604,11 +11604,11 @@ __metadata:
linkType: hard
"jiti@npm:^2.3.0":
version: 2.5.1
resolution: "jiti@npm:2.5.1"
version: 2.6.1
resolution: "jiti@npm:2.6.1"
bin:
jiti: lib/jiti-cli.mjs
checksum: 10c0/f0a38d7d8842cb35ffe883038166aa2d52ffd21f1a4fc839ae4076ea7301c22a1f11373f8fc52e2667de7acde8f3e092835620dd6f72a0fbe9296b268b0874bb
checksum: 10c0/79b2e96a8e623f66c1b703b98ec1b8be4500e1d217e09b09e343471bbb9c105381b83edbb979d01cef18318cc45ce6e153571b6c83122170eefa531c64b6789b
languageName: node
linkType: hard
@ -12456,14 +12456,14 @@ __metadata:
linkType: hard
"mlly@npm:^1.7.1, mlly@npm:^1.7.4":
version: 1.7.4
resolution: "mlly@npm:1.7.4"
version: 1.8.0
resolution: "mlly@npm:1.8.0"
dependencies:
acorn: "npm:^8.14.0"
pathe: "npm:^2.0.1"
pkg-types: "npm:^1.3.0"
ufo: "npm:^1.5.4"
checksum: 10c0/69e738218a13d6365caf930e0ab4e2b848b84eec261597df9788cefb9930f3e40667be9cb58a4718834ba5f97a6efeef31d3b5a95f4388143fd4e0d0deff72ff
acorn: "npm:^8.15.0"
pathe: "npm:^2.0.3"
pkg-types: "npm:^1.3.1"
ufo: "npm:^1.6.1"
checksum: 10c0/f174b844ae066c71e9b128046677868e2e28694f0bbeeffbe760b2a9d8ff24de0748d0fde6fabe706700c1d2e11d3c0d7a53071b5ea99671592fac03364604ab
languageName: node
linkType: hard
@ -12649,9 +12649,9 @@ __metadata:
linkType: hard
"node-fetch-native@npm:^1.6.6":
version: 1.6.6
resolution: "node-fetch-native@npm:1.6.6"
checksum: 10c0/8c12dab0e640d8bc126a03d604af9cf3fc1b87f2cda5af0c71601079d5ed835c1dc149c7042b61c83f252a382e1cf1e541788f4c9e8e6c089af77497190f5dc3
version: 1.6.7
resolution: "node-fetch-native@npm:1.6.7"
checksum: 10c0/8b748300fb053d21ca4d3db9c3ff52593d5e8f8a2d9fe90cbfad159676e324b954fdaefab46aeca007b5b9edab3d150021c4846444e4e8ab1f4e44cd3807be87
languageName: node
linkType: hard
@ -13325,7 +13325,7 @@ __metadata:
languageName: node
linkType: hard
"pkg-types@npm:^1.2.0, pkg-types@npm:^1.3.0, pkg-types@npm:^1.3.1":
"pkg-types@npm:^1.2.0, pkg-types@npm:^1.3.1":
version: 1.3.1
resolution: "pkg-types@npm:1.3.1"
dependencies:
@ -14469,9 +14469,9 @@ __metadata:
linkType: hard
"run-applescript@npm:^7.0.0":
version: 7.0.0
resolution: "run-applescript@npm:7.0.0"
checksum: 10c0/bd821bbf154b8e6c8ecffeaf0c33cebbb78eb2987476c3f6b420d67ab4c5301faa905dec99ded76ebb3a7042b4e440189ae6d85bbbd3fc6e8d493347ecda8bfe
version: 7.1.0
resolution: "run-applescript@npm:7.1.0"
checksum: 10c0/ab826c57c20f244b2ee807704b1ef4ba7f566aa766481ae5922aac785e2570809e297c69afcccc3593095b538a8a77d26f2b2e9a1d9dffee24e0e039502d1a03
languageName: node
linkType: hard
@ -16335,7 +16335,7 @@ __metadata:
languageName: node
linkType: hard
"ufo@npm:^1.5.4":
"ufo@npm:^1.5.4, ufo@npm:^1.6.1":
version: 1.6.1
resolution: "ufo@npm:1.6.1"
checksum: 10c0/5a9f041e5945fba7c189d5410508cbcbefef80b253ed29aa2e1f8a2b86f4bd51af44ee18d4485e6d3468c92be9bf4a42e3a2b72dcaf27ce39ce947ec994f1e6b
@ -17160,8 +17160,8 @@ __metadata:
linkType: hard
"zod@npm:^4.0.10":
version: 4.0.10
resolution: "zod@npm:4.0.10"
checksum: 10c0/8d1145e767c22b571a7967c198632f69ef15ce571b5021cdba84cf31d9af2ca40b033ea2fcbe5797cfd2da9c67b3a6ebe435938eabfbb1d1f3ab2f17f00f443b
version: 4.1.12
resolution: "zod@npm:4.1.12"
checksum: 10c0/b64c1feb19e99d77075261eaf613e0b2be4dfcd3551eff65ad8b4f2a079b61e379854d066f7d447491fcf193f45babd8095551a9d47973d30b46b6d8e2c46774
languageName: node
linkType: hard