Merge branch 'dev' into 'staging'
Dev See merge request frontend/squiz!326
@ -29,7 +29,6 @@
|
||||
"cytoscape": "^3.26.0",
|
||||
"cytoscape-popper": "^2.0.0",
|
||||
"date-fns": "^3.0.6",
|
||||
"dayjs": "^1.11.10",
|
||||
"emoji-mart": "^5.5.2",
|
||||
"file-saver": "^2.0.5",
|
||||
"formik": "^2.4.5",
|
||||
|
@ -1,12 +1,16 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
<title>Pena Quiz</title>
|
||||
<meta name="description" content="Веб-сервис с инструментами для повышения эффективности маркетологов." />
|
||||
<meta name="keywords" content=" Экосистема маркетинговых инструментов,
|
||||
<title>Pena Quiz</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Веб-сервис с инструментами для повышения эффективности маркетологов."
|
||||
/>
|
||||
<meta
|
||||
name="keywords"
|
||||
content=" Экосистема маркетинговых инструментов,
|
||||
Инструменты для социальных исследований,
|
||||
Малый бизнес,
|
||||
Маркетинговые инструменты,
|
||||
@ -26,82 +30,140 @@
|
||||
Анализ данных,
|
||||
Улучшение результатов,
|
||||
Увеличение прибыли,
|
||||
Повышение конкурентоспособности " />
|
||||
Повышение конкурентоспособности "
|
||||
/>
|
||||
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" sizes="any" /><!-- 32×32 -->
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.svg" type="image/svg+xml" />
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/favicon.png" /><!-- 180×180 -->
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" sizes="any" />
|
||||
<!-- 32×32 -->
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.svg" type="image/svg+xml" />
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/favicon.png" />
|
||||
<!-- 180×180 -->
|
||||
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta http-equiv="Pragma" content="no-cache" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Rubik:wght@400;500;600&display=swap" rel="stylesheet" />
|
||||
<!-- Yandex.Metrika counter -->
|
||||
<script type="text/javascript">
|
||||
(function (m, e, t, r, i, k, a) {
|
||||
m[i] = m[i] || function () { (m[i].a = m[i].a || []).push(arguments) };
|
||||
m[i].l = 1 * new Date();
|
||||
for (var j = 0; j < document.scripts.length; j++) { if (document.scripts[j].src === r) { return; } }
|
||||
k = e.createElement(t), a = e.getElementsByTagName(t)[0], k.async = 1, k.src = r, a.parentNode.insertBefore(k, a)
|
||||
})
|
||||
(window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta http-equiv="Pragma" content="no-cache" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Rubik:wght@400;500;600&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<!-- Yandex.Metrika counter -->
|
||||
<script type="text/javascript">
|
||||
(function (m, e, t, r, i, k, a) {
|
||||
m[i] =
|
||||
m[i] ||
|
||||
function () {
|
||||
(m[i].a = m[i].a || []).push(arguments);
|
||||
};
|
||||
m[i].l = 1 * new Date();
|
||||
for (var j = 0; j < document.scripts.length; j++) {
|
||||
if (document.scripts[j].src === r) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
(k = e.createElement(t)),
|
||||
(a = e.getElementsByTagName(t)[0]),
|
||||
(k.async = 1),
|
||||
(k.src = r),
|
||||
a.parentNode.insertBefore(k, a);
|
||||
})(
|
||||
window,
|
||||
document,
|
||||
"script",
|
||||
"https://mc.yandex.ru/metrika/tag.js",
|
||||
"ym",
|
||||
);
|
||||
|
||||
const domain = location.hostname
|
||||
if (domain === "quiz.pena.digital") {
|
||||
ym(96979576, "init", {
|
||||
clickmap: true,
|
||||
trackLinks: true,
|
||||
accurateTrackBounce: true,
|
||||
webvisor: true
|
||||
});
|
||||
const domain = location.hostname;
|
||||
if (domain === "quiz.pena.digital") {
|
||||
ym(96979576, "init", {
|
||||
clickmap: true,
|
||||
trackLinks: true,
|
||||
accurateTrackBounce: true,
|
||||
webvisor: true,
|
||||
});
|
||||
|
||||
// <!-- Top.Mail.Ru counter -->
|
||||
var _tmr = window._tmr || (window._tmr = []);
|
||||
_tmr.push({ id: "3513005", type: "pageView", start: (new Date()).getTime() });
|
||||
(function (d, w, id) {
|
||||
if (d.getElementById(id)) return;
|
||||
var ts = d.createElement("script"); ts.type = "text/javascript"; ts.async = true; ts.id = id;
|
||||
ts.src = "https://top-fwz1.mail.ru/js/code.js";
|
||||
var f = function () { var s = d.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ts, s); };
|
||||
if (w.opera == "[object Opera]") { d.addEventListener("DOMContentLoaded", f, false); } else { f(); }
|
||||
})(document, window, "tmr-code");
|
||||
// <!-- /Top.Mail.Ru counter -->
|
||||
|
||||
};
|
||||
if (domain === "squiz.pena.digital") {
|
||||
ym(96979625, "init", {
|
||||
clickmap: true,
|
||||
trackLinks: true,
|
||||
accurateTrackBounce: true,
|
||||
webvisor: true
|
||||
});
|
||||
};
|
||||
if (domain === "penaquiz.online" || domain === "penaquiz.ru") {
|
||||
ym(97241101, "init", {
|
||||
clickmap: true,
|
||||
trackLinks: true,
|
||||
accurateTrackBounce: true,
|
||||
webvisor: true
|
||||
});
|
||||
};
|
||||
|
||||
</script>
|
||||
<noscript>
|
||||
<div><img src="https://mc.yandex.ru/watch/96979576" style="position:absolute; left:-9999px;" alt="" /></div>
|
||||
<div><img src="https://mc.yandex.ru/watch/96979625" style="position:absolute; left:-9999px;" alt="" /></div>
|
||||
<div><img src="https://mc.yandex.ru/watch/97241101" style="position:absolute; left:-9999px;" alt="" /></div>
|
||||
<div><img src="https://top-fwz1.mail.ru/counter?id=3513005;js=na" style="position:absolute;left:-9999px;" alt="Top.Mail.Ru" /></div>
|
||||
</noscript>
|
||||
<!-- /Yandex.Metrika counter -->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
// <!-- Top.Mail.Ru counter -->
|
||||
var _tmr = window._tmr || (window._tmr = []);
|
||||
_tmr.push({
|
||||
id: "3513005",
|
||||
type: "pageView",
|
||||
start: new Date().getTime(),
|
||||
});
|
||||
(function (d, w, id) {
|
||||
if (d.getElementById(id)) return;
|
||||
var ts = d.createElement("script");
|
||||
ts.type = "text/javascript";
|
||||
ts.async = true;
|
||||
ts.id = id;
|
||||
ts.src = "https://top-fwz1.mail.ru/js/code.js";
|
||||
var f = function () {
|
||||
var s = d.getElementsByTagName("script")[0];
|
||||
s.parentNode.insertBefore(ts, s);
|
||||
};
|
||||
if (w.opera == "[object Opera]") {
|
||||
d.addEventListener("DOMContentLoaded", f, false);
|
||||
} else {
|
||||
f();
|
||||
}
|
||||
})(document, window, "tmr-code");
|
||||
// <!-- /Top.Mail.Ru counter -->
|
||||
}
|
||||
if (domain === "squiz.pena.digital") {
|
||||
ym(96979625, "init", {
|
||||
clickmap: true,
|
||||
trackLinks: true,
|
||||
accurateTrackBounce: true,
|
||||
webvisor: true,
|
||||
});
|
||||
}
|
||||
if (domain === "penaquiz.online" || domain === "penaquiz.ru") {
|
||||
ym(97241101, "init", {
|
||||
clickmap: true,
|
||||
trackLinks: true,
|
||||
accurateTrackBounce: true,
|
||||
webvisor: true,
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<noscript>
|
||||
<div>
|
||||
<img
|
||||
src="https://mc.yandex.ru/watch/96979576"
|
||||
style="position: absolute; left: -9999px"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<img
|
||||
src="https://mc.yandex.ru/watch/96979625"
|
||||
style="position: absolute; left: -9999px"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<img
|
||||
src="https://mc.yandex.ru/watch/97241101"
|
||||
style="position: absolute; left: -9999px"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<img
|
||||
src="https://top-fwz1.mail.ru/counter?id=3513005;js=na"
|
||||
style="position: absolute; left: -9999px"
|
||||
alt="Top.Mail.Ru"
|
||||
/>
|
||||
</div>
|
||||
</noscript>
|
||||
<!-- /Yandex.Metrika counter -->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
73
src/App.tsx
@ -1,9 +1,7 @@
|
||||
import type { SuspenseProps } from "react";
|
||||
import { lazy, Suspense, useEffect, useLayoutEffect, useRef } from "react";
|
||||
import { lazy, Suspense } from "react";
|
||||
import { lazily } from "react-lazily";
|
||||
import ContactFormModal from "@ui_kit/ContactForm";
|
||||
import dayjs from "dayjs";
|
||||
import "dayjs/locale/ru";
|
||||
import SigninDialog from "./pages/auth/Signin";
|
||||
import SignupDialog from "./pages/auth/Signup";
|
||||
import {
|
||||
@ -18,13 +16,10 @@ import Landing from "./pages/Landing/Landing";
|
||||
import Main from "./pages/main";
|
||||
import {
|
||||
clearAuthToken,
|
||||
createUserAccount,
|
||||
devlog,
|
||||
getMessageFromFetchError,
|
||||
UserAccount,
|
||||
useUserFetcher,
|
||||
} from "@frontend/kitui";
|
||||
import makeRequest from "@api/makeRequest";
|
||||
import type { OriginalUserAccount } from "@root/user";
|
||||
import {
|
||||
clearUserData,
|
||||
@ -44,9 +39,11 @@ import RecoverPassword from "./pages/auth/RecoverPassword";
|
||||
import { InfoPrivilege } from "./pages/InfoPrivilege";
|
||||
import OutdatedLink from "./pages/auth/OutdatedLink";
|
||||
import { useAfterpay } from "@utils/hooks/useAfterpay";
|
||||
import { useUserAccountFetcher } from "@utils/hooks/useUserAccountFetcher";
|
||||
|
||||
const MyQuizzesFull = lazy(() => import("./pages/createQuize/MyQuizzesFull"));
|
||||
|
||||
const QuizGallery = lazy(() => import("./pages/createQuize/QuizGallery"));
|
||||
const ViewPage = lazy(() => import("./pages/ViewPublicationPage"));
|
||||
const Analytics = lazy(() => import("./pages/Analytics/Analytics"));
|
||||
const EditPage = lazy(() => import("./pages/startPage/EditPage"));
|
||||
@ -62,8 +59,6 @@ const ChatImageNewWindow = lazy(
|
||||
() => import("@ui_kit/FloatingSupportChat/ChatImageNewWindow"),
|
||||
);
|
||||
|
||||
dayjs.locale("ru");
|
||||
|
||||
const routeslink = [
|
||||
{
|
||||
path: "/edit",
|
||||
@ -92,65 +87,13 @@ const LazyLoading = ({ children, fallback }: SuspenseProps) => (
|
||||
<Suspense fallback={fallback ?? <></>}>{children}</Suspense>
|
||||
);
|
||||
|
||||
export function useUserAccountFetcher<T = UserAccount>({
|
||||
onError,
|
||||
onNewUserAccount,
|
||||
url,
|
||||
userId,
|
||||
}: {
|
||||
url: string;
|
||||
userId: string | null;
|
||||
onNewUserAccount: (response: T) => void;
|
||||
onError?: (error: any) => void;
|
||||
}) {
|
||||
const onNewUserAccountRef = useRef(onNewUserAccount);
|
||||
const onErrorRef = useRef(onError);
|
||||
useLayoutEffect(() => {
|
||||
onNewUserAccountRef.current = onNewUserAccount;
|
||||
onErrorRef.current = onError;
|
||||
}, [onError, onNewUserAccount]);
|
||||
useEffect(() => {
|
||||
if (!userId) return;
|
||||
const controller = new AbortController();
|
||||
makeRequest<never, T>({
|
||||
url,
|
||||
contentType: true,
|
||||
method: "GET",
|
||||
useToken: true,
|
||||
withCredentials: false,
|
||||
signal: controller.signal,
|
||||
})
|
||||
.then((result) => {
|
||||
devlog("User account", result);
|
||||
onNewUserAccountRef.current(result);
|
||||
})
|
||||
.catch((error) => {
|
||||
devlog("Error fetching user account", error);
|
||||
if (isAxiosError(error) && error.response?.status === 404) {
|
||||
createUserAccount(controller.signal, url.replace("get", "create"))
|
||||
.then((result) => {
|
||||
devlog("Created user account", result);
|
||||
onNewUserAccountRef.current(result as T);
|
||||
})
|
||||
.catch((error) => {
|
||||
devlog("Error creating user account", error);
|
||||
onErrorRef.current?.(error);
|
||||
});
|
||||
} else {
|
||||
onErrorRef.current?.(error);
|
||||
}
|
||||
});
|
||||
return () => controller.abort();
|
||||
}, [url, userId]);
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
const userId = useUserStore((state) => state.userId);
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useUserFetcher({
|
||||
url: process.env.REACT_APP_DOMAIN + `/user/${userId}`,
|
||||
url: `${process.env.REACT_APP_DOMAIN}/user/${userId}`,
|
||||
userId,
|
||||
onNewUser: setUser,
|
||||
onError: (error) => {
|
||||
@ -164,7 +107,7 @@ export default function App() {
|
||||
});
|
||||
|
||||
useUserAccountFetcher<UserAccount>({
|
||||
url: process.env.REACT_APP_DOMAIN + "/customer/account",
|
||||
url: `${process.env.REACT_APP_DOMAIN}/customer/account`,
|
||||
userId,
|
||||
onNewUserAccount: setCustomerAccount,
|
||||
onError: (error) => {
|
||||
@ -179,7 +122,7 @@ export default function App() {
|
||||
});
|
||||
|
||||
useUserAccountFetcher<OriginalUserAccount>({
|
||||
url: process.env.REACT_APP_DOMAIN + "/squiz/account/get",
|
||||
url: `${process.env.REACT_APP_DOMAIN}/squiz/account/get`,
|
||||
userId,
|
||||
onNewUserAccount: setUserAccount,
|
||||
onError: (error) => {
|
||||
@ -259,6 +202,10 @@ export default function App() {
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/gallery"
|
||||
element={<LazyLoading children={<QuizGallery />} />}
|
||||
/>
|
||||
<Route
|
||||
path="/list"
|
||||
element={<LazyLoading children={<MyQuizzesFull />} />}
|
||||
|
@ -1,4 +1,6 @@
|
||||
import makeRequest from "@api/makeRequest";
|
||||
import { makeRequest } from "@api/makeRequest";
|
||||
|
||||
import { parseAxiosError } from "@utils/parse-error";
|
||||
|
||||
import type {
|
||||
LoginRequest,
|
||||
@ -6,21 +8,24 @@ import type {
|
||||
RegisterRequest,
|
||||
RegisterResponse,
|
||||
} from "@frontend/kitui";
|
||||
import { parseAxiosError } from "../utils/parse-error";
|
||||
|
||||
const apiUrl = process.env.REACT_APP_DOMAIN + "/auth";
|
||||
type RecoverResponse = {
|
||||
message: string;
|
||||
};
|
||||
|
||||
export async function register(
|
||||
const API_URL = `${process.env.REACT_APP_DOMAIN}/auth`;
|
||||
|
||||
export const register = async (
|
||||
login: string,
|
||||
password: string,
|
||||
phoneNumber: string,
|
||||
): Promise<[RegisterResponse | null, string?]> {
|
||||
): Promise<[RegisterResponse | null, string?]> => {
|
||||
try {
|
||||
const registerResponse = await makeRequest<
|
||||
RegisterRequest,
|
||||
RegisterResponse
|
||||
>({
|
||||
url: apiUrl + "/register",
|
||||
url: `${API_URL}/register`,
|
||||
body: { login, password, phoneNumber },
|
||||
useToken: false,
|
||||
withCredentials: true,
|
||||
@ -32,15 +37,15 @@ export async function register(
|
||||
|
||||
return [null, `Не удалось зарегестрировать аккаунт. ${error}`];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export async function login(
|
||||
export const login = async (
|
||||
login: string,
|
||||
password: string,
|
||||
): Promise<[LoginResponse | null, string?]> {
|
||||
): Promise<[LoginResponse | null, string?]> => {
|
||||
try {
|
||||
const loginResponse = await makeRequest<LoginRequest, LoginResponse>({
|
||||
url: apiUrl + "/login",
|
||||
url: `${API_URL}/login`,
|
||||
body: { login, password },
|
||||
useToken: false,
|
||||
withCredentials: true,
|
||||
@ -52,13 +57,13 @@ export async function login(
|
||||
|
||||
return [null, `Не удалось войти. ${error}`];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export async function logout(): Promise<[unknown, string?]> {
|
||||
export const logout = async (): Promise<[void | null, string?]> => {
|
||||
try {
|
||||
const logoutResponse = await makeRequest<never, void>({
|
||||
url: apiUrl + "/logout",
|
||||
method: "POST",
|
||||
url: `${API_URL}/logout`,
|
||||
useToken: true,
|
||||
withCredentials: true,
|
||||
});
|
||||
@ -67,30 +72,32 @@ export async function logout(): Promise<[unknown, string?]> {
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null];
|
||||
return [null, `Не удалось выйти. ${error}`];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export async function recover(
|
||||
export const recover = async (
|
||||
email: string,
|
||||
): Promise<[unknown | null, string?]> {
|
||||
): Promise<[RecoverResponse | null, string?]> => {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append("email", email);
|
||||
formData.append(
|
||||
"RedirectionURL",
|
||||
process.env.REACT_APP_DOMAIN + "/changepwd",
|
||||
`${process.env.REACT_APP_DOMAIN}/changepwd`,
|
||||
);
|
||||
const recoverResponse = await makeRequest<unknown, unknown>({
|
||||
url: process.env.REACT_APP_DOMAIN + "/codeword/recover",
|
||||
|
||||
const recoverResponse = await makeRequest<FormData, RecoverResponse>({
|
||||
url: `${process.env.REACT_APP_DOMAIN}/codeword/recover`,
|
||||
body: formData,
|
||||
useToken: false,
|
||||
withCredentials: true,
|
||||
});
|
||||
|
||||
return [recoverResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось восстановить пароль. ${error}`];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,15 +1,16 @@
|
||||
import { UserAccount } from "@frontend/kitui";
|
||||
import makeRequest from "@api/makeRequest";
|
||||
import { makeRequest } from "@api/makeRequest";
|
||||
|
||||
import { parseAxiosError } from "@utils/parse-error";
|
||||
|
||||
const apiUrl = process.env.REACT_APP_DOMAIN + "/customer";
|
||||
import type { UserAccount } from "@frontend/kitui";
|
||||
|
||||
export async function payCart(): Promise<[UserAccount | null, string?]> {
|
||||
const API_URL = `${process.env.REACT_APP_DOMAIN}/customer/cart`;
|
||||
|
||||
const payCart = async (): Promise<[UserAccount | null, string?]> => {
|
||||
try {
|
||||
const payCartResponse = await makeRequest<never, UserAccount>({
|
||||
url: apiUrl + "/cart/pay",
|
||||
method: "POST",
|
||||
url: `${API_URL}/pay`,
|
||||
useToken: true,
|
||||
});
|
||||
|
||||
@ -19,4 +20,44 @@ export async function payCart(): Promise<[UserAccount | null, string?]> {
|
||||
|
||||
return [null, `Не удалось оплатить товар из корзины. ${error}`];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const addCartItem = async (
|
||||
id: string,
|
||||
): Promise<[UserAccount | null, string?]> => {
|
||||
try {
|
||||
const addedItem = await makeRequest<never, UserAccount>({
|
||||
method: "PATCH",
|
||||
url: `${API_URL}?id=${id}`,
|
||||
});
|
||||
|
||||
return [addedItem];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось добавить товар в корзину. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
const deleteCartItem = async (
|
||||
id: string,
|
||||
): Promise<[UserAccount | null, string?]> => {
|
||||
try {
|
||||
const deletedItem = await makeRequest<never, UserAccount>({
|
||||
method: "DELETE",
|
||||
url: `${API_URL}?id=${id}`,
|
||||
});
|
||||
|
||||
return [deletedItem];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось удалить товар из корзины. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
export const cartApi = {
|
||||
pay: payCart,
|
||||
add: addCartItem,
|
||||
delete: deleteCartItem,
|
||||
};
|
||||
|
@ -1,16 +1,31 @@
|
||||
import axios from "axios";
|
||||
import { makeRequest } from "@api/makeRequest";
|
||||
|
||||
const domen = process.env.REACT_APP_DOMAIN;
|
||||
import { parseAxiosError } from "@utils/parse-error";
|
||||
|
||||
export function sendContactFormRequest(body: {
|
||||
const API_URL = `${process.env.REACT_APP_DOMAIN}/feedback`;
|
||||
|
||||
type SendContactFormBody = {
|
||||
contact: string;
|
||||
whoami: string;
|
||||
}) {
|
||||
return axios(`${domen}/feedback/callme`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
data: body,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const sendContactFormRequest = async (
|
||||
body: SendContactFormBody,
|
||||
): Promise<[unknown | null, string?, number?]> => {
|
||||
try {
|
||||
const sendContactFormResponse = await makeRequest<
|
||||
SendContactFormBody,
|
||||
unknown
|
||||
>({
|
||||
method: "POST",
|
||||
url: `${API_URL}/callme`,
|
||||
body,
|
||||
});
|
||||
|
||||
return [sendContactFormResponse];
|
||||
} catch (nativeError) {
|
||||
const [error, status] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось отправить контакты. ${error}`, status];
|
||||
}
|
||||
};
|
||||
|
@ -1,17 +1,19 @@
|
||||
import { makeRequest } from "@frontend/kitui";
|
||||
|
||||
import { parseAxiosError } from "@utils/parse-error";
|
||||
|
||||
import type { Discount } from "@model/discounts";
|
||||
|
||||
const API_URL = process.env.REACT_APP_DOMAIN + "/price/discount";
|
||||
const API_URL = `${process.env.REACT_APP_DOMAIN}/price/discount`;
|
||||
|
||||
export async function getDiscounts(
|
||||
export const getDiscounts = async (
|
||||
userId: string,
|
||||
): Promise<[Discount[] | null, string?]> {
|
||||
): Promise<[Discount[] | null, string?]> => {
|
||||
try {
|
||||
const { Discounts } = await makeRequest<unknown, { Discounts: Discount[] }>(
|
||||
{ method: "GET", url: `${API_URL}/user/${userId}` },
|
||||
);
|
||||
const { Discounts } = await makeRequest<never, { Discounts: Discount[] }>({
|
||||
method: "GET",
|
||||
url: `${API_URL}/user/${userId}`,
|
||||
});
|
||||
|
||||
return [Discounts];
|
||||
} catch (nativeError) {
|
||||
@ -19,4 +21,4 @@ export async function getDiscounts(
|
||||
|
||||
return [null, `Не удалось получить скидки. ${error}`];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
332
src/api/integration.ts
Normal file
@ -0,0 +1,332 @@
|
||||
import { makeRequest } from "@api/makeRequest";
|
||||
import { parseAxiosError } from "@utils/parse-error";
|
||||
|
||||
export type PaginationRequest = {
|
||||
page: number;
|
||||
size: number;
|
||||
};
|
||||
|
||||
const API_URL = `${process.env.REACT_APP_DOMAIN}/squiz/amocrm`;
|
||||
|
||||
// получение информации об аккаунте
|
||||
|
||||
export type AccountResponse = {
|
||||
ID: number;
|
||||
AccountID: string;
|
||||
AmoID: number;
|
||||
Name: string;
|
||||
Email: string;
|
||||
Role: string;
|
||||
Group: number;
|
||||
Deleted: boolean;
|
||||
CreatedAt: number;
|
||||
Subdomain: string;
|
||||
Amoiserid: number;
|
||||
Country: string;
|
||||
};
|
||||
|
||||
export const getAccount = async (): Promise<
|
||||
[AccountResponse | null, string?]
|
||||
> => {
|
||||
try {
|
||||
const response = await makeRequest<void, AccountResponse>({
|
||||
method: "GET",
|
||||
url: `${API_URL}/account`,
|
||||
useToken: true,
|
||||
});
|
||||
return [response];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
return [null, `Не удалось получить информацию об аккаунте. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
// подключить Amo
|
||||
|
||||
export const connectAmo = async (): Promise<[string | null, string?]> => {
|
||||
try {
|
||||
const response = await makeRequest<void, { link: string }>({
|
||||
method: "POST",
|
||||
url: `${API_URL}/account`,
|
||||
useToken: true,
|
||||
withCredentials: true,
|
||||
});
|
||||
return [response.link];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
return [null, `Не удалось подключить аккаунт. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
// получение токена
|
||||
|
||||
export type TokenPair = {
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
};
|
||||
|
||||
export const getTokens = async (): Promise<[TokenPair | null, string?]> => {
|
||||
try {
|
||||
const response = await makeRequest<void, TokenPair>({
|
||||
method: "GET",
|
||||
url: `${API_URL}/webhook/create`,
|
||||
useToken: true,
|
||||
});
|
||||
return [response];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
return [null, `Failed to get tokens. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
//получение списка тегов
|
||||
|
||||
export type Tag = {
|
||||
ID: number;
|
||||
AmoID: number;
|
||||
AccountID: number;
|
||||
Entity: string;
|
||||
Name: string;
|
||||
Color: string;
|
||||
Deleted: boolean;
|
||||
CreatedAt: number;
|
||||
};
|
||||
|
||||
export type TagsResponse = {
|
||||
count: number;
|
||||
items: Tag[];
|
||||
};
|
||||
|
||||
export const getTags = async ({
|
||||
page,
|
||||
size,
|
||||
}: PaginationRequest): Promise<[TagsResponse | null, string?]> => {
|
||||
try {
|
||||
const tagsResponse = await makeRequest<PaginationRequest, TagsResponse>({
|
||||
method: "GET",
|
||||
url: `${API_URL}/tags?page=${page}&size=${size}`,
|
||||
});
|
||||
return [tagsResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
return [null, `Не удалось получить список тегов. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
//получение списка пользователей
|
||||
|
||||
export type User = {
|
||||
ID: number;
|
||||
AccountID: string;
|
||||
AmoID: number;
|
||||
Name: string;
|
||||
Email: string;
|
||||
Role: string;
|
||||
Group: number;
|
||||
Deleted: boolean;
|
||||
CreatedAt: number;
|
||||
Subdomain: string;
|
||||
Amoiserid: number;
|
||||
Country: string;
|
||||
};
|
||||
|
||||
export type UsersResponse = {
|
||||
count: number;
|
||||
items: User[];
|
||||
};
|
||||
|
||||
export const getUsers = async ({
|
||||
page,
|
||||
size,
|
||||
}: PaginationRequest): Promise<[UsersResponse | null, string?]> => {
|
||||
try {
|
||||
const usersResponse = await makeRequest<PaginationRequest, UsersResponse>({
|
||||
method: "GET",
|
||||
url: `${API_URL}/users?page=${page}&size=${size}`,
|
||||
});
|
||||
return [usersResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
return [null, `Не удалось получить список пользователей. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
//получение списка шагов
|
||||
|
||||
export type Step = {
|
||||
ID: number;
|
||||
AmoID: number;
|
||||
PipelineID: number;
|
||||
AccountID: number;
|
||||
Name: string;
|
||||
Color: string;
|
||||
Deleted: boolean;
|
||||
CreatedAt: number;
|
||||
};
|
||||
|
||||
export type StepsResponse = {
|
||||
count: number;
|
||||
items: Step[];
|
||||
};
|
||||
|
||||
export const getSteps = async ({
|
||||
page,
|
||||
size,
|
||||
pipelineId,
|
||||
}: PaginationRequest & { pipelineId: number }): Promise<
|
||||
[StepsResponse | null, string?]
|
||||
> => {
|
||||
try {
|
||||
const stepsResponse = await makeRequest<
|
||||
PaginationRequest & { pipelineId: number },
|
||||
StepsResponse
|
||||
>({
|
||||
method: "GET",
|
||||
url: `${API_URL}/steps?page=${page}&size=${size}&pipelineID=${pipelineId}`,
|
||||
});
|
||||
return [stepsResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
return [null, `Не удалось получить список шагов. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
//получение списка воронок
|
||||
|
||||
export type Pipeline = {
|
||||
ID: number;
|
||||
AmoID: number;
|
||||
AccountID: number;
|
||||
Name: string;
|
||||
IsArchive: boolean;
|
||||
Deleted: boolean;
|
||||
CreatedAt: number;
|
||||
};
|
||||
|
||||
export type PipelinesResponse = {
|
||||
count: number;
|
||||
items: Pipeline[];
|
||||
};
|
||||
|
||||
export const getPipelines = async ({
|
||||
page,
|
||||
size,
|
||||
}: PaginationRequest): Promise<[PipelinesResponse | null, string?]> => {
|
||||
try {
|
||||
const pipelinesResponse = await makeRequest<
|
||||
PaginationRequest,
|
||||
PipelinesResponse
|
||||
>({
|
||||
method: "GET",
|
||||
url: `${API_URL}/pipelines?page=${page}&size=${size}`,
|
||||
});
|
||||
return [pipelinesResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
return [null, `Не удалось получить список воронок. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
//получение настроек интеграции
|
||||
|
||||
export type IntegrationRules = {
|
||||
ID: number;
|
||||
AccountID: number;
|
||||
QuizID: number;
|
||||
PerformerID: number;
|
||||
PipelineID: number;
|
||||
StepID: number;
|
||||
UTMs: number[];
|
||||
FieldsRule: {
|
||||
lead: { QuestionID: number }[];
|
||||
contact: { ContactRuleMap: string }[];
|
||||
company: { QuestionID: number }[];
|
||||
customer: { QuestionID: number }[];
|
||||
};
|
||||
Deleted: boolean;
|
||||
CreatedAt: number;
|
||||
};
|
||||
|
||||
export const getIntegrationRules = async (
|
||||
quizID: string,
|
||||
): Promise<[IntegrationRules | null, string?]> => {
|
||||
try {
|
||||
const settingsResponse = await makeRequest<void, IntegrationRules>({
|
||||
method: "GET",
|
||||
url: `${API_URL}/rules/${quizID}`,
|
||||
});
|
||||
return [settingsResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
return [null, `Не удалось получить настройки интеграции. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
//обновление настроек интеграции
|
||||
|
||||
export type IntegrationRulesUpdate = {
|
||||
PerformerID: number;
|
||||
PipelineID: number;
|
||||
StepID: number;
|
||||
Utms: number[];
|
||||
Fieldsrule: {
|
||||
Lead: { QuestionID: number }[];
|
||||
Contact: { ContactRuleMap: string }[];
|
||||
Company: { QuestionID: number }[];
|
||||
Customer: { QuestionID: number }[];
|
||||
};
|
||||
};
|
||||
|
||||
export const updateIntegrationRules = async (
|
||||
quizID: string,
|
||||
settings: IntegrationRulesUpdate,
|
||||
): Promise<[string | null, string?]> => {
|
||||
try {
|
||||
const updateResponse = await makeRequest<IntegrationRulesUpdate, string>({
|
||||
method: "PATCH",
|
||||
url: `${API_URL}/rules/${quizID}`,
|
||||
body: settings,
|
||||
});
|
||||
return [updateResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
return [null, `Failed to update integration settings. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
// Получение кастомных полей
|
||||
|
||||
export type CustomField = {
|
||||
ID: number;
|
||||
AmoID: number;
|
||||
Code: string;
|
||||
AccountID: number;
|
||||
Name: string;
|
||||
EntityType: string;
|
||||
Type: string;
|
||||
Deleted: boolean;
|
||||
CreatedAt: number;
|
||||
};
|
||||
|
||||
export type CustomFieldsResponse = {
|
||||
count: number;
|
||||
items: CustomField[];
|
||||
};
|
||||
|
||||
export const getCustomFields = async (
|
||||
pagination: PaginationRequest,
|
||||
): Promise<[CustomFieldsResponse | null, string?]> => {
|
||||
try {
|
||||
const fieldsResponse = await makeRequest<
|
||||
PaginationRequest,
|
||||
CustomFieldsResponse
|
||||
>({
|
||||
method: "GET",
|
||||
url: `${API_URL}/fields?page=${pagination.page}&size=${pagination.size}`,
|
||||
});
|
||||
return [fieldsResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
return [null, `Не удалось получить список кастомных полей. ${error}`];
|
||||
}
|
||||
};
|
@ -1,10 +1,13 @@
|
||||
import * as KIT from "@frontend/kitui";
|
||||
import { Method, ResponseType, AxiosError } from "axios";
|
||||
import { redirect } from "react-router-dom";
|
||||
import { clearAuthToken } from "@frontend/kitui";
|
||||
|
||||
import { cleanAuthTicketData } from "@root/ticket";
|
||||
import { clearUserData } from "@root/user";
|
||||
import { clearQuizData } from "@root/quizes/store";
|
||||
import { redirect } from "react-router-dom";
|
||||
|
||||
import type { AxiosResponse } from "axios";
|
||||
|
||||
interface MakeRequest {
|
||||
method?: Method | undefined;
|
||||
@ -17,18 +20,22 @@ interface MakeRequest {
|
||||
withCredentials?: boolean | undefined;
|
||||
}
|
||||
|
||||
async function makeRequest<TRequest = unknown, TResponse = unknown>(
|
||||
data: MakeRequest,
|
||||
): Promise<TResponse> {
|
||||
try {
|
||||
const response = await KIT.makeRequest<unknown>(data);
|
||||
type ExtendedAxiosResponse = AxiosResponse & { message: string };
|
||||
|
||||
export const makeRequest = async <TRequest = unknown, TResponse = unknown>(
|
||||
data: MakeRequest,
|
||||
): Promise<TResponse> => {
|
||||
try {
|
||||
const response = await KIT.makeRequest<unknown, TResponse>(data);
|
||||
|
||||
return response;
|
||||
} catch (nativeError) {
|
||||
const error = nativeError as AxiosError;
|
||||
|
||||
return response as TResponse;
|
||||
} catch (e) {
|
||||
const error = e as AxiosError;
|
||||
if (
|
||||
error.response?.status === 400 &&
|
||||
error.response?.data?.message === "refreshToken is empty"
|
||||
(error.response?.data as ExtendedAxiosResponse)?.message ===
|
||||
"refreshToken is empty"
|
||||
) {
|
||||
cleanAuthTicketData();
|
||||
clearAuthToken();
|
||||
@ -36,7 +43,7 @@ async function makeRequest<TRequest = unknown, TResponse = unknown>(
|
||||
clearQuizData();
|
||||
redirect("/");
|
||||
}
|
||||
throw e;
|
||||
|
||||
throw nativeError;
|
||||
}
|
||||
}
|
||||
export default makeRequest;
|
||||
};
|
||||
|
@ -1,25 +1,30 @@
|
||||
import makeRequest from "@api/makeRequest";
|
||||
import { makeRequest } from "@api/makeRequest";
|
||||
|
||||
import { parseAxiosError } from "@utils/parse-error";
|
||||
|
||||
const apiUrl = process.env.REACT_APP_DOMAIN + "/codeword/promocode";
|
||||
type ActivatePromocodeRequest = { codeword: string } | { fastLink: string };
|
||||
type ActivatePromocodeResponse = { greetings: string };
|
||||
|
||||
export async function activatePromocode(promocode: string) {
|
||||
const API_URL = `${process.env.REACT_APP_DOMAIN}/codeword/promocode`;
|
||||
|
||||
export const activatePromocode = async (
|
||||
promocode: string,
|
||||
): Promise<[string | null, string?]> => {
|
||||
try {
|
||||
const response = await makeRequest<
|
||||
{ codeword: string } | { fastLink: string },
|
||||
{ greetings: string }
|
||||
ActivatePromocodeRequest,
|
||||
ActivatePromocodeResponse
|
||||
>({
|
||||
url: apiUrl + "/activate",
|
||||
method: "POST",
|
||||
contentType: true,
|
||||
url: `${API_URL}/activate`,
|
||||
body: { codeword: promocode },
|
||||
contentType: true,
|
||||
});
|
||||
|
||||
return response.greetings;
|
||||
return [response.greetings];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
throw new Error(error);
|
||||
|
||||
return [null, `Ошибка при активации промокода. ${error}`];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,84 +1,155 @@
|
||||
import makeRequest from "@api/makeRequest";
|
||||
import { CreateQuestionRequest } from "model/question/create";
|
||||
import { RawQuestion } from "model/question/question";
|
||||
import {
|
||||
import { makeRequest } from "@api/makeRequest";
|
||||
|
||||
import { replaceSpacesToEmptyLines } from "@utils/replaceSpacesToEmptyLines";
|
||||
import { parseAxiosError } from "@utils/parse-error";
|
||||
|
||||
import type { CreateQuestionRequest } from "model/question/create";
|
||||
import type { RawQuestion } from "model/question/question";
|
||||
import type {
|
||||
GetQuestionListRequest,
|
||||
GetQuestionListResponse,
|
||||
} from "@model/question/getList";
|
||||
import {
|
||||
import type {
|
||||
EditQuestionRequest,
|
||||
EditQuestionResponse,
|
||||
} from "@model/question/edit";
|
||||
import {
|
||||
import type {
|
||||
DeleteQuestionRequest,
|
||||
DeleteQuestionResponse,
|
||||
} from "@model/question/delete";
|
||||
import {
|
||||
import type {
|
||||
CopyQuestionRequest,
|
||||
CopyQuestionResponse,
|
||||
} from "@model/question/copy";
|
||||
import { replaceSpacesToEmptyLines } from "../utils/replaceSpacesToEmptyLines";
|
||||
|
||||
const baseUrl = process.env.REACT_APP_DOMAIN + "/squiz";
|
||||
const API_URL = `${process.env.REACT_APP_DOMAIN}/squiz`;
|
||||
|
||||
function createQuestion(body: CreateQuestionRequest) {
|
||||
return makeRequest<CreateQuestionRequest, RawQuestion>({
|
||||
url: `${baseUrl}/question/create`,
|
||||
body,
|
||||
method: "POST",
|
||||
});
|
||||
}
|
||||
export const createQuestion = async (
|
||||
body: CreateQuestionRequest,
|
||||
): Promise<[RawQuestion | null, string?]> => {
|
||||
try {
|
||||
const createdQuestion = await makeRequest<
|
||||
CreateQuestionRequest,
|
||||
RawQuestion
|
||||
>({
|
||||
method: "POST",
|
||||
url: `${API_URL}/question/create`,
|
||||
body,
|
||||
});
|
||||
|
||||
async function getQuestionList(body?: Partial<GetQuestionListRequest>) {
|
||||
if (!body?.quiz_id) return null;
|
||||
return [createdQuestion];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
const response = await makeRequest<
|
||||
GetQuestionListRequest,
|
||||
GetQuestionListResponse
|
||||
>({
|
||||
url: `${baseUrl}/question/getList`,
|
||||
body: { ...defaultGetQuestionListBody, ...body },
|
||||
method: "POST",
|
||||
});
|
||||
const clearArrayFromEmptySpaceBlaBlaValue = response.items?.map(
|
||||
(question) => {
|
||||
let data = question;
|
||||
for (let key in question) {
|
||||
const k = key as keyof RawQuestion;
|
||||
//@ts-ignore
|
||||
if (question[key] === " ") data[key] = "";
|
||||
}
|
||||
return data;
|
||||
},
|
||||
);
|
||||
return [null, `Не удалось создать вопрос. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
return replaceSpacesToEmptyLines(clearArrayFromEmptySpaceBlaBlaValue);
|
||||
}
|
||||
const getQuestionList = async (
|
||||
body?: Partial<GetQuestionListRequest>,
|
||||
): Promise<[RawQuestion[] | null, string?]> => {
|
||||
try {
|
||||
if (!body?.quiz_id) return [null, "Квиз не найден"];
|
||||
|
||||
function editQuestion(body: EditQuestionRequest, signal?: AbortSignal) {
|
||||
return makeRequest<EditQuestionRequest, EditQuestionResponse>({
|
||||
url: `${baseUrl}/question/edit`,
|
||||
body,
|
||||
method: "PATCH",
|
||||
signal,
|
||||
});
|
||||
}
|
||||
const response = await makeRequest<
|
||||
GetQuestionListRequest,
|
||||
GetQuestionListResponse
|
||||
>({
|
||||
method: "POST",
|
||||
url: `${API_URL}/question/getList`,
|
||||
body: { ...defaultGetQuestionListBody, ...body },
|
||||
});
|
||||
|
||||
function copyQuestion(questionId: number, quizId: number) {
|
||||
return makeRequest<CopyQuestionRequest, CopyQuestionResponse>({
|
||||
url: `${baseUrl}/question/copy`,
|
||||
body: { id: questionId, quiz_id: quizId },
|
||||
method: "POST",
|
||||
});
|
||||
}
|
||||
const clearArrayFromEmptySpaceBlaBlaValue = response.items?.map(
|
||||
(question) => {
|
||||
let data = question;
|
||||
|
||||
function deleteQuestion(id: number) {
|
||||
return makeRequest<DeleteQuestionRequest, DeleteQuestionResponse>({
|
||||
url: `${baseUrl}/question/delete`,
|
||||
body: { id },
|
||||
method: "DELETE",
|
||||
});
|
||||
}
|
||||
for (let key in question) {
|
||||
if (question[key as keyof RawQuestion] === " ") {
|
||||
//@ts-ignore
|
||||
data[key] = "";
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
);
|
||||
|
||||
return [
|
||||
replaceSpacesToEmptyLines(clearArrayFromEmptySpaceBlaBlaValue) ?? null,
|
||||
];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось получить список вопросов. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
export const editQuestion = async (
|
||||
body: EditQuestionRequest,
|
||||
signal?: AbortSignal,
|
||||
): Promise<[EditQuestionResponse | null, string?]> => {
|
||||
try {
|
||||
const editedQuestion = await makeRequest<
|
||||
EditQuestionRequest,
|
||||
EditQuestionResponse
|
||||
>({
|
||||
method: "PATCH",
|
||||
url: `${API_URL}/question/edit`,
|
||||
body,
|
||||
signal,
|
||||
});
|
||||
|
||||
return [editedQuestion];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось изменить вопрос. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
export const copyQuestion = async (
|
||||
questionId: number,
|
||||
quizId: number,
|
||||
): Promise<[CopyQuestionResponse | null, string?]> => {
|
||||
try {
|
||||
const copiedQuestion = await makeRequest<
|
||||
CopyQuestionRequest,
|
||||
CopyQuestionResponse
|
||||
>({
|
||||
method: "POST",
|
||||
url: `${API_URL}/question/copy`,
|
||||
body: { id: questionId, quiz_id: quizId },
|
||||
});
|
||||
|
||||
return [copiedQuestion];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось скопировать вопрос. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteQuestion = async (
|
||||
id: number,
|
||||
): Promise<[DeleteQuestionResponse | null, string?]> => {
|
||||
try {
|
||||
const deletedQuestion = await makeRequest<
|
||||
DeleteQuestionRequest,
|
||||
DeleteQuestionResponse
|
||||
>({
|
||||
url: `${API_URL}/question/delete`,
|
||||
body: { id },
|
||||
method: "DELETE",
|
||||
});
|
||||
|
||||
return [deletedQuestion];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось удалить вопрос. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
export const questionApi = {
|
||||
create: createQuestion,
|
||||
|
240
src/api/quiz.ts
@ -1,79 +1,190 @@
|
||||
import makeRequest from "@api/makeRequest";
|
||||
import { makeRequest } from "@api/makeRequest";
|
||||
import { defaultQuizConfig } from "@model/quizSettings";
|
||||
import { CopyQuizRequest, CopyQuizResponse } from "model/quiz/copy";
|
||||
import { CreateQuizRequest } from "model/quiz/create";
|
||||
import { DeleteQuizRequest, DeleteQuizResponse } from "model/quiz/delete";
|
||||
import { EditQuizRequest, EditQuizResponse } from "model/quiz/edit";
|
||||
import { GetQuizRequest, GetQuizResponse } from "model/quiz/get";
|
||||
import { GetQuizListRequest, GetQuizListResponse } from "model/quiz/getList";
|
||||
import { RawQuiz } from "model/quiz/quiz";
|
||||
|
||||
const baseUrl = process.env.REACT_APP_DOMAIN + "/squiz";
|
||||
const imagesUrl = process.env.REACT_APP_DOMAIN + "/squizstorer";
|
||||
import { parseAxiosError } from "@utils/parse-error";
|
||||
|
||||
function createQuiz(body?: Partial<CreateQuizRequest>) {
|
||||
return makeRequest<CreateQuizRequest, RawQuiz>({
|
||||
url: `${baseUrl}/quiz/create`,
|
||||
body: { ...defaultCreateQuizBody, ...body },
|
||||
method: "POST",
|
||||
});
|
||||
}
|
||||
import type { RawQuiz } from "model/quiz/quiz";
|
||||
import type { CopyQuizRequest, CopyQuizResponse } from "model/quiz/copy";
|
||||
import type { CreateQuizRequest } from "model/quiz/create";
|
||||
import type { DeleteQuizRequest, DeleteQuizResponse } from "model/quiz/delete";
|
||||
import type { EditQuizRequest, EditQuizResponse } from "model/quiz/edit";
|
||||
import type { GetQuizRequest, GetQuizResponse } from "model/quiz/get";
|
||||
import type {
|
||||
GetQuizListRequest,
|
||||
GetQuizListResponse,
|
||||
} from "model/quiz/getList";
|
||||
|
||||
async function getQuizList(body?: Partial<GetQuizListRequest>) {
|
||||
const response = await makeRequest<GetQuizListRequest, GetQuizListResponse>({
|
||||
url: `${baseUrl}/quiz/getList`,
|
||||
body: { ...defaultGetQuizListBody, ...body },
|
||||
method: "POST",
|
||||
});
|
||||
type AddedQuizImagesResponse = {
|
||||
[key: string]: string;
|
||||
};
|
||||
|
||||
return response.items;
|
||||
}
|
||||
const API_URL = `${process.env.REACT_APP_DOMAIN}/squiz`;
|
||||
const IMAGES_URL = `${process.env.REACT_APP_DOMAIN}/squizstorer`;
|
||||
|
||||
function getQuiz(body?: Partial<GetQuizRequest>) {
|
||||
return makeRequest<GetQuizRequest, GetQuizResponse>({
|
||||
url: `${baseUrl}/quiz/get`,
|
||||
body: { ...defaultGetQuizBody, ...body },
|
||||
method: "GET",
|
||||
});
|
||||
}
|
||||
export const createQuiz = async (
|
||||
body?: Partial<CreateQuizRequest>,
|
||||
): Promise<[RawQuiz | null, string?]> => {
|
||||
try {
|
||||
const createdQuiz = await makeRequest<CreateQuizRequest, RawQuiz>({
|
||||
method: "POST",
|
||||
url: `${API_URL}/quiz/create`,
|
||||
body: { ...defaultCreateQuizBody, ...body },
|
||||
});
|
||||
|
||||
async function editQuiz(body: EditQuizRequest, signal?: AbortSignal) {
|
||||
return makeRequest<EditQuizRequest, EditQuizResponse>({
|
||||
url: `${baseUrl}/quiz/edit`,
|
||||
body,
|
||||
method: "PATCH",
|
||||
signal,
|
||||
});
|
||||
}
|
||||
return [createdQuiz];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
function copyQuiz(id: number) {
|
||||
return makeRequest<CopyQuizRequest, CopyQuizResponse>({
|
||||
url: `${baseUrl}/quiz/copy`,
|
||||
body: { id },
|
||||
method: "POST",
|
||||
});
|
||||
}
|
||||
return [null, `Не удалось создать квиз. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
function deleteQuiz(id: number) {
|
||||
return makeRequest<DeleteQuizRequest, DeleteQuizResponse>({
|
||||
url: `${baseUrl}/quiz/delete`,
|
||||
body: { id },
|
||||
method: "DELETE",
|
||||
});
|
||||
}
|
||||
export const getQuizList = async (
|
||||
body?: Partial<CreateQuizRequest>,
|
||||
): Promise<[RawQuiz[] | null, string?]> => {
|
||||
try {
|
||||
const { items } = await makeRequest<
|
||||
GetQuizListRequest,
|
||||
GetQuizListResponse
|
||||
>({
|
||||
method: "POST",
|
||||
url: `${API_URL}/quiz/getList`,
|
||||
body: { ...defaultGetQuizListBody, ...body },
|
||||
});
|
||||
|
||||
function addQuizImages(quizId: number, image: Blob) {
|
||||
const formData = new FormData();
|
||||
return [items];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
formData.append("quiz", quizId.toString());
|
||||
formData.append("image", image);
|
||||
return [null, `Не удалось получить список квизов. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
return makeRequest<FormData, { [key: string]: string }>({
|
||||
url: `${imagesUrl}/quiz/putImages`,
|
||||
body: formData,
|
||||
method: "PUT",
|
||||
});
|
||||
}
|
||||
export const getQuiz = async (
|
||||
body?: Partial<GetQuizRequest>,
|
||||
): Promise<[GetQuizResponse | null, string?]> => {
|
||||
try {
|
||||
const quiz = await makeRequest<GetQuizRequest, GetQuizResponse>({
|
||||
method: "GET",
|
||||
url: `${API_URL}/quiz/get`,
|
||||
body: { ...defaultGetQuizBody, ...body },
|
||||
});
|
||||
|
||||
return [quiz];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось получить квиз. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
export const editQuiz = async (
|
||||
body: EditQuizRequest,
|
||||
signal?: AbortSignal,
|
||||
): Promise<[EditQuizResponse | null, string?]> => {
|
||||
try {
|
||||
const editedQuiz = await makeRequest<EditQuizRequest, EditQuizResponse>({
|
||||
method: "PATCH",
|
||||
url: `${API_URL}/quiz/edit`,
|
||||
body,
|
||||
signal,
|
||||
});
|
||||
|
||||
return [editedQuiz];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось изменить квиз. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
export const copyQuiz = async (
|
||||
id: number,
|
||||
): Promise<[EditQuizResponse | null, string?]> => {
|
||||
try {
|
||||
const copiedQuiz = await makeRequest<CopyQuizRequest, CopyQuizResponse>({
|
||||
method: "POST",
|
||||
url: `${API_URL}/quiz/copy`,
|
||||
body: { id },
|
||||
});
|
||||
|
||||
return [copiedQuiz];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось скопировать квиз. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteQuiz = async (
|
||||
id: number,
|
||||
): Promise<[DeleteQuizResponse | null, string?]> => {
|
||||
try {
|
||||
const deletedQuiz = await makeRequest<
|
||||
DeleteQuizRequest,
|
||||
DeleteQuizResponse
|
||||
>({
|
||||
method: "DELETE",
|
||||
url: `${API_URL}/quiz/delete`,
|
||||
body: { id },
|
||||
});
|
||||
|
||||
return [deletedQuiz];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось удалить квиз. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
export const addQuizImages = async (
|
||||
quizId: number,
|
||||
image: Blob,
|
||||
): Promise<[AddedQuizImagesResponse | null, string?]> => {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append("quiz", quizId.toString());
|
||||
formData.append("image", image);
|
||||
|
||||
const addedQuizImages = await makeRequest<
|
||||
FormData,
|
||||
AddedQuizImagesResponse
|
||||
>({
|
||||
url: `${IMAGES_URL}/quiz/putImages`,
|
||||
body: formData,
|
||||
method: "PUT",
|
||||
});
|
||||
|
||||
return [addedQuizImages];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось добавить изображение. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
export const copyQuizTemplate = async (
|
||||
qid: string,
|
||||
): Promise<[number | null, string?]> => {
|
||||
try {
|
||||
const { id } = await makeRequest<{ Qid: string }, { id: number }>({
|
||||
method: "POST",
|
||||
url: `${API_URL}/quiz/template`,
|
||||
body: { Qid: qid },
|
||||
});
|
||||
|
||||
if (!id) {
|
||||
return [null, `Не удалось скопировать шаблон квиза.`];
|
||||
}
|
||||
|
||||
return [id];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось скопировать шаблон квиза. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
export const quizApi = {
|
||||
create: createQuiz,
|
||||
@ -83,6 +194,7 @@ export const quizApi = {
|
||||
copy: copyQuiz,
|
||||
delete: deleteQuiz,
|
||||
addImages: addQuizImages,
|
||||
copyTemplate: copyQuizTemplate,
|
||||
};
|
||||
|
||||
const defaultCreateQuizBody: CreateQuizRequest = {
|
||||
|
@ -1,5 +1,8 @@
|
||||
import makeRequest from "@api/makeRequest";
|
||||
import { RawResult } from "@model/result/result";
|
||||
import { makeRequest } from "@api/makeRequest";
|
||||
|
||||
import { parseAxiosError } from "@utils/parse-error";
|
||||
|
||||
import type { RawResult } from "@model/result/result";
|
||||
|
||||
interface IResultListBody {
|
||||
to: number;
|
||||
@ -29,47 +32,113 @@ export interface IAnswerResult {
|
||||
question_id: number;
|
||||
}
|
||||
|
||||
async function getResultList(quizId: number, page: number, body: any) {
|
||||
return makeRequest<IResultListBody, RawResult>({
|
||||
url: process.env.REACT_APP_DOMAIN + `/squiz/results/getResults/${quizId}`,
|
||||
method: "POST",
|
||||
body: { page: page, limit: 10, ...body },
|
||||
});
|
||||
}
|
||||
type ResultFilter = {
|
||||
from?: string;
|
||||
new?: boolean;
|
||||
to?: string;
|
||||
};
|
||||
|
||||
function deleteResult(resultId: number) {
|
||||
return makeRequest<unknown, unknown>({
|
||||
url: process.env.REACT_APP_DOMAIN + `/squiz/results/delete/${resultId}`,
|
||||
body: {},
|
||||
method: "DELETE",
|
||||
});
|
||||
}
|
||||
type ObsolescenceRequest = {
|
||||
answers: number[];
|
||||
};
|
||||
|
||||
function obsolescenceResult(idResultArray: number[]) {
|
||||
return makeRequest<unknown, unknown>({
|
||||
url: process.env.REACT_APP_DOMAIN + `/squiz/result/seen`,
|
||||
body: {
|
||||
answers: idResultArray,
|
||||
},
|
||||
method: "PATCH",
|
||||
});
|
||||
}
|
||||
const API_URL = `${process.env.REACT_APP_DOMAIN}/squiz`;
|
||||
|
||||
function getAnswerResultList(resultId: number) {
|
||||
return makeRequest<unknown, IAnswerResult[]>({
|
||||
url: process.env.REACT_APP_DOMAIN + `/squiz/result/${resultId}`,
|
||||
method: "GET",
|
||||
});
|
||||
}
|
||||
const getResultList = async (
|
||||
quizId: number,
|
||||
page: number,
|
||||
body: ResultFilter,
|
||||
): Promise<[RawResult | null, string?]> => {
|
||||
try {
|
||||
const resultList = await makeRequest<IResultListBody, RawResult>({
|
||||
method: "POST",
|
||||
url: `${API_URL}/results/getResults/${quizId}`,
|
||||
body: { page: page, limit: 10, ...body },
|
||||
});
|
||||
|
||||
function AnswerResultListEx(quizId: number, body: any) {
|
||||
return makeRequest<unknown, unknown>({
|
||||
responseType: "blob",
|
||||
url: process.env.REACT_APP_DOMAIN + `/squiz/results/${quizId}/export`,
|
||||
method: "POST",
|
||||
body: body,
|
||||
});
|
||||
}
|
||||
return [resultList];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось получить результат. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
const deleteResult = async (
|
||||
resultId: number,
|
||||
): Promise<[string | null, string?]> => {
|
||||
try {
|
||||
const deletedResult = await makeRequest<void, string>({
|
||||
method: "DELETE",
|
||||
url: `${API_URL}/results/delete/${resultId}`,
|
||||
body: {},
|
||||
});
|
||||
|
||||
return [deletedResult];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось удалить результат. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
const obsolescenceResult = async (
|
||||
idResultArray: number[],
|
||||
): Promise<[null, string?]> => {
|
||||
try {
|
||||
const obsolescencedResult = await makeRequest<ObsolescenceRequest, null>({
|
||||
method: "PATCH",
|
||||
url: `${API_URL}/result/seen`,
|
||||
body: { answers: idResultArray },
|
||||
});
|
||||
|
||||
return [obsolescencedResult];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось изменить результат. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
const getAnswerResultList = async (
|
||||
resultId: number,
|
||||
): Promise<[IAnswerResult[] | null, string?]> => {
|
||||
try {
|
||||
const answerResultList = await makeRequest<never, IAnswerResult[]>({
|
||||
method: "GET",
|
||||
url: `${API_URL}/result/${resultId}`,
|
||||
});
|
||||
|
||||
return [answerResultList];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось получить список результатов. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
const AnswerResultListEx = async (
|
||||
quizId: number,
|
||||
body: ResultFilter,
|
||||
): Promise<[Blob | null, string?]> => {
|
||||
try {
|
||||
const answerResultListEx = await makeRequest<ResultFilter, Blob>({
|
||||
method: "POST",
|
||||
url: `${API_URL}/results/${quizId}/export`,
|
||||
body,
|
||||
responseType: "blob",
|
||||
});
|
||||
|
||||
return [answerResultListEx];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [
|
||||
null,
|
||||
`Не удалось получить список устаревших результатов. ${error}`,
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
export const resultApi = {
|
||||
getList: getResultList,
|
||||
|
@ -1,9 +1,7 @@
|
||||
import makeRequest from "@api/makeRequest";
|
||||
import { makeRequest } from "@api/makeRequest";
|
||||
|
||||
import { parseAxiosError } from "@utils/parse-error";
|
||||
|
||||
const apiUrl = process.env.REACT_APP_DOMAIN + "/squiz/statistic";
|
||||
|
||||
export type DevicesResponse = {
|
||||
Device: Record<string, number>;
|
||||
OS: Record<string, number>;
|
||||
@ -29,6 +27,8 @@ type TRequest = {
|
||||
from: number;
|
||||
};
|
||||
|
||||
const API_URL = `${process.env.REACT_APP_DOMAIN}/squiz/statistic`;
|
||||
|
||||
export const getDevices = async (
|
||||
quizId: string,
|
||||
to: number,
|
||||
@ -37,7 +37,7 @@ export const getDevices = async (
|
||||
try {
|
||||
const devicesResponse = await makeRequest<TRequest, DevicesResponse>({
|
||||
method: "POST",
|
||||
url: `${apiUrl}/${quizId}/devices`,
|
||||
url: `${API_URL}/${quizId}/devices`,
|
||||
withCredentials: true,
|
||||
body: { to, from },
|
||||
});
|
||||
@ -58,7 +58,7 @@ export const getGeneral = async (
|
||||
try {
|
||||
const generalResponse = await makeRequest<TRequest, GeneralResponse>({
|
||||
method: "POST",
|
||||
url: `${apiUrl}/${quizId}/general`,
|
||||
url: `${API_URL}/${quizId}/general`,
|
||||
withCredentials: true,
|
||||
body: { to, from },
|
||||
});
|
||||
@ -79,7 +79,7 @@ export const getQuestions = async (
|
||||
try {
|
||||
const questionsResponse = await makeRequest<TRequest, QuestionsResponse>({
|
||||
method: "POST",
|
||||
url: `${apiUrl}/${quizId}/questions`,
|
||||
url: `${API_URL}/${quizId}/questions`,
|
||||
withCredentials: true,
|
||||
body: { to, from },
|
||||
});
|
||||
|
23
src/api/tariff.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { makeRequest } from "@api/makeRequest";
|
||||
|
||||
import { parseAxiosError } from "@utils/parse-error";
|
||||
|
||||
import type { GetTariffsResponse } from "@frontend/kitui";
|
||||
|
||||
const API_URL = `${process.env.REACT_APP_DOMAIN}/strator/tariff`;
|
||||
|
||||
export const getTariffs = async (
|
||||
page: number,
|
||||
): Promise<[GetTariffsResponse | null, string?]> => {
|
||||
try {
|
||||
const tariffs = await makeRequest<never, GetTariffsResponse>({
|
||||
method: "GET",
|
||||
url: `${API_URL}?page=${page}&limit=100`,
|
||||
});
|
||||
return [tariffs];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Ошибка при получении списка тарифов. ${error}`];
|
||||
}
|
||||
};
|
@ -1,20 +1,30 @@
|
||||
import makeRequest from "@api/makeRequest";
|
||||
import { parseAxiosError } from "../utils/parse-error";
|
||||
import { createTicket as createTicketRequest } from "@frontend/kitui";
|
||||
|
||||
import { SendTicketMessageRequest } from "@frontend/kitui";
|
||||
import { makeRequest } from "@api/makeRequest";
|
||||
|
||||
const apiUrl = process.env.REACT_APP_DOMAIN + "/heruvym";
|
||||
import { parseAxiosError } from "@utils/parse-error";
|
||||
|
||||
export async function sendTicketMessage(
|
||||
import type {
|
||||
SendTicketMessageRequest,
|
||||
CreateTicketResponse,
|
||||
} from "@frontend/kitui";
|
||||
|
||||
type SendFileResponse = {
|
||||
message: string;
|
||||
};
|
||||
|
||||
const API_URL = `${process.env.REACT_APP_DOMAIN}/heruvym`;
|
||||
|
||||
export const sendTicketMessage = async (
|
||||
ticketId: string,
|
||||
message: string,
|
||||
): Promise<[null, string?]> {
|
||||
): Promise<[null, string?]> => {
|
||||
try {
|
||||
const sendTicketMessageResponse = await makeRequest<
|
||||
SendTicketMessageRequest,
|
||||
null
|
||||
>({
|
||||
url: `${apiUrl}/send`,
|
||||
url: `${API_URL}/send`,
|
||||
method: "POST",
|
||||
useToken: true,
|
||||
body: { ticket: ticketId, message: message, lang: "ru", files: [] },
|
||||
@ -26,12 +36,12 @@ export async function sendTicketMessage(
|
||||
|
||||
return [null, `Не удалось отправить сообщение. ${error}`];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export async function shownMessage(id: string): Promise<[null, string?]> {
|
||||
export const shownMessage = async (id: string): Promise<[null, string?]> => {
|
||||
try {
|
||||
const shownMessageResponse = await makeRequest<{ id: string }, null>({
|
||||
url: apiUrl + "/shown",
|
||||
url: `${API_URL}/shown`,
|
||||
method: "POST",
|
||||
useToken: true,
|
||||
body: { id },
|
||||
@ -43,4 +53,47 @@ export async function shownMessage(id: string): Promise<[null, string?]> {
|
||||
|
||||
return [null, `Не удалось прочесть сообщение. ${error}`];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const sendFile = async (
|
||||
ticketId: string,
|
||||
file: File,
|
||||
): Promise<[SendFileResponse | null, string?]> => {
|
||||
try {
|
||||
const body = new FormData();
|
||||
|
||||
body.append(file.name, file);
|
||||
body.append("ticket", ticketId);
|
||||
|
||||
const sendResponse = await makeRequest<FormData, SendFileResponse>({
|
||||
method: "POST",
|
||||
url: `${process.env.REACT_APP_DOMAIN}/heruvym/sendFiles`,
|
||||
body,
|
||||
});
|
||||
|
||||
return [sendResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось отправить файл. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
export const createTicket = async (
|
||||
message: string,
|
||||
useToken: boolean,
|
||||
): Promise<[CreateTicketResponse | null, string?]> => {
|
||||
try {
|
||||
const createdTicket = await createTicketRequest({
|
||||
url: `${process.env.REACT_APP_DOMAIN}/heruvym/create`,
|
||||
body: { Title: "Unauth title", Message: message },
|
||||
useToken,
|
||||
});
|
||||
|
||||
return [createdTicket];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось создать тикет. ${error}`];
|
||||
}
|
||||
};
|
||||
|
66
src/api/user.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { makeRequest } from "@api/makeRequest";
|
||||
|
||||
import { parseAxiosError } from "@utils/parse-error";
|
||||
|
||||
import type { UserAccount } from "@frontend/kitui";
|
||||
import type { OriginalUserAccount } from "@root/user";
|
||||
|
||||
type RecoverUserRequest = {
|
||||
password: string;
|
||||
};
|
||||
|
||||
export const getUser = async (): Promise<[UserAccount | null, string?]> => {
|
||||
try {
|
||||
const user = await makeRequest<never, UserAccount>({
|
||||
method: "GET",
|
||||
url: `${process.env.REACT_APP_DOMAIN}/customer/account`,
|
||||
});
|
||||
|
||||
return [user];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось получить пользователя. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
export const getAccount = async (): Promise<
|
||||
[OriginalUserAccount | null, string?]
|
||||
> => {
|
||||
try {
|
||||
const controller = new AbortController();
|
||||
|
||||
const account = await makeRequest<never, OriginalUserAccount>({
|
||||
url: `${process.env.REACT_APP_DOMAIN}/squiz/account/get`,
|
||||
contentType: true,
|
||||
method: "GET",
|
||||
useToken: true,
|
||||
withCredentials: false,
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
return [account];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось получить данные аккаунта. ${error}`];
|
||||
}
|
||||
};
|
||||
|
||||
export const recoverUser = async (
|
||||
password: string,
|
||||
): Promise<[unknown | null, string?]> => {
|
||||
try {
|
||||
const recoverResponse = await makeRequest<RecoverUserRequest, unknown>({
|
||||
url: `${process.env.REACT_APP_DOMAIN}/user`,
|
||||
method: "PATCH",
|
||||
body: { password },
|
||||
});
|
||||
|
||||
return [recoverResponse];
|
||||
} catch (nativeError) {
|
||||
const [error] = parseAxiosError(nativeError);
|
||||
|
||||
return [null, `Не удалось восстановить пароль. ${error}`];
|
||||
}
|
||||
};
|
BIN
src/assets/icons/Amologo.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
@ -4,10 +4,15 @@ interface Props {
|
||||
color?: string;
|
||||
bgcolor?: string;
|
||||
marL?: string;
|
||||
width?: string
|
||||
width?: string;
|
||||
}
|
||||
|
||||
export default function CopyIcon({ color, bgcolor, marL, width = "36px" }: Props) {
|
||||
export default function CopyIcon({
|
||||
color,
|
||||
bgcolor,
|
||||
marL,
|
||||
width = "36px",
|
||||
}: Props) {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
|
Before Width: | Height: | Size: 124 KiB |
Before Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 170 KiB |
Before Width: | Height: | Size: 83 KiB |
Before Width: | Height: | Size: 124 KiB |
Before Width: | Height: | Size: 86 KiB |
BIN
src/assets/quiz-templates/auto/auto-1.jpg
Normal file
After Width: | Height: | Size: 123 KiB |
BIN
src/assets/quiz-templates/auto/auto-10.jpg
Normal file
After Width: | Height: | Size: 1.3 MiB |
BIN
src/assets/quiz-templates/auto/auto-2.jpg
Normal file
After Width: | Height: | Size: 164 KiB |
BIN
src/assets/quiz-templates/auto/auto-3.jpg
Normal file
After Width: | Height: | Size: 861 KiB |
BIN
src/assets/quiz-templates/auto/auto-4.jpg
Normal file
After Width: | Height: | Size: 514 KiB |
BIN
src/assets/quiz-templates/auto/auto-5.jpg
Normal file
After Width: | Height: | Size: 642 KiB |
BIN
src/assets/quiz-templates/auto/auto-6.jpg
Normal file
After Width: | Height: | Size: 667 KiB |
BIN
src/assets/quiz-templates/auto/auto-7.jpg
Normal file
After Width: | Height: | Size: 107 KiB |
BIN
src/assets/quiz-templates/auto/auto-8.jpg
Normal file
After Width: | Height: | Size: 93 KiB |
BIN
src/assets/quiz-templates/auto/auto-9.jpg
Normal file
After Width: | Height: | Size: 600 KiB |
BIN
src/assets/quiz-templates/education/education-1.jpg
Normal file
After Width: | Height: | Size: 618 KiB |
BIN
src/assets/quiz-templates/education/education-10.jpg
Normal file
After Width: | Height: | Size: 442 KiB |
BIN
src/assets/quiz-templates/education/education-2.jpg
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
src/assets/quiz-templates/education/education-3.jpg
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
src/assets/quiz-templates/education/education-4.jpg
Normal file
After Width: | Height: | Size: 264 KiB |
BIN
src/assets/quiz-templates/education/education-5.jpg
Normal file
After Width: | Height: | Size: 600 KiB |
BIN
src/assets/quiz-templates/education/education-6.jpg
Normal file
After Width: | Height: | Size: 295 KiB |
BIN
src/assets/quiz-templates/education/education-7.jpg
Normal file
After Width: | Height: | Size: 645 KiB |
BIN
src/assets/quiz-templates/education/education-8.jpg
Normal file
After Width: | Height: | Size: 847 KiB |
BIN
src/assets/quiz-templates/education/education-9.jpg
Normal file
After Width: | Height: | Size: 746 KiB |
BIN
src/assets/quiz-templates/health/health-1.jpg
Normal file
After Width: | Height: | Size: 431 KiB |
BIN
src/assets/quiz-templates/health/health-10.jpg
Normal file
After Width: | Height: | Size: 752 KiB |
BIN
src/assets/quiz-templates/health/health-11.jpg
Normal file
After Width: | Height: | Size: 424 KiB |
BIN
src/assets/quiz-templates/health/health-12.jpg
Normal file
After Width: | Height: | Size: 584 KiB |
BIN
src/assets/quiz-templates/health/health-13.jpg
Normal file
After Width: | Height: | Size: 205 KiB |
BIN
src/assets/quiz-templates/health/health-14.jpg
Normal file
After Width: | Height: | Size: 517 KiB |
BIN
src/assets/quiz-templates/health/health-15.jpg
Normal file
After Width: | Height: | Size: 442 KiB |
BIN
src/assets/quiz-templates/health/health-16.jpg
Normal file
After Width: | Height: | Size: 209 KiB |
BIN
src/assets/quiz-templates/health/health-17.jpg
Normal file
After Width: | Height: | Size: 719 KiB |
BIN
src/assets/quiz-templates/health/health-18.jpg
Normal file
After Width: | Height: | Size: 132 KiB |
BIN
src/assets/quiz-templates/health/health-19.jpg
Normal file
After Width: | Height: | Size: 870 KiB |
BIN
src/assets/quiz-templates/health/health-2.jpg
Normal file
After Width: | Height: | Size: 516 KiB |
BIN
src/assets/quiz-templates/health/health-20.jpg
Normal file
After Width: | Height: | Size: 522 KiB |
BIN
src/assets/quiz-templates/health/health-3.jpg
Normal file
After Width: | Height: | Size: 310 KiB |
BIN
src/assets/quiz-templates/health/health-4.jpg
Normal file
After Width: | Height: | Size: 572 KiB |
BIN
src/assets/quiz-templates/health/health-5.jpg
Normal file
After Width: | Height: | Size: 758 KiB |
BIN
src/assets/quiz-templates/health/health-6.jpg
Normal file
After Width: | Height: | Size: 1013 KiB |
BIN
src/assets/quiz-templates/health/health-7.jpg
Normal file
After Width: | Height: | Size: 436 KiB |
BIN
src/assets/quiz-templates/health/health-8.jpg
Normal file
After Width: | Height: | Size: 571 KiB |
BIN
src/assets/quiz-templates/health/health-9.jpg
Normal file
After Width: | Height: | Size: 669 KiB |
BIN
src/assets/quiz-templates/production/production-1.jpg
Normal file
After Width: | Height: | Size: 425 KiB |
BIN
src/assets/quiz-templates/production/production-10.jpg
Normal file
After Width: | Height: | Size: 865 KiB |
BIN
src/assets/quiz-templates/production/production-2.jpg
Normal file
After Width: | Height: | Size: 378 KiB |
BIN
src/assets/quiz-templates/production/production-3.jpg
Normal file
After Width: | Height: | Size: 568 KiB |
BIN
src/assets/quiz-templates/production/production-4.jpg
Normal file
After Width: | Height: | Size: 550 KiB |
BIN
src/assets/quiz-templates/production/production-5.jpg
Normal file
After Width: | Height: | Size: 512 KiB |
BIN
src/assets/quiz-templates/production/production-6.jpg
Normal file
After Width: | Height: | Size: 410 KiB |
BIN
src/assets/quiz-templates/production/production-7.jpg
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
src/assets/quiz-templates/production/production-8.jpg
Normal file
After Width: | Height: | Size: 351 KiB |
BIN
src/assets/quiz-templates/production/production-9.jpg
Normal file
After Width: | Height: | Size: 658 KiB |
BIN
src/assets/quiz-templates/real-estate/real-estate-1.jpg
Normal file
After Width: | Height: | Size: 242 KiB |
BIN
src/assets/quiz-templates/real-estate/real-estate-10.jpg
Normal file
After Width: | Height: | Size: 297 KiB |
BIN
src/assets/quiz-templates/real-estate/real-estate-2.jpg
Normal file
After Width: | Height: | Size: 698 KiB |
BIN
src/assets/quiz-templates/real-estate/real-estate-3.jpg
Normal file
After Width: | Height: | Size: 356 KiB |
BIN
src/assets/quiz-templates/real-estate/real-estate-4.jpg
Normal file
After Width: | Height: | Size: 1.0 MiB |
BIN
src/assets/quiz-templates/real-estate/real-estate-5.jpg
Normal file
After Width: | Height: | Size: 262 KiB |
BIN
src/assets/quiz-templates/real-estate/real-estate-6.jpg
Normal file
After Width: | Height: | Size: 788 KiB |
BIN
src/assets/quiz-templates/real-estate/real-estate-7.jpg
Normal file
After Width: | Height: | Size: 533 KiB |
BIN
src/assets/quiz-templates/real-estate/real-estate-8.jpg
Normal file
After Width: | Height: | Size: 656 KiB |
BIN
src/assets/quiz-templates/real-estate/real-estate-9.jpg
Normal file
After Width: | Height: | Size: 352 KiB |
BIN
src/assets/quiz-templates/repair/repair-1.jpg
Normal file
After Width: | Height: | Size: 972 KiB |
BIN
src/assets/quiz-templates/repair/repair-10.jpg
Normal file
After Width: | Height: | Size: 488 KiB |
BIN
src/assets/quiz-templates/repair/repair-2.jpg
Normal file
After Width: | Height: | Size: 592 KiB |
BIN
src/assets/quiz-templates/repair/repair-3.jpg
Normal file
After Width: | Height: | Size: 637 KiB |
BIN
src/assets/quiz-templates/repair/repair-4.jpg
Normal file
After Width: | Height: | Size: 771 KiB |
BIN
src/assets/quiz-templates/repair/repair-5.jpg
Normal file
After Width: | Height: | Size: 447 KiB |
BIN
src/assets/quiz-templates/repair/repair-6.jpg
Normal file
After Width: | Height: | Size: 1.3 MiB |
BIN
src/assets/quiz-templates/repair/repair-7.jpg
Normal file
After Width: | Height: | Size: 518 KiB |
BIN
src/assets/quiz-templates/repair/repair-8.jpg
Normal file
After Width: | Height: | Size: 580 KiB |
BIN
src/assets/quiz-templates/repair/repair-9.jpg
Normal file
After Width: | Height: | Size: 826 KiB |
BIN
src/assets/quiz-templates/research/research-1.jpg
Normal file
After Width: | Height: | Size: 108 KiB |
BIN
src/assets/quiz-templates/research/research-10.jpg
Normal file
After Width: | Height: | Size: 2.0 MiB |
BIN
src/assets/quiz-templates/research/research-2.jpg
Normal file
After Width: | Height: | Size: 369 KiB |
BIN
src/assets/quiz-templates/research/research-3.jpg
Normal file
After Width: | Height: | Size: 834 KiB |
BIN
src/assets/quiz-templates/research/research-4.jpg
Normal file
After Width: | Height: | Size: 112 KiB |