add stuff
This commit is contained in:
parent
6ad843a2a0
commit
a9ca5294dd
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
node_modules
|
||||
dist
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
40
package.json
40
package.json
@ -1,12 +1,32 @@
|
||||
{
|
||||
"name": "@frontend/kitui",
|
||||
"version": "1.0.0",
|
||||
"description": "test",
|
||||
"main": "index.js",
|
||||
"repository": "git@penahub.gitlab.yandexcloud.net:frontend/kitui.git",
|
||||
"author": "skeris <kotilion.95@gmail.com>",
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"registry": "https://penahub.gitlab.yandexcloud.net/api/v4/projects/21/packages/npm/"
|
||||
}
|
||||
"name": "@frontend/kitui",
|
||||
"version": "1.0.1",
|
||||
"description": "test",
|
||||
"main": "index.js",
|
||||
"types": "index.d.ts",
|
||||
"repository": "git@penahub.gitlab.yandexcloud.net:frontend/kitui.git",
|
||||
"author": "skeris <kotilion.95@gmail.com>",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"clean": "rm -rf dist",
|
||||
"build": "npm run clean && tsc && cp package.json README.md ./dist"
|
||||
},
|
||||
"publishConfig": {
|
||||
"registry": "https://penahub.gitlab.yandexcloud.net/api/v4/projects/21/packages/npm/"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.2.5",
|
||||
"@types/react": "^18.2.7",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"typescript": "^5.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.4.0",
|
||||
"reconnecting-eventsource": "^1.6.2"
|
||||
}
|
||||
}
|
||||
|
72
src/api/createMakeRequest.ts
Normal file
72
src/api/createMakeRequest.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import axios, { AxiosResponse, Method } from "axios";
|
||||
|
||||
|
||||
interface MakeRequestArgs<T> {
|
||||
method?: Method;
|
||||
url: string;
|
||||
body?: T;
|
||||
/** Send access token */
|
||||
useToken?: boolean;
|
||||
contentType?: boolean;
|
||||
signal?: AbortSignal;
|
||||
/** Send refresh token */
|
||||
withCredentials?: boolean;
|
||||
}
|
||||
|
||||
export const createMakeRequest = (getToken: () => string, setToken: (token: string) => void) => async <TRequest = unknown, TResponse = unknown>({
|
||||
method = "post",
|
||||
url,
|
||||
body,
|
||||
useToken = true,
|
||||
contentType = false,
|
||||
signal,
|
||||
withCredentials,
|
||||
}: MakeRequestArgs<TRequest>): Promise<TResponse> => {
|
||||
const headers: Record<string, string> = {};
|
||||
if (useToken) headers["Authorization"] = `Bearer ${getToken()}`;
|
||||
if (contentType) headers["Content-Type"] = "application/json";
|
||||
|
||||
try {
|
||||
const response = await axios<TRequest, AxiosResponse<TResponse & { accessToken?: string; }>>({
|
||||
url,
|
||||
method,
|
||||
headers,
|
||||
data: body,
|
||||
signal,
|
||||
withCredentials,
|
||||
});
|
||||
|
||||
if (response.data?.accessToken) {
|
||||
setToken(response.data.accessToken);
|
||||
}
|
||||
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error) && error.response?.status === 401 && !withCredentials) {
|
||||
const refreshResponse = await refresh(getToken());
|
||||
if (refreshResponse.data?.accessToken) setToken(refreshResponse.data.accessToken);
|
||||
|
||||
headers["Authorization"] = refreshResponse.data.accessToken;
|
||||
const response = await axios.request<TRequest, AxiosResponse<TResponse>>({
|
||||
url,
|
||||
method,
|
||||
headers,
|
||||
data: body,
|
||||
signal,
|
||||
});
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
function refresh(token: string) {
|
||||
return axios<never, AxiosResponse<{ accessToken: string; }>>("https://admin.pena.digital/auth/refresh", {
|
||||
headers: {
|
||||
"Authorization": token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
}
|
2
src/api/index.ts
Normal file
2
src/api/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from "./createMakeRequest";
|
||||
export * from "./tickets";
|
18
src/api/tickets.ts
Normal file
18
src/api/tickets.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { CreateTicketRequest, CreateTicketResponse } from "../model/ticket";
|
||||
import { createMakeRequest } from "./createMakeRequest";
|
||||
|
||||
|
||||
export function createTicket({ makeRequest, url,body, useToken = true }: {
|
||||
makeRequest: ReturnType<typeof createMakeRequest>;
|
||||
url: string;
|
||||
body: CreateTicketRequest;
|
||||
useToken?: boolean;
|
||||
}): Promise<CreateTicketResponse> {
|
||||
return makeRequest({
|
||||
url,
|
||||
method: "POST",
|
||||
useToken,
|
||||
body,
|
||||
withCredentials: true,
|
||||
});
|
||||
}
|
1
src/decorators/index.ts
Normal file
1
src/decorators/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from "./throttle";
|
29
src/decorators/throttle.ts
Normal file
29
src/decorators/throttle.ts
Normal file
@ -0,0 +1,29 @@
|
||||
export type ThrottledFunction<T extends (...args: any) => any> = (...args: Parameters<T>) => void;
|
||||
|
||||
export function throttle<T extends (...args: any) => any>(func: T, ms: number): ThrottledFunction<T> {
|
||||
let isThrottled = false;
|
||||
let savedArgs: Parameters<T> | null;
|
||||
let savedThis: any;
|
||||
|
||||
function wrapper(this: any, ...args: Parameters<T>) {
|
||||
if (isThrottled) {
|
||||
savedArgs = args;
|
||||
savedThis = this;
|
||||
return;
|
||||
}
|
||||
|
||||
func.apply(this, args);
|
||||
|
||||
isThrottled = true;
|
||||
|
||||
setTimeout(function () {
|
||||
isThrottled = false;
|
||||
if (savedArgs) {
|
||||
wrapper.apply(savedThis, savedArgs);
|
||||
savedArgs = savedThis = null;
|
||||
}
|
||||
}, ms);
|
||||
}
|
||||
|
||||
return wrapper;
|
||||
}
|
6
src/hooks/index.ts
Normal file
6
src/hooks/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export * from "./useDebounce";
|
||||
export * from "./useEventListener";
|
||||
export * from "./useSSESubscription";
|
||||
export * from "./useThrottle";
|
||||
export * from "./useTicketMessages";
|
||||
export * from "./useTickets";
|
15
src/hooks/useDebounce.ts
Normal file
15
src/hooks/useDebounce.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
|
||||
export function useDebounce<T>(value: T, delay: number) {
|
||||
const [debouncedValue, setDebouncedValue] = useState<T>(value);
|
||||
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedValue(value);
|
||||
}, delay);
|
||||
return () => clearTimeout(handler);
|
||||
}, [value, delay]);
|
||||
|
||||
return debouncedValue;
|
||||
}
|
82
src/hooks/useEventListener.ts
Normal file
82
src/hooks/useEventListener.ts
Normal file
@ -0,0 +1,82 @@
|
||||
import { useEffect, useRef, RefObject, useLayoutEffect } from "react";
|
||||
|
||||
// https://usehooks-ts.com/react-hook/use-event-listener
|
||||
|
||||
// MediaQueryList Event based useEventListener interface
|
||||
function useEventListener<K extends keyof MediaQueryListEventMap>(
|
||||
eventName: K,
|
||||
handler: (event: MediaQueryListEventMap[K]) => void,
|
||||
element: RefObject<MediaQueryList>,
|
||||
options?: boolean | AddEventListenerOptions,
|
||||
): void;
|
||||
|
||||
// Window Event based useEventListener interface
|
||||
function useEventListener<K extends keyof WindowEventMap>(
|
||||
eventName: K,
|
||||
handler: (event: WindowEventMap[K]) => void,
|
||||
element?: undefined,
|
||||
options?: boolean | AddEventListenerOptions,
|
||||
): void;
|
||||
|
||||
// Element Event based useEventListener interface
|
||||
function useEventListener<
|
||||
K extends keyof HTMLElementEventMap,
|
||||
T extends HTMLElement = HTMLDivElement,
|
||||
>(
|
||||
eventName: K,
|
||||
handler: (event: HTMLElementEventMap[K]) => void,
|
||||
element: RefObject<T>,
|
||||
options?: boolean | AddEventListenerOptions,
|
||||
): void;
|
||||
|
||||
// Document Event based useEventListener interface
|
||||
function useEventListener<K extends keyof DocumentEventMap>(
|
||||
eventName: K,
|
||||
handler: (event: DocumentEventMap[K]) => void,
|
||||
element: RefObject<Document>,
|
||||
options?: boolean | AddEventListenerOptions,
|
||||
): void;
|
||||
|
||||
function useEventListener<
|
||||
KW extends keyof WindowEventMap,
|
||||
KH extends keyof HTMLElementEventMap,
|
||||
KM extends keyof MediaQueryListEventMap,
|
||||
T extends HTMLElement | MediaQueryList | void = void,
|
||||
>(
|
||||
eventName: KW | KH | KM,
|
||||
handler: (
|
||||
event:
|
||||
| WindowEventMap[KW]
|
||||
| HTMLElementEventMap[KH]
|
||||
| MediaQueryListEventMap[KM]
|
||||
| Event,
|
||||
) => void,
|
||||
element?: RefObject<T>,
|
||||
options?: boolean | AddEventListenerOptions,
|
||||
) {
|
||||
// Create a ref that stores handler
|
||||
const savedHandler = useRef(handler);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
savedHandler.current = handler;
|
||||
}, [handler]);
|
||||
|
||||
useEffect(() => {
|
||||
// Define the listening target
|
||||
const targetElement: T | Window = element?.current ?? window;
|
||||
|
||||
if (!(targetElement && targetElement.addEventListener)) return;
|
||||
|
||||
// Create event listener that calls handler function stored in ref
|
||||
const listener: typeof handler = event => savedHandler.current(event);
|
||||
|
||||
targetElement.addEventListener(eventName, listener, options);
|
||||
|
||||
// Remove event listener on cleanup
|
||||
return () => {
|
||||
targetElement.removeEventListener(eventName, listener, options);
|
||||
};
|
||||
}, [eventName, element, options]);
|
||||
}
|
||||
|
||||
export default useEventListener;
|
38
src/hooks/useSSESubscription.ts
Normal file
38
src/hooks/useSSESubscription.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { useEffect } from "react";
|
||||
import ReconnectingEventSource from "reconnecting-eventsource";
|
||||
import { devlog } from "../utils";
|
||||
|
||||
|
||||
export function useSSESubscription<T>({ enabled = true, url, onNewData, onDisconnect, marker = "" }: {
|
||||
enabled?: boolean;
|
||||
url: string;
|
||||
onNewData: (data: T[]) => void;
|
||||
onDisconnect: () => void;
|
||||
marker?: string;
|
||||
}) {
|
||||
useEffect(() => {
|
||||
if (!enabled) return;
|
||||
|
||||
const eventSource = new ReconnectingEventSource(url);
|
||||
|
||||
eventSource.addEventListener("open", () => devlog(`EventSource connected with ${url}`));
|
||||
eventSource.addEventListener("close", () => devlog(`EventSource closed with ${url}`));
|
||||
eventSource.addEventListener("message", event => {
|
||||
try {
|
||||
const newData = JSON.parse(event.data) as T;
|
||||
devlog(`new SSE: ${marker}`, newData);
|
||||
onNewData([newData]);
|
||||
} catch (error) {
|
||||
devlog(`SSE parsing error: ${marker}`, event.data, error);
|
||||
}
|
||||
});
|
||||
eventSource.addEventListener("error", event => {
|
||||
devlog("SSE Error:", event);
|
||||
});
|
||||
|
||||
return () => {
|
||||
eventSource.close();
|
||||
onDisconnect();
|
||||
};
|
||||
}, [enabled, marker, onDisconnect, onNewData, url]);
|
||||
}
|
22
src/hooks/useThrottle.ts
Normal file
22
src/hooks/useThrottle.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
|
||||
|
||||
export function useThrottle<T>(value: T, delay: number) {
|
||||
const [throttledValue, setThrottledValue] = useState<T>(value);
|
||||
const time = useRef<number>(0);
|
||||
|
||||
useEffect(() => {
|
||||
const now = Date.now();
|
||||
if (now > time.current + delay) {
|
||||
time.current = now;
|
||||
setThrottledValue(value);
|
||||
} else {
|
||||
const handler = setTimeout(() => {
|
||||
setThrottledValue(value);
|
||||
}, delay);
|
||||
return () => clearTimeout(handler);
|
||||
}
|
||||
}, [value, delay]);
|
||||
|
||||
return throttledValue;
|
||||
}
|
51
src/hooks/useTicketMessages.ts
Normal file
51
src/hooks/useTicketMessages.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { createMakeRequest } from "../api";
|
||||
import { TicketMessage, GetMessagesRequest, GetMessagesResponse } from "../model";
|
||||
import { devlog } from "../utils";
|
||||
|
||||
|
||||
export function useTicketMessages({ makeRequest, url, messageApiPage, messagesPerPage, ticketId, onNewMessages, onError, isUnauth = false }: {
|
||||
makeRequest: ReturnType<typeof createMakeRequest>;
|
||||
url: string;
|
||||
ticketId: string | undefined;
|
||||
messagesPerPage: number;
|
||||
messageApiPage: number;
|
||||
onNewMessages: (messages: TicketMessage[]) => void;
|
||||
onError: (error: Error) => void;
|
||||
isUnauth?: boolean;
|
||||
}) {
|
||||
const [fetchState, setFetchState] = useState<"fetching" | "idle" | "all fetched">("idle");
|
||||
|
||||
useEffect(function fetchTicketMessages() {
|
||||
if (!ticketId) return;
|
||||
|
||||
const controller = new AbortController();
|
||||
|
||||
setFetchState("fetching");
|
||||
makeRequest<GetMessagesRequest, GetMessagesResponse>({
|
||||
url,
|
||||
method: "POST",
|
||||
useToken: !isUnauth,
|
||||
body: {
|
||||
amt: messagesPerPage,
|
||||
page: messageApiPage,
|
||||
ticket: ticketId,
|
||||
},
|
||||
signal: controller.signal,
|
||||
withCredentials: isUnauth,
|
||||
}).then(result => {
|
||||
devlog("GetMessagesResponse", result);
|
||||
if (result?.length > 0) {
|
||||
onNewMessages(result);
|
||||
setFetchState("idle");
|
||||
} else setFetchState("all fetched");
|
||||
}).catch(error => {
|
||||
devlog("Error fetching messages", error);
|
||||
onError(error);
|
||||
});
|
||||
|
||||
return () => controller.abort();
|
||||
}, [isUnauth, makeRequest, messageApiPage, messagesPerPage, onError, onNewMessages, ticketId, url]);
|
||||
|
||||
return fetchState;
|
||||
}
|
46
src/hooks/useTickets.ts
Normal file
46
src/hooks/useTickets.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { createMakeRequest } from "../api";
|
||||
import { GetTicketsResponse, GetTicketsRequest } from "../model";
|
||||
import { devlog } from "../utils";
|
||||
|
||||
|
||||
export function useTickets({ makeRequest, url, ticketsPerPage, ticketApiPage, onNewTickets, onError }: {
|
||||
makeRequest: ReturnType<typeof createMakeRequest>;
|
||||
url: string;
|
||||
ticketsPerPage: number;
|
||||
ticketApiPage: number;
|
||||
onNewTickets: (response: GetTicketsResponse) => void;
|
||||
onError: (error: Error) => void;
|
||||
}) {
|
||||
const [fetchState, setFetchState] = useState<"fetching" | "idle" | "all fetched">("idle");
|
||||
|
||||
useEffect(function fetchTickets() {
|
||||
const controller = new AbortController();
|
||||
|
||||
setFetchState("fetching");
|
||||
makeRequest<GetTicketsRequest, GetTicketsResponse>({
|
||||
url,
|
||||
method: "POST",
|
||||
useToken: true,
|
||||
body: {
|
||||
amt: ticketsPerPage,
|
||||
page: ticketApiPage,
|
||||
status: "open",
|
||||
},
|
||||
signal: controller.signal,
|
||||
}).then((result) => {
|
||||
devlog("GetTicketsResponse", result);
|
||||
if (result.data) {
|
||||
onNewTickets(result);
|
||||
setFetchState("idle");
|
||||
} else setFetchState("all fetched");
|
||||
}).catch(error => {
|
||||
console.log("Error fetching tickets", error);
|
||||
onError(error);
|
||||
});
|
||||
|
||||
return () => controller.abort();
|
||||
}, [makeRequest, onError, onNewTickets, ticketApiPage, ticketsPerPage, url]);
|
||||
|
||||
return fetchState;
|
||||
}
|
6
src/index.ts
Normal file
6
src/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export * from "./api";
|
||||
export * from "./hooks";
|
||||
export * from "./decorators";
|
||||
export * from "./utils";
|
||||
|
||||
export type * from "./model";
|
1
src/model/index.ts
Normal file
1
src/model/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export type * from "./ticket";
|
67
src/model/ticket.ts
Normal file
67
src/model/ticket.ts
Normal file
@ -0,0 +1,67 @@
|
||||
|
||||
|
||||
export interface CreateTicketRequest {
|
||||
Title: string;
|
||||
Message: string;
|
||||
};
|
||||
|
||||
export interface CreateTicketResponse {
|
||||
Ticket: string;
|
||||
sess: string;
|
||||
};
|
||||
|
||||
export interface SendTicketMessageRequest {
|
||||
message: string;
|
||||
ticket: string;
|
||||
lang: string;
|
||||
files: string[];
|
||||
};
|
||||
|
||||
export type TicketStatus = "open";
|
||||
|
||||
export interface GetTicketsRequest {
|
||||
amt: number;
|
||||
/** Пагинация начинается с индекса 0 */
|
||||
page: number;
|
||||
srch?: string;
|
||||
status?: TicketStatus;
|
||||
};
|
||||
|
||||
export interface GetTicketsResponse {
|
||||
count: number;
|
||||
data: Ticket[] | null;
|
||||
};
|
||||
|
||||
export interface Ticket {
|
||||
id: string;
|
||||
user: string;
|
||||
sess: string;
|
||||
ans: string;
|
||||
state: string;
|
||||
top_message: TicketMessage;
|
||||
title: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
rate: number;
|
||||
};
|
||||
|
||||
export interface TicketMessage {
|
||||
id: string;
|
||||
ticket_id: string;
|
||||
user_id: string,
|
||||
session_id: string;
|
||||
message: string;
|
||||
files: string[],
|
||||
shown: { [key: string]: number; },
|
||||
request_screenshot: string,
|
||||
created_at: string;
|
||||
};
|
||||
|
||||
export interface GetMessagesRequest {
|
||||
amt: number;
|
||||
page: number;
|
||||
srch?: string;
|
||||
ticket: string;
|
||||
};
|
||||
|
||||
export type GetMessagesResponse = TicketMessage[];
|
29
src/utils/backendMessageHandler.ts
Normal file
29
src/utils/backendMessageHandler.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { isAxiosError } from "axios";
|
||||
import { devlog } from "./devlog";
|
||||
|
||||
|
||||
const backendErrorMessage: Record<string, string> = {
|
||||
"user not found": "Пользователь не найден",
|
||||
"invalid password": "Неправильный пароль",
|
||||
"field <password> is empty": "Поле \"Пароль\" не заполнено",
|
||||
"field <login> is empty": "Поле \"Логин\" не заполнено",
|
||||
"field <email> is empty": "Поле \"E-mail\" не заполнено",
|
||||
"field <phoneNumber> is empty": "Поле \"Номер телефона\" не заполнено",
|
||||
"user with this email or login is exist": "Пользователь уже существует",
|
||||
};
|
||||
|
||||
export function getMessageFromFetchError(error: any, defaultMessage?: string): string | null {
|
||||
devlog(error);
|
||||
|
||||
const message = backendErrorMessage[error.response?.data?.message];
|
||||
if (message) return message;
|
||||
|
||||
if (isAxiosError(error)) {
|
||||
switch (error.code) {
|
||||
case "ERR_NETWORK": return "Ошибка сети";
|
||||
case "ERR_CANCELED": return null;
|
||||
}
|
||||
}
|
||||
|
||||
return defaultMessage ?? "Что-то пошло не так. Повторите попытку позже";
|
||||
}
|
3
src/utils/devlog.ts
Normal file
3
src/utils/devlog.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export const devlog: typeof console.log = (...args) => {
|
||||
if (process.env.NODE_ENV === "develpment") console.log(...args);
|
||||
};
|
2
src/utils/index.ts
Normal file
2
src/utils/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from "./devlog";
|
||||
export * from "./backendMessageHandler";
|
16
tsconfig.json
Normal file
16
tsconfig.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"jsx": "react",
|
||||
"declaration": true,
|
||||
"esModuleInterop": true,
|
||||
"outDir": "dist",
|
||||
"target": "es6",
|
||||
"module": "es6",
|
||||
"moduleResolution": "node",
|
||||
"skipLibCheck": true,
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
133
yarn.lock
Normal file
133
yarn.lock
Normal file
@ -0,0 +1,133 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@types/node@^20.2.5":
|
||||
version "20.2.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.2.5.tgz#26d295f3570323b2837d322180dfbf1ba156fefb"
|
||||
integrity sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==
|
||||
|
||||
"@types/prop-types@*":
|
||||
version "15.7.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
|
||||
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
|
||||
|
||||
"@types/react@^18.2.7":
|
||||
version "18.2.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.7.tgz#dfb4518042a3117a045b8c222316f83414a783b3"
|
||||
integrity sha512-ojrXpSH2XFCmHm7Jy3q44nXDyN54+EYKP2lBhJ2bqfyPj6cIUW/FZW/Csdia34NQgq7KYcAlHi5184m4X88+yw==
|
||||
dependencies:
|
||||
"@types/prop-types" "*"
|
||||
"@types/scheduler" "*"
|
||||
csstype "^3.0.2"
|
||||
|
||||
"@types/scheduler@*":
|
||||
version "0.16.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.3.tgz#cef09e3ec9af1d63d2a6cc5b383a737e24e6dcf5"
|
||||
integrity sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==
|
||||
|
||||
asynckit@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
|
||||
|
||||
axios@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f"
|
||||
integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==
|
||||
dependencies:
|
||||
follow-redirects "^1.15.0"
|
||||
form-data "^4.0.0"
|
||||
proxy-from-env "^1.1.0"
|
||||
|
||||
combined-stream@^1.0.8:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
|
||||
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
|
||||
dependencies:
|
||||
delayed-stream "~1.0.0"
|
||||
|
||||
csstype@^3.0.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b"
|
||||
integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==
|
||||
|
||||
delayed-stream@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
|
||||
|
||||
follow-redirects@^1.15.0:
|
||||
version "1.15.2"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
|
||||
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
|
||||
|
||||
form-data@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
|
||||
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
|
||||
dependencies:
|
||||
asynckit "^0.4.0"
|
||||
combined-stream "^1.0.8"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
"js-tokens@^3.0.0 || ^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
||||
|
||||
loose-envify@^1.1.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
||||
dependencies:
|
||||
js-tokens "^3.0.0 || ^4.0.0"
|
||||
|
||||
mime-db@1.52.0:
|
||||
version "1.52.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
|
||||
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
|
||||
|
||||
mime-types@^2.1.12:
|
||||
version "2.1.35"
|
||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
|
||||
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
|
||||
dependencies:
|
||||
mime-db "1.52.0"
|
||||
|
||||
proxy-from-env@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
|
||||
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
|
||||
|
||||
react-dom@^18.2.0:
|
||||
version "18.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
|
||||
integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
scheduler "^0.23.0"
|
||||
|
||||
react@^18.2.0:
|
||||
version "18.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
|
||||
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
|
||||
reconnecting-eventsource@^1.6.2:
|
||||
version "1.6.2"
|
||||
resolved "https://registry.yarnpkg.com/reconnecting-eventsource/-/reconnecting-eventsource-1.6.2.tgz#b7f5b03b1c76291f6fbcb0203004892a57ae253b"
|
||||
integrity sha512-vHhoxVLbA2YcfljWMKEbgR1KVTgwIrnyh/bzVJc+gfQbGcUIToLL6jNhkUL4E+9FbnAcfUVNLIw2YCiliTg/4g==
|
||||
|
||||
scheduler@^0.23.0:
|
||||
version "0.23.0"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
|
||||
integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
|
||||
typescript@^5.0.4:
|
||||
version "5.0.4"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b"
|
||||
integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==
|
Loading…
Reference in New Issue
Block a user