feat: login_challenge and scope storing, mock api
This commit is contained in:
@ -23,9 +23,11 @@
|
||||
"@mantine/core": "8.1.2",
|
||||
"@mantine/form": "^8.1.3",
|
||||
"@mantine/hooks": "8.1.2",
|
||||
"@mantine/notifications": "^8.2.1",
|
||||
"@next/bundle-analyzer": "^15.3.3",
|
||||
"@reduxjs/toolkit": "^2.8.2",
|
||||
"@tabler/icons-react": "^3.34.0",
|
||||
"axios": "^1.11.0",
|
||||
"classnames": "^2.5.1",
|
||||
"framer-motion": "^12.23.7",
|
||||
"i18n-iso-countries": "^7.14.0",
|
||||
|
||||
@ -1,28 +1,63 @@
|
||||
"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 SCOPES from "@/constants/scopes";
|
||||
import { Scopes } from "@/enums/Scopes";
|
||||
import { notifications } from "@/lib/notifications";
|
||||
import { RootState } from "@/lib/store";
|
||||
import ServiceData from "@/types/ServiceData";
|
||||
import { AuthService } from "@/mocks/authService";
|
||||
|
||||
const ConsentButton: FC = () => {
|
||||
const serviceCode = useSelector(
|
||||
(state: RootState) => state.targetService.serviceCode
|
||||
);
|
||||
const [serviceData, setServiceData] = useState<ServiceData>();
|
||||
const auth = useSelector((state: RootState) => state.auth);
|
||||
const [clientName, setClientName] = useState<string>(Scopes.UNDEFINED);
|
||||
const [serviceRequiredAccess, setServiceRequiredAccess] =
|
||||
useState<string>("");
|
||||
|
||||
const setAccessesForScope = () => {
|
||||
const accesses: string[] = [];
|
||||
(auth.scope ?? []).forEach((scopeItem: Scopes) => {
|
||||
const access = SCOPES[scopeItem];
|
||||
if (access) accesses.push(access);
|
||||
});
|
||||
setServiceRequiredAccess(accesses.join("\n"));
|
||||
};
|
||||
|
||||
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(() => {
|
||||
if (serviceCode === ServiceCode.UNDEFINED) {
|
||||
redirect("services");
|
||||
}
|
||||
setServiceData(SERVICES[serviceCode]);
|
||||
}, [serviceCode]);
|
||||
setAccessesForScope();
|
||||
requestConsent();
|
||||
}, []);
|
||||
|
||||
const confirmAccess = () => {};
|
||||
const confirmAccess = () => {
|
||||
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 (
|
||||
<>
|
||||
@ -34,8 +69,7 @@ const ConsentButton: FC = () => {
|
||||
<Text
|
||||
fz={"h4"}
|
||||
ta="center">
|
||||
Сервис {serviceData?.name} получит{" "}
|
||||
{serviceData?.requiredAccesses}
|
||||
Сервис {clientName} получит {serviceRequiredAccess}
|
||||
</Text>
|
||||
</>
|
||||
);
|
||||
|
||||
19
src/app/login/page.tsx
Normal file
19
src/app/login/page.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import LoginForm from "@/components/LoginForm/LoginForm";
|
||||
import Logo from "@/components/Logo/Logo";
|
||||
import PageItem from "@/components/PageBlock/PageItem";
|
||||
import PageContainer from "@/components/PageContainer/PageContainer";
|
||||
|
||||
interface LoginPageProps {
|
||||
searchParams: { login_challenge?: string };
|
||||
}
|
||||
|
||||
export default function LoginPage({ searchParams }: LoginPageProps) {
|
||||
return (
|
||||
<PageContainer center>
|
||||
<PageItem fullScreenMobile>
|
||||
<Logo title={"Вход"} />
|
||||
<LoginForm loginChallenge={searchParams.login_challenge} />
|
||||
</PageItem>
|
||||
</PageContainer>
|
||||
);
|
||||
}
|
||||
@ -1,15 +1,5 @@
|
||||
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 { redirect } from "next/navigation";
|
||||
|
||||
export default function MainPage() {
|
||||
return (
|
||||
<PageContainer center>
|
||||
<PageItem fullScreenMobile>
|
||||
<Logo title={"Вход"} />
|
||||
<LoginForm />
|
||||
</PageItem>
|
||||
</PageContainer>
|
||||
);
|
||||
redirect("/login");
|
||||
}
|
||||
|
||||
@ -5,8 +5,8 @@ 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 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";
|
||||
@ -15,10 +15,10 @@ const ServicesList = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const services = useMemo(
|
||||
() =>
|
||||
Object.entries(SERVICES)
|
||||
.filter(([key]) => key !== ServiceCode.UNDEFINED)
|
||||
Object.entries(SCOPES)
|
||||
.filter(([key]) => key !== Scopes.UNDEFINED)
|
||||
.map(([, value]) => value),
|
||||
[SERVICES]
|
||||
[SCOPES]
|
||||
);
|
||||
|
||||
const onServiceClick = (service: ServiceData) => {
|
||||
|
||||
@ -2,10 +2,15 @@
|
||||
|
||||
import { FC } from "react";
|
||||
import { redirect } from "next/navigation";
|
||||
import { useSelector } from "react-redux";
|
||||
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";
|
||||
import { setScope } from "@/lib/features/auth/authSlice";
|
||||
import { notifications } from "@/lib/notifications";
|
||||
import { RootState, useAppDispatch } from "@/lib/store";
|
||||
import { AuthService } from "@/mocks/authService";
|
||||
|
||||
type VerifyNumberForm = {
|
||||
code: string;
|
||||
@ -20,11 +25,27 @@ const VerifyPhoneForm: FC = () => {
|
||||
code: code => code.length !== 6 && "Введите весь код",
|
||||
},
|
||||
});
|
||||
const authState = useSelector((state: RootState) => state.auth);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleSubmit = (values: VerifyNumberForm) => {
|
||||
console.log(values);
|
||||
if (!authState.phoneNumber || !authState.loginChallenge) return;
|
||||
|
||||
redirect("/services");
|
||||
new AuthService()
|
||||
.approveLogin(
|
||||
authState.phoneNumber,
|
||||
values.code,
|
||||
authState.loginChallenge
|
||||
)
|
||||
.then(response => response.data)
|
||||
.then(({ redirectUrl, scope }) => {
|
||||
dispatch(setScope(scope));
|
||||
window.location.href = redirectUrl;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
notifications.error({ message: error.toString() });
|
||||
});
|
||||
};
|
||||
|
||||
const navigateToLogin = () => redirect("/");
|
||||
|
||||
@ -1,21 +1,27 @@
|
||||
"use client";
|
||||
|
||||
import { FC, useState } from "react";
|
||||
import { redirect } from "next/navigation";
|
||||
import { FC, useEffect, useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { Button, Stack } from "@mantine/core";
|
||||
import { useForm } from "@mantine/form";
|
||||
import PhoneInput from "@/components/PhoneInput/PhoneInput";
|
||||
import { useAppDispatch } from "@/lib/store";
|
||||
import { setLoginChallenge, setPhoneNumber } from "@/lib/features/auth/authSlice";
|
||||
import { AuthService } from "@/mocks/authService";
|
||||
import { notifications } from "@/lib/notifications";
|
||||
|
||||
type LoginForm = {
|
||||
phoneNumber: string;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
loginChallenge?: string;
|
||||
isCreatingId?: boolean;
|
||||
};
|
||||
|
||||
const LoginForm: FC<Props> = ({ isCreatingId = false }) => {
|
||||
const LoginForm: FC<Props> = ({ loginChallenge, isCreatingId = false }) => {
|
||||
const [phoneMask, setPhoneMask] = useState<string>("");
|
||||
const router = useRouter();
|
||||
const form = useForm<LoginForm>({
|
||||
initialValues: {
|
||||
phoneNumber: "",
|
||||
@ -26,17 +32,33 @@ const LoginForm: FC<Props> = ({ isCreatingId = false }) => {
|
||||
"Введите корректный номер",
|
||||
},
|
||||
});
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(setLoginChallenge(loginChallenge ?? null));
|
||||
}, [loginChallenge]);
|
||||
|
||||
const handleSubmit = (values: LoginForm) => {
|
||||
console.log(values);
|
||||
console.log(phoneMask);
|
||||
dispatch(setPhoneNumber(values.phoneNumber));
|
||||
|
||||
redirect("/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 = () => redirect("/create-id");
|
||||
const navigateToCreateId = () => router.push("/create-id");
|
||||
|
||||
const navigateToLogin = () => redirect("/");
|
||||
const navigateToLogin = () => router.push("/");
|
||||
|
||||
return (
|
||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||
|
||||
8
src/constants/scopes.ts
Normal file
8
src/constants/scopes.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { Scopes } from "@/enums/Scopes";
|
||||
|
||||
const SCOPES = {
|
||||
[Scopes.UNDEFINED]: "",
|
||||
[Scopes.OPENID]: "доступ к учетной записи и номеру телефона",
|
||||
};
|
||||
|
||||
export default SCOPES;
|
||||
@ -1,20 +0,0 @@
|
||||
import { ServiceCode } from "@/enums/ServiceCode";
|
||||
import ServiceData from "@/types/ServiceData";
|
||||
|
||||
const SERVICES = {
|
||||
[ServiceCode.UNDEFINED]: {
|
||||
code: ServiceCode.UNDEFINED,
|
||||
name: "undefined",
|
||||
link: "",
|
||||
requiredAccesses: "",
|
||||
},
|
||||
[ServiceCode.CRM]: {
|
||||
code: ServiceCode.CRM,
|
||||
name: "LogiDex CRM",
|
||||
link: "https://skirbo.ru",
|
||||
requiredAccesses:
|
||||
"доступ к учетной записи и сделкам по фулфиллменту",
|
||||
} as ServiceData,
|
||||
};
|
||||
|
||||
export default SERVICES;
|
||||
4
src/enums/Scopes.ts
Normal file
4
src/enums/Scopes.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export enum Scopes {
|
||||
OPENID = "openid",
|
||||
UNDEFINED = "",
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
export enum ServiceCode {
|
||||
CRM = "crm",
|
||||
UNDEFINED = "",
|
||||
}
|
||||
34
src/lib/features/auth/authSlice.ts
Normal file
34
src/lib/features/auth/authSlice.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
import { Scopes } from "@/enums/Scopes";
|
||||
|
||||
interface AuthState {
|
||||
loginChallenge: string | null;
|
||||
phoneNumber: string | null;
|
||||
scope: Scopes[];
|
||||
}
|
||||
|
||||
const initialState: AuthState = {
|
||||
loginChallenge: null,
|
||||
phoneNumber: null,
|
||||
scope: [],
|
||||
};
|
||||
|
||||
export const authSlice = createSlice({
|
||||
name: "authentication",
|
||||
initialState,
|
||||
reducers: {
|
||||
setLoginChallenge: (state, action: PayloadAction<string | null>) => {
|
||||
state.loginChallenge = action.payload;
|
||||
},
|
||||
setPhoneNumber: (state, action: PayloadAction<string | null>) => {
|
||||
state.phoneNumber = action.payload;
|
||||
},
|
||||
setScope: (state, action: PayloadAction<Scopes[]>) => {
|
||||
state.scope = action.payload;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export const { setLoginChallenge, setPhoneNumber, setScope } = authSlice.actions;
|
||||
|
||||
export default authSlice.reducer;
|
||||
@ -1,10 +1,12 @@
|
||||
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;
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
import { ServiceCode } from "@/enums/ServiceCode";
|
||||
import { Scopes } from "@/enums/Scopes";
|
||||
|
||||
interface TargetServiceState {
|
||||
serviceCode: ServiceCode;
|
||||
serviceCode: Scopes;
|
||||
}
|
||||
|
||||
const initialState: TargetServiceState = {
|
||||
serviceCode: ServiceCode.UNDEFINED,
|
||||
serviceCode: Scopes.UNDEFINED,
|
||||
};
|
||||
|
||||
export const targetServiceSlice = createSlice({
|
||||
name: "targetService",
|
||||
initialState,
|
||||
reducers: {
|
||||
setTargetService: (state, action: PayloadAction<ServiceCode>) => {
|
||||
setTargetService: (state, action: PayloadAction<Scopes>) => {
|
||||
state.serviceCode = action.payload;
|
||||
},
|
||||
},
|
||||
|
||||
1
src/lib/notifications/index.ts
Normal file
1
src/lib/notifications/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from "./notifications";
|
||||
46
src/lib/notifications/notifications.ts
Normal file
46
src/lib/notifications/notifications.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { notifications } from "@mantine/notifications";
|
||||
|
||||
type CustomNotifications = {
|
||||
notify: (...params: Parameters<typeof notifications.show>) => void;
|
||||
success: (...params: Parameters<typeof notifications.show>) => void;
|
||||
warn: (...params: Parameters<typeof notifications.show>) => void;
|
||||
error: (...params: Parameters<typeof notifications.show>) => void;
|
||||
guess: (
|
||||
ok: boolean,
|
||||
...params: Parameters<typeof notifications.show>
|
||||
) => void;
|
||||
} & typeof notifications;
|
||||
|
||||
const customNotifications: CustomNotifications = {
|
||||
...notifications,
|
||||
notify: params => {
|
||||
return notifications.show({
|
||||
...params,
|
||||
color: "blue",
|
||||
});
|
||||
},
|
||||
success: params => {
|
||||
return notifications.show({
|
||||
...params,
|
||||
color: "green",
|
||||
});
|
||||
},
|
||||
warn: params => {
|
||||
return notifications.show({
|
||||
...params,
|
||||
color: "yellow",
|
||||
});
|
||||
},
|
||||
error: params => {
|
||||
return notifications.show({
|
||||
...params,
|
||||
color: "red",
|
||||
});
|
||||
},
|
||||
guess: (ok: boolean, params) => {
|
||||
if (ok) return customNotifications.success(params);
|
||||
return customNotifications.error(params);
|
||||
},
|
||||
};
|
||||
|
||||
export { customNotifications as notifications };
|
||||
@ -7,7 +7,7 @@ import rootReducer from "@/lib/features/rootReducer";
|
||||
const persistConfig = {
|
||||
key: "root",
|
||||
storage,
|
||||
whitelist: ["targetService", "verification"],
|
||||
whitelist: ["targetService", "verification", "auth"],
|
||||
};
|
||||
|
||||
const persistedReducer = persistReducer(persistConfig, rootReducer);
|
||||
|
||||
75
src/mocks/authService.ts
Normal file
75
src/mocks/authService.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import { AxiosResponse } from "axios";
|
||||
import { Scopes } from "@/enums/Scopes";
|
||||
|
||||
type MockOkMessage = {
|
||||
ok: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
||||
type MockApproveLoginResponse = {
|
||||
redirectUrl: string;
|
||||
scope: Scopes[];
|
||||
}
|
||||
|
||||
type MockRequestConsentResponse = {
|
||||
clientName: string;
|
||||
}
|
||||
|
||||
type MockApproveConsentResponse = {
|
||||
redirectUrl: string;
|
||||
}
|
||||
|
||||
export class AuthService {
|
||||
async requestLogin(phoneNumber: string): Promise<AxiosResponse<MockOkMessage>> {
|
||||
return Promise.resolve({
|
||||
data: { ok: true, message: "Mock response" } as MockOkMessage,
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
headers: {},
|
||||
config: {} as any,
|
||||
});
|
||||
}
|
||||
|
||||
async approveLogin(
|
||||
phoneNumber: string,
|
||||
code: string,
|
||||
loginChallenge: string
|
||||
): Promise<AxiosResponse<MockApproveLoginResponse>> {
|
||||
return Promise.resolve({
|
||||
data: {
|
||||
redirectUrl:
|
||||
"http://oauth2.logidex.ru/oauth2/auth?client_id=crm-client&login_verifier=c-4HxjyA0rUMMOYQG-kHStU5pxFeOdViKOyk-eT_qv7vNPHu3xdysdYj3Jwq2gh6vRSHdO6xA7NQgneUho5I_LyENNTFqRkjWwhAsy7Ad9DFhcAWVRCU1rI3ksy38-UTWlto7vKVVhGgRzvlk-coa273uLz-BEo64oUl9B_gcojLnLjO1Q1W6Hu8_nJxqyQBhLPcFOZ1vDVnNcQF5UbB9bld3Wr_1nn3r7sQMBQR54Vphcku6a37GYbPVMVGHo0nBGf6rps6Xg4L6IsD5hlsHzsw5OX3W0MDJ6o--VxHr_HveAH8K7R21Q59JtHa-26pO4KGjetGfgr8rPOTRtsZiKApjZ8qjM9pEgusPj39ysmqYo1wmJo1ZXz8wXh-t3UO0pGIZogmzEOLs5bPCCbXrjG7VaQ78jufSAG2pmhMJxR9AmWsaJms16lev1dBFn-IBdrr4LqYrsVQMqpZstF18ENSURAKejHc8l8m4xocy2-D-ZaeZX0k0vnuGsNTbxT79D1u_-ALm2n0LRwcsK5VCF8v5oO_aMFxcSO87QHU8wy8Wj3um3IBb0sCQXDCpsYIlwqczWNWmxaGXDsmqqUYvZdvWKEXMI5BbbVc44h4_sOaa6BKAaNSceC2GHqN94GWz8dmsX7xyfXMsHYR8_hUFsztN8OstrQkRddJ8een4mdW777W3PYe_U4UL2S2az4L7tC5DTifDCHTfYknb4baQ3UT7x4N8eCd2_Xlkl4gQKU5Mm8njZWucWsLjdW7NG8Q_aDQEl45VunmaQ8iKOTrn1BiNzRHnYdOm15C8nnxHyZ9pO8IKELxUGIKnwP8eF6-A-Rj_bLWWIBquLTRgBrR153gu5Srh2Xl8-LU4ffxM6ipO1nHJMPg3_5493yS1ua_ZWUuht_d4C4Y6j6xuFJHx-bKrCIffiGiSUnpKkepzGnTHCS02wNDVItheAUnlO18zbxsHBFM1tjQQrLIB3cxQRrK12NmIgOheiQMzkSwpQ2CmdRnVpJBGy8Nzp7X_YP-nVC9ctzFR1YrTEw-ZH0wVYjPu_vsijwUtqq3mABD8lw%3D&redirect_uri=http%3A%2F%2Fcrm.logidex.ru%2Fcallback&response_type=code&scope=openid&state=csrf_token",
|
||||
scope: ["openid"] as Scopes[],
|
||||
},
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
headers: {},
|
||||
config: {} as any,
|
||||
});
|
||||
}
|
||||
|
||||
async requestConsent(consentChallenge: string): Promise<AxiosResponse<MockRequestConsentResponse>> {
|
||||
return Promise.resolve({
|
||||
data: {
|
||||
clientName: "crm",
|
||||
},
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
headers: {},
|
||||
config: {} as any,
|
||||
});
|
||||
}
|
||||
|
||||
async approveConsent(consentChallenge: string): Promise<AxiosResponse<MockApproveConsentResponse>> {
|
||||
return Promise.resolve({
|
||||
data: {
|
||||
redirectUrl:
|
||||
"http://oauth2.logidex.ru/oauth2/auth?client_id=crm-client&consent_verifier=hU0HetHSHoqc4ZMvwsjMaFUsVckZ3B6ztXyQim4vwcptp4Dp9cIvSByiFHvThLkaIVl2f7uDxB8hUcUoG1-DvNDC3qcGCskLlNn0tDlNcxb41LZtS28D8iZJAUiZedqDdGCfhkuH4TioErId5m-8-y5Y-PYrosfcrqsVfK88vZ5kgViMIjROe68Vc_O5kxpPUymt5I_-oUeFMdrDnjpVcTipwTJIG-WutbtUBHp6tA3FXIfo-0ai-o8yr2Lv2bQiBSegYKA4GmfrQ25xn7_yQGLyGVBVsKPCNRQAyRvdeqFEVGm-3SUxvIJCeyCXaZrHxENSUbxo6xd1m_oVHqye8hXcZSWmFVOa4eo4Rw6OWsnN3AWl75XLt_maKcL_LZftkQERtJBgV2-8C1QYJXwoPS0uTFANq39s2778KIP0XbufiB3UW1QvmUdzKKH43K4MnB-F9ah26nzaw8HwEBTbDGclvkq7TFAozKddwnumgrqRkbElwC3eqr5LnpMfGR1vCVBP81sPjx26LoiKOpmuamfT-O37EsVHdooeP8ry2IjCx0KrUe5wI93XiUc4RIMn_MsO8zaifyNrzFfvVQ7VPNj3QasM4O4drDyGLict1fWiNZP_KVFeAnojOp258nPqDn76VSzROweummzSD-lC56zviX9pZjmGb0RpXb8eeB2Nc4uWCy3dYw4kxEFQSqwU7liqI9paZLv0Vl1PrvS4GL_vv3zh4YKpp8h1yT0IWDnEL75dmIeSBXB7eTkZy8ING1HdwvfH1TdYImCrmLTi93JWtSJvsZWklUBQsFQ900hYPYGK4WVdxRQOTsHrJhJwOex5so7mrnowHpXQuUU6eDi9p2Yj0_YN2XuPIs1I9iS2F2S6t1_kNRmJupzo3g09bY8AGNsSDeEwp2riqXQ_o3xgATaIUycbve2qcOIr89kHomYSCc0YiQnka3zBb5RRTlYhDuQlgeHuEnIBWU2oLYcnyP-zlbeSxRbYuYu4uxXtLHDT9E6tDxqXxSYvw4AzUw-EwQX8v0LamJnQLHCSwD7F8S0M5COSm_Pv56DhtBevnU7PqrpZ-FKOXm89A2HXx9XO8qQMG-tg3y5TZ5vVfTwJ6WkzZNkZSWAZGiwQX4wiQmjep4wlKgP3ZQ8VV0CVS6R9f1DMwY8tCvuJFumd_OORK7-q_bgg3Xp3Njc%3D&redirect_uri=http%3A%2F%2Fcrm.logidex.ru%2Fcallback&response_type=code&scope=openid&state=csrf_token",
|
||||
},
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
headers: {},
|
||||
config: {} as any,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
import { ServiceCode } from "@/enums/ServiceCode";
|
||||
|
||||
type ServiceData = {
|
||||
code: ServiceCode;
|
||||
name: string;
|
||||
link: string;
|
||||
requiredAccesses: string;
|
||||
}
|
||||
|
||||
export default ServiceData;
|
||||
121
yarn.lock
121
yarn.lock
@ -2025,7 +2025,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/runtime@npm:^7.9.2":
|
||||
"@babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2":
|
||||
version: 7.28.2
|
||||
resolution: "@babel/runtime@npm:7.28.2"
|
||||
checksum: 10c0/c20afe253629d53a405a610b12a62ac74d341a2c1e0fb202bbef0c118f6b5c84f94bf16039f58fd0483dd256901259930a43976845bdeb180cab1f882c21b6e0
|
||||
@ -3797,6 +3797,30 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@mantine/notifications@npm:^8.2.1":
|
||||
version: 8.2.1
|
||||
resolution: "@mantine/notifications@npm:8.2.1"
|
||||
dependencies:
|
||||
"@mantine/store": "npm:8.2.1"
|
||||
react-transition-group: "npm:4.4.5"
|
||||
peerDependencies:
|
||||
"@mantine/core": 8.2.1
|
||||
"@mantine/hooks": 8.2.1
|
||||
react: ^18.x || ^19.x
|
||||
react-dom: ^18.x || ^19.x
|
||||
checksum: 10c0/879b6e286ba28a111f0a765e5e0e56574c1e2521bcbb753ef3a25be7bf42df77a3168e0b99d7c093a250324e6bbd0cd6ab9a40e3ed1acce5ee18d5d2919dd118
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@mantine/store@npm:8.2.1":
|
||||
version: 8.2.1
|
||||
resolution: "@mantine/store@npm:8.2.1"
|
||||
peerDependencies:
|
||||
react: ^18.x || ^19.x
|
||||
checksum: 10c0/9897fd48d4c9d1f5cebd641881df8334604d844f0fbc630b80f18807ad27152d10491041cfc7ed75ca12ddc0fc059e7728a94d0bfda4ac479b56ba0f953f2e91
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@napi-rs/wasm-runtime@npm:^0.2.11":
|
||||
version: 0.2.11
|
||||
resolution: "@napi-rs/wasm-runtime@npm:0.2.11"
|
||||
@ -6330,6 +6354,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"asynckit@npm:^0.4.0":
|
||||
version: 0.4.0
|
||||
resolution: "asynckit@npm:0.4.0"
|
||||
checksum: 10c0/d73e2ddf20c4eb9337e1b3df1a0f6159481050a5de457c55b14ea2e5cb6d90bb69e004c9af54737a5ee0917fcf2c9e25de67777bbe58261847846066ba75bc9d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"available-typed-arrays@npm:^1.0.5":
|
||||
version: 1.0.5
|
||||
resolution: "available-typed-arrays@npm:1.0.5"
|
||||
@ -6353,6 +6384,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"axios@npm:^1.11.0":
|
||||
version: 1.11.0
|
||||
resolution: "axios@npm:1.11.0"
|
||||
dependencies:
|
||||
follow-redirects: "npm:^1.15.6"
|
||||
form-data: "npm:^4.0.4"
|
||||
proxy-from-env: "npm:^1.1.0"
|
||||
checksum: 10c0/5de273d33d43058610e4d252f0963cc4f10714da0bfe872e8ef2cbc23c2c999acc300fd357b6bce0fc84a2ca9bd45740fa6bb28199ce2c1266c8b1a393f2b36e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"axobject-query@npm:^4.1.0":
|
||||
version: 4.1.0
|
||||
resolution: "axobject-query@npm:4.1.0"
|
||||
@ -7216,6 +7258,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"combined-stream@npm:^1.0.8":
|
||||
version: 1.0.8
|
||||
resolution: "combined-stream@npm:1.0.8"
|
||||
dependencies:
|
||||
delayed-stream: "npm:~1.0.0"
|
||||
checksum: 10c0/0dbb829577e1b1e839fa82b40c07ffaf7de8a09b935cadd355a73652ae70a88b4320db322f6634a4ad93424292fa80973ac6480986247f1734a1137debf271d5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"commander@npm:^2.20.0":
|
||||
version: 2.20.3
|
||||
resolution: "commander@npm:2.20.3"
|
||||
@ -7715,6 +7766,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"delayed-stream@npm:~1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "delayed-stream@npm:1.0.0"
|
||||
checksum: 10c0/d758899da03392e6712f042bec80aa293bbe9e9ff1b2634baae6a360113e708b91326594c8a486d475c69d6259afb7efacdc3537bfcda1c6c648e390ce601b19
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dequal@npm:^2.0.2, dequal@npm:^2.0.3":
|
||||
version: 2.0.3
|
||||
resolution: "dequal@npm:2.0.3"
|
||||
@ -7835,6 +7893,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dom-helpers@npm:^5.0.1":
|
||||
version: 5.2.1
|
||||
resolution: "dom-helpers@npm:5.2.1"
|
||||
dependencies:
|
||||
"@babel/runtime": "npm:^7.8.7"
|
||||
csstype: "npm:^3.0.2"
|
||||
checksum: 10c0/f735074d66dd759b36b158fa26e9d00c9388ee0e8c9b16af941c38f014a37fc80782de83afefd621681b19ac0501034b4f1c4a3bff5caa1b8667f0212b5e124c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dom-serializer@npm:^1.0.1":
|
||||
version: 1.4.1
|
||||
resolution: "dom-serializer@npm:1.4.1"
|
||||
@ -9149,6 +9217,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"follow-redirects@npm:^1.15.6":
|
||||
version: 1.15.9
|
||||
resolution: "follow-redirects@npm:1.15.9"
|
||||
peerDependenciesMeta:
|
||||
debug:
|
||||
optional: true
|
||||
checksum: 10c0/5829165bd112c3c0e82be6c15b1a58fa9dcfaede3b3c54697a82fe4a62dd5ae5e8222956b448d2f98e331525f05d00404aba7d696de9e761ef6e42fdc780244f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"for-each@npm:^0.3.3":
|
||||
version: 0.3.3
|
||||
resolution: "for-each@npm:0.3.3"
|
||||
@ -9191,6 +9269,19 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"form-data@npm:^4.0.4":
|
||||
version: 4.0.4
|
||||
resolution: "form-data@npm:4.0.4"
|
||||
dependencies:
|
||||
asynckit: "npm:^0.4.0"
|
||||
combined-stream: "npm:^1.0.8"
|
||||
es-set-tostringtag: "npm:^2.1.0"
|
||||
hasown: "npm:^2.0.2"
|
||||
mime-types: "npm:^2.1.12"
|
||||
checksum: 10c0/373525a9a034b9d57073e55eab79e501a714ffac02e7a9b01be1c820780652b16e4101819785e1e18f8d98f0aee866cc654d660a435c378e16a72f2e7cac9695
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"framer-motion@npm:^12.23.7":
|
||||
version: 12.23.9
|
||||
resolution: "framer-motion@npm:12.23.9"
|
||||
@ -11633,6 +11724,7 @@ __metadata:
|
||||
"@mantine/core": "npm:8.1.2"
|
||||
"@mantine/form": "npm:^8.1.3"
|
||||
"@mantine/hooks": "npm:8.1.2"
|
||||
"@mantine/notifications": "npm:^8.2.1"
|
||||
"@next/bundle-analyzer": "npm:^15.3.3"
|
||||
"@reduxjs/toolkit": "npm:^2.8.2"
|
||||
"@storybook/nextjs": "npm:^8.6.8"
|
||||
@ -11648,6 +11740,7 @@ __metadata:
|
||||
"@types/react": "npm:19.1.8"
|
||||
"@types/react-redux": "npm:^7.1.34"
|
||||
"@types/redux-persist": "npm:^4.3.1"
|
||||
axios: "npm:^1.11.0"
|
||||
babel-loader: "npm:^10.0.0"
|
||||
classnames: "npm:^2.5.1"
|
||||
eslint: "npm:^9.29.0"
|
||||
@ -11934,7 +12027,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mime-types@npm:^2.1.27, mime-types@npm:^2.1.31":
|
||||
"mime-types@npm:^2.1.12, mime-types@npm:^2.1.27, mime-types@npm:^2.1.31":
|
||||
version: 2.1.35
|
||||
resolution: "mime-types@npm:2.1.35"
|
||||
dependencies:
|
||||
@ -13243,7 +13336,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"prop-types@npm:^15.8.1":
|
||||
"prop-types@npm:^15.6.2, prop-types@npm:^15.8.1":
|
||||
version: 15.8.1
|
||||
resolution: "prop-types@npm:15.8.1"
|
||||
dependencies:
|
||||
@ -13254,6 +13347,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"proxy-from-env@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "proxy-from-env@npm:1.1.0"
|
||||
checksum: 10c0/fe7dd8b1bdbbbea18d1459107729c3e4a2243ca870d26d34c2c1bcd3e4425b7bcc5112362df2d93cc7fb9746f6142b5e272fd1cc5c86ddf8580175186f6ad42b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"public-encrypt@npm:^4.0.0":
|
||||
version: 4.0.3
|
||||
resolution: "public-encrypt@npm:4.0.3"
|
||||
@ -13591,6 +13691,21 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-transition-group@npm:4.4.5":
|
||||
version: 4.4.5
|
||||
resolution: "react-transition-group@npm:4.4.5"
|
||||
dependencies:
|
||||
"@babel/runtime": "npm:^7.5.5"
|
||||
dom-helpers: "npm:^5.0.1"
|
||||
loose-envify: "npm:^1.4.0"
|
||||
prop-types: "npm:^15.6.2"
|
||||
peerDependencies:
|
||||
react: ">=16.6.0"
|
||||
react-dom: ">=16.6.0"
|
||||
checksum: 10c0/2ba754ba748faefa15f87c96dfa700d5525054a0141de8c75763aae6734af0740e77e11261a1e8f4ffc08fd9ab78510122e05c21c2d79066c38bb6861a886c82
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react@npm:19.1.0":
|
||||
version: 19.1.0
|
||||
resolution: "react@npm:19.1.0"
|
||||
|
||||
Reference in New Issue
Block a user