refactor: requests

This commit is contained in:
IlyaDoronin 2024-05-29 14:58:54 +03:00
parent f58f2c0c47
commit a76fe4b0a0
13 changed files with 283 additions and 239 deletions

@ -87,9 +87,10 @@ export const getHistory = async (): Promise<
export const sendReport = async ( export const sendReport = async (
id: string id: string
): Promise<[unknown | null, string?]> => { ): Promise<[void | null, string?]> => {
debugger;
try { try {
const sendReportResponse = await makeRequest<{ id: string }, unknown>({ const sendReportResponse = await makeRequest<{ id: string }, void>({
method: "POST", method: "POST",
url: `${API_URL}/sendReport`, url: `${API_URL}/sendReport`,
body: { id }, body: { id },
@ -99,15 +100,16 @@ export const sendReport = async (
} catch (nativeError) { } catch (nativeError) {
const [error] = parseAxiosError(nativeError); const [error] = parseAxiosError(nativeError);
return [[], `Не удалось отправить отчёт. ${error}`]; return [null, `Не удалось отправить отчёт. ${error}`];
} }
}; };
export const sendReportById = async ( export const sendReportById = async (
tariffId: string tariffId: string
): Promise<[unknown | null, string?]> => { ): Promise<[void | null, string?]> => {
debugger;
try { try {
const sendReportResponse = await makeRequest<never, unknown>({ const sendReportResponse = await makeRequest<never, void>({
method: "POST", method: "POST",
url: `${API_URL}/sendReport/${tariffId}`, url: `${API_URL}/sendReport/${tariffId}`,
}); });
@ -116,6 +118,6 @@ export const sendReportById = async (
} catch (nativeError) { } catch (nativeError) {
const [error] = parseAxiosError(nativeError); const [error] = parseAxiosError(nativeError);
return [[], `Не удалось отправить отчёт. ${error}`]; return [null, `Не удалось отправить отчёт. ${error}`];
} }
}; };

@ -8,13 +8,13 @@ type GetRecentlyPurchasedTariffsResponse = {
}; };
export const getRecentlyPurchasedTariffs = async (): Promise< export const getRecentlyPurchasedTariffs = async (): Promise<
[GetRecentlyPurchasedTariffsResponse | null, string?] [GetRecentlyPurchasedTariffsResponse[] | null, string?]
> => { > => {
try { try {
debugger; debugger;
const recentlyPurchased = await makeRequest< const recentlyPurchased = await makeRequest<
never, never,
GetRecentlyPurchasedTariffsResponse GetRecentlyPurchasedTariffsResponse[]
>({ >({
method: "GET", method: "GET",
url: `${API_URL}/recent`, url: `${API_URL}/recent`,

@ -1,5 +1,7 @@
import makeRequest from "@api/makeRequest"; import makeRequest from "@api/makeRequest";
import { parseAxiosError } from "@root/utils/parse-error"; import { parseAxiosError } from "@root/utils/parse-error";
import { createTicket as createTicketRequest } from "@frontend/kitui";
import type { CreateTicketResponse } from "@frontend/kitui";
import { SendTicketMessageRequest } from "@frontend/kitui"; import { SendTicketMessageRequest } from "@frontend/kitui";
@ -7,7 +9,7 @@ type SendFileResponse = {
message: string; message: string;
}; };
const API_URL = `${process.env.REACT_APP_DOMAIN}/heruvym`; const API_URL = `${process.env.REACT_APP_DOMAIN}/heruvym/v1.0.0`;
export const sendTicketMessage = async ( export const sendTicketMessage = async (
ticketId: string, ticketId: string,
@ -61,7 +63,7 @@ export const sendFile = async (
const sendResponse = await makeRequest<FormData, SendFileResponse>({ const sendResponse = await makeRequest<FormData, SendFileResponse>({
method: "POST", method: "POST",
url: `${process.env.REACT_APP_DOMAIN}/sendFiles`, url: `${API_URL}/sendFiles`,
body, body,
}); });
@ -72,3 +74,21 @@ export const sendFile = async (
return [null, `Не удалось отправить файл. ${error}`]; return [null, `Не удалось отправить файл. ${error}`];
} }
}; };
export const createTicket = async (
ticketNameField: string,
ticketBodyField: string
): Promise<[CreateTicketResponse | null, string?]> => {
try {
const createTicketResponse = await createTicketRequest({
url: `${API_URL}/create`,
body: { Title: ticketNameField, Message: ticketBodyField },
});
return [createTicketResponse];
} catch (nativeError) {
const [error] = parseAxiosError(nativeError);
return [null, `Не удалось отправить файл. ${error}`];
}
};

@ -63,20 +63,22 @@ export const sendPayment = async ({
} }
}; };
export const sendRSPayment = async (money: number): Promise<string | null> => { export const sendRSPayment = async (
money: number
): Promise<[string | null, string?]> => {
try { try {
await makeRequest<unknown, string>({ const sendRSPaymentResponse = await makeRequest<{ money: number }, string>({
method: "POST", method: "POST",
url: `${API_URL}/wallet/rspay`, url: `${API_URL}/wallet/rspay`,
body: { money: money }, body: { money },
useToken: true, useToken: true,
withCredentials: false, withCredentials: false,
}); });
return null; return [sendRSPaymentResponse];
} catch (nativeError) { } catch (nativeError) {
const [error] = parseAxiosError(nativeError); const [error] = parseAxiosError(nativeError);
return `Ошибка оплаты. ${error}`; return [null, `Ошибка оплаты. ${error}`];
} }
}; };

@ -11,7 +11,6 @@ import {
useTheme, useTheme,
} from "@mui/material"; } from "@mui/material";
import { import {
createTicket,
getMessageFromFetchError, getMessageFromFetchError,
throttle, throttle,
TicketMessage, TicketMessage,
@ -59,6 +58,7 @@ import AttachFileIcon from "@mui/icons-material/AttachFile";
import ChatDocument from "./ChatDocument"; import ChatDocument from "./ChatDocument";
import ChatImage from "./ChatImage"; import ChatImage from "./ChatImage";
import ChatVideo from "./ChatVideo"; import ChatVideo from "./ChatVideo";
import { createTicket } from "@api/ticket";
type ModalWarningType = type ModalWarningType =
| "errorType" | "errorType"
@ -267,29 +267,24 @@ export default function Chat({ open = false, onclickArrow, sx }: Props) {
if (!sessionData?.ticketId) { if (!sessionData?.ticketId) {
setIsMessageSending(true); setIsMessageSending(true);
createTicket({
url: process.env.REACT_APP_DOMAIN + "/heruvym/create", const [createTicketresult, createTicketerror] = await createTicket(
body: { "Unauth title",
Title: "Unauth title", messageField
Message: messageField, );
},
useToken: Boolean(user), if (createTicketerror) {
}) enqueueSnackbar(createTicketerror);
.then((response) => { } else if (createTicketresult) {
setTicketData({ setTicketData({
ticketId: response.Ticket, ticketId: createTicketresult.Ticket,
sessionId: response.sess, sessionId: createTicketresult.sess,
});
setSseEnabled(true);
})
.catch((error) => {
const errorMessage = getMessageFromFetchError(error);
if (errorMessage) enqueueSnackbar(errorMessage);
})
.finally(() => {
setMessageField("");
setIsMessageSending(false);
}); });
setSseEnabled(true);
}
setMessageField("");
setIsMessageSending(false);
} else { } else {
setIsMessageSending(true); setIsMessageSending(true);
@ -333,18 +328,18 @@ export default function Chat({ open = false, onclickArrow, sx }: Props) {
let data; let data;
if (!ticket.sessionData?.ticketId) { if (!ticket.sessionData?.ticketId) {
try { try {
data = await createTicket({ const [createTicketresult] = await createTicket("Unauth title", "");
url: process.env.REACT_APP_DOMAIN + "/heruvym/create",
body: { if (createTicketresult) {
Title: "Unauth title", data = createTicketresult;
Message: "", }
},
useToken: Boolean(user), if (data) {
}); setTicketData({
setTicketData({ ticketId: data.Ticket,
ticketId: data.Ticket, sessionId: data.sess,
sessionId: data.sess, });
}); }
} catch (error: any) { } catch (error: any) {
const errorMessage = getMessageFromFetchError(error); const errorMessage = getMessageFromFetchError(error);
if (errorMessage) enqueueSnackbar(errorMessage); if (errorMessage) enqueueSnackbar(errorMessage);

@ -98,7 +98,7 @@ export default function ChatDocument({
</svg> </svg>
<Link <Link
download="" download=""
href={`https://storage.yandexcloud.net/pair/${file}`} href={`https://3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b.s3.timeweb.cloud/pair/${file}`}
style={{ style={{
color: "#7E2AEA", color: "#7E2AEA",
display: "flex", display: "flex",

@ -110,7 +110,7 @@ export default function ChatImage({
height: "217px", height: "217px",
width: "217px", width: "217px",
}} }}
src={`https://storage.yandexcloud.net/pair/${file}`} src={`https://3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b.s3.timeweb.cloud/pair/${file}`}
/> />
</ButtonBase> </ButtonBase>
</Box> </Box>

@ -113,7 +113,7 @@ export default function ChatImage({
}} }}
controls controls
> >
<source src={`https://storage.yandexcloud.net/pair/${file}`} /> <source src={`https://3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b.s3.timeweb.cloud/pair/${file}`} />
</Box> </Box>
</Box> </Box>
</Box> </Box>

@ -139,10 +139,12 @@ export default function Payment() {
return; return;
} }
const sendRSPaymentError = await sendRSPayment(Number(paymentValueField)); const [sendRSPaymentResponse] = await sendRSPayment(
Number(paymentValueField)
);
if (sendRSPaymentError) { if (sendRSPaymentResponse) {
return enqueueSnackbar(sendRSPaymentError); return enqueueSnackbar(sendRSPaymentResponse);
} }
enqueueSnackbar( enqueueSnackbar(

@ -12,7 +12,7 @@ export default function ChatImageNewWindow() {
maxHeight: "100vh", maxHeight: "100vh",
maxWidth: "100vw", maxWidth: "100vw",
}} }}
src={`https://storage.yandexcloud.net/pair/${srcImage}`} src={`https://3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b.s3.timeweb.cloud/pair/${srcImage}`}
/> />
</> </>
); );

@ -1,132 +1,138 @@
import { Box, Typography, FormControl, InputBase, useMediaQuery, useTheme, Button } from "@mui/material" import {
import { useState } from "react" Box,
import { useNavigate } from "react-router-dom" Typography,
import { enqueueSnackbar } from "notistack" FormControl,
import { cardShadow } from "@root/utils/theme" InputBase,
import { createTicket } from "@frontend/kitui" useMediaQuery,
useTheme,
Button,
} from "@mui/material";
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { enqueueSnackbar } from "notistack";
import { cardShadow } from "@root/utils/theme";
import { createTicket } from "@api/ticket";
export default function CreateTicket() { export default function CreateTicket() {
const theme = useTheme() const theme = useTheme();
const upMd = useMediaQuery(theme.breakpoints.up("md")) const upMd = useMediaQuery(theme.breakpoints.up("md"));
const navigate = useNavigate() const navigate = useNavigate();
const [ticketNameField, setTicketNameField] = useState<string>("") const [ticketNameField, setTicketNameField] = useState<string>("");
const [ticketBodyField, setTicketBodyField] = useState<string>("") const [ticketBodyField, setTicketBodyField] = useState<string>("");
async function handleCreateTicket() { async function handleCreateTicket() {
if (!ticketBodyField || !ticketNameField) return if (!ticketBodyField || !ticketNameField) return;
createTicket({ const [createTicketresult, createTicketerror] = await createTicket(
url: process.env.REACT_APP_DOMAIN + "/heruvym/create", ticketNameField,
body: { ticketBodyField
Title: ticketNameField, );
Message: ticketBodyField,
}
}).then(result => {
navigate(`/support/${result.Ticket}`)
}).catch(error => {
enqueueSnackbar(error.message)
})
}
return ( if (createTicketerror) {
<Box return enqueueSnackbar(createTicketerror);
sx={{ }
backgroundColor: upMd ? "white" : undefined,
display: "flex", navigate(`/support/${createTicketresult?.Ticket}`);
flexDirection: upMd ? "row" : "column", }
maxHeight: upMd ? "443px" : undefined,
borderRadius: "12px", return (
p: upMd ? "20px" : undefined, <Box
gap: upMd ? "40px" : "20px", sx={{
boxShadow: upMd backgroundColor: upMd ? "white" : undefined,
? cardShadow display: "flex",
: undefined, flexDirection: upMd ? "row" : "column",
}} maxHeight: upMd ? "443px" : undefined,
> borderRadius: "12px",
<Box p: upMd ? "20px" : undefined,
sx={{ gap: upMd ? "40px" : "20px",
display: "flex", boxShadow: upMd ? cardShadow : undefined,
alignItems: "start", }}
flexDirection: "column", >
flexGrow: 1, <Box
}} sx={{
> display: "flex",
<Typography variant={upMd ? "h5" : "body2"} mb={upMd ? "30px" : "20px"}> alignItems: "start",
Написать обращение flexDirection: "column",
</Typography> flexGrow: 1,
<FormControl sx={{ width: "100%" }}> }}
<InputBase >
value={ticketNameField} <Typography variant={upMd ? "h5" : "body2"} mb={upMd ? "30px" : "20px"}>
fullWidth Написать обращение
placeholder="Заголовок обращения" </Typography>
id="ticket-header" <FormControl sx={{ width: "100%" }}>
sx={{ <InputBase
backgroundColor: theme.palette.background.default, value={ticketNameField}
border: `1px solid ${theme.palette.gray.main}`, fullWidth
borderRadius: "10px", placeholder="Заголовок обращения"
p: 0, id="ticket-header"
}} sx={{
inputProps={{ backgroundColor: theme.palette.background.default,
sx: { border: `1px solid ${theme.palette.gray.main}`,
borderRadius: "10px", borderRadius: "10px",
fontWeight: 400, p: 0,
fontSize: "16px", }}
lineHeight: "19px", inputProps={{
pt: "10px", sx: {
pb: "10px", borderRadius: "10px",
px: "19px", fontWeight: 400,
}, fontSize: "16px",
}} lineHeight: "19px",
onChange={(e) => setTicketNameField(e.target.value)} pt: "10px",
/> pb: "10px",
</FormControl> px: "19px",
<FormControl sx={{ width: "100%" }}> },
<Box }}
sx={{ onChange={(e) => setTicketNameField(e.target.value)}
overflow: "hidden", />
mt: "16px", </FormControl>
backgroundColor: theme.palette.background.default, <FormControl sx={{ width: "100%" }}>
border: `1px solid ${theme.palette.gray.main}`, <Box
borderRadius: "10px", sx={{
}} overflow: "hidden",
> mt: "16px",
<InputBase backgroundColor: theme.palette.background.default,
value={ticketBodyField} border: `1px solid ${theme.palette.gray.main}`,
fullWidth borderRadius: "10px",
placeholder="Текст обращения" }}
id="ticket-body" >
multiline <InputBase
sx={{ value={ticketBodyField}
p: 0, fullWidth
height: "284px", placeholder="Текст обращения"
alignItems: "start", id="ticket-body"
overflow: "auto", multiline
}} sx={{
inputProps={{ p: 0,
sx: { height: "284px",
borderRadius: "10px", alignItems: "start",
fontWeight: 400, overflow: "auto",
fontSize: "16px", }}
lineHeight: "19px", inputProps={{
pt: "13px", sx: {
pb: "13px", borderRadius: "10px",
px: "19px", fontWeight: 400,
height: "300px", fontSize: "16px",
}, lineHeight: "19px",
}} pt: "13px",
onChange={(e) => setTicketBodyField(e.target.value)} pb: "13px",
/> px: "19px",
</Box> height: "300px",
</FormControl> },
</Box> }}
<Box sx={{ alignSelf: upMd ? "end" : "start" }}> onChange={(e) => setTicketBodyField(e.target.value)}
<Button />
variant="pena-contained-dark" </Box>
onClick={handleCreateTicket} </FormControl>
disabled={!ticketBodyField || !ticketNameField} </Box>
>Отправить</Button> <Box sx={{ alignSelf: upMd ? "end" : "start" }}>
</Box> <Button
</Box> variant="pena-contained-dark"
) onClick={handleCreateTicket}
disabled={!ticketBodyField || !ticketNameField}
>
Отправить
</Button>
</Box>
</Box>
);
} }

@ -1,38 +1,45 @@
import { useEffect, useLayoutEffect, useRef } from "react" import { useEffect, useLayoutEffect, useRef } from "react";
import { devlog } from "@frontend/kitui" import { devlog } from "@frontend/kitui";
import { ServiceKeyToPrivilegesMap } from "@root/model/privilege" import { ServiceKeyToPrivilegesMap } from "@root/model/privilege";
import { getCustomTariffs } from "@root/api/tariff" import { getCustomTariffs } from "@root/api/tariff";
export function useCustomTariffs({ export function useCustomTariffs({
onError, onError,
onNewUser, onNewUser,
}: { }: {
onNewUser: (response: ServiceKeyToPrivilegesMap) => void; onNewUser: (response: ServiceKeyToPrivilegesMap) => void;
onError: (error: any) => void; onError: (error: any) => void;
}) { }) {
const onNewUserRef = useRef(onNewUser) const onNewUserRef = useRef(onNewUser);
const onErrorRef = useRef(onError) const onErrorRef = useRef(onError);
useLayoutEffect(() => { useLayoutEffect(() => {
onNewUserRef.current = onNewUser onNewUserRef.current = onNewUser;
onErrorRef.current = onError onErrorRef.current = onError;
}) });
useEffect(() => { useEffect(() => {
const controller = new AbortController() const controller = new AbortController();
getCustomTariffs(controller.signal) const getCustomTariffsRequest = async () => {
.then(([customTariffs]) => { const [customTariffs, customTariffsError] = await getCustomTariffs(
if (customTariffs) { controller.signal
onNewUserRef.current(customTariffs) );
}
})
.catch(([_, error]) => {
devlog("Error fetching custom tariffs", error)
onErrorRef.current(error)
})
return () => controller.abort() if (customTariffsError) {
}, []) devlog("Error fetching custom tariffs", customTariffsError);
onErrorRef.current(customTariffsError);
return;
}
if (customTariffs) {
onNewUserRef.current(customTariffs);
}
};
getCustomTariffsRequest();
return () => controller.abort();
}, []);
} }

@ -1,14 +1,14 @@
import { useEffect, useLayoutEffect, useRef, useState } from "react" import { useEffect, useLayoutEffect, useRef, useState } from "react";
import { getTariffs } from "@root/api/tariff" import { getTariffs } from "@root/api/tariff";
import type { Tariff } from "@frontend/kitui" import type { Tariff } from "@frontend/kitui";
export function useTariffFetcher({ export function useTariffFetcher({
tariffsPerPage, tariffsPerPage,
apiPage, apiPage,
onSuccess, onSuccess,
onError, onError,
}: { }: {
baseUrl?: string; baseUrl?: string;
tariffsPerPage: number; tariffsPerPage: number;
@ -16,32 +16,42 @@ export function useTariffFetcher({
onSuccess: (response: Tariff[]) => void; onSuccess: (response: Tariff[]) => void;
onError?: (error: Error) => void; onError?: (error: Error) => void;
}) { }) {
const [fetchState, setFetchState] = useState<"fetching" | "idle" | "all fetched">("idle") const [fetchState, setFetchState] = useState<
const onSuccessRef = useRef(onSuccess) "fetching" | "idle" | "all fetched"
const onErrorRef = useRef(onError) >("idle");
const onSuccessRef = useRef(onSuccess);
const onErrorRef = useRef(onError);
useLayoutEffect(() => { useLayoutEffect(() => {
onSuccessRef.current = onSuccess onSuccessRef.current = onSuccess;
onErrorRef.current = onError onErrorRef.current = onError;
}, [onError, onSuccess]) }, [onError, onSuccess]);
useEffect(() => { useEffect(() => {
const controller = new AbortController() const controller = new AbortController();
setFetchState("fetching") const getTariffsRequest = async () => {
getTariffs(apiPage, tariffsPerPage, controller.signal) setFetchState("fetching");
.then(([result]) => { const [tariffs, tariffsError] = await getTariffs(
if (result && result.tariffs.length > 0) { apiPage,
onSuccessRef.current(result.tariffs) tariffsPerPage,
setFetchState("idle") controller.signal
} else setFetchState("all fetched") );
})
.catch(([_, error]) => {
onErrorRef.current?.(error)
})
return () => controller.abort() if (tariffsError) {
}, [apiPage, tariffsPerPage]) return onErrorRef.current?.(new Error(tariffsError));
}
return fetchState if (tariffs && tariffs.tariffs.length > 0) {
onSuccessRef.current(tariffs.tariffs);
setFetchState("idle");
} else setFetchState("all fetched");
};
getTariffsRequest();
return () => controller.abort();
}, [apiPage, tariffsPerPage]);
return fetchState;
} }