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

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

@ -1,5 +1,7 @@
import makeRequest from "@api/makeRequest";
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";
@ -7,7 +9,7 @@ type SendFileResponse = {
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 (
ticketId: string,
@ -61,7 +63,7 @@ export const sendFile = async (
const sendResponse = await makeRequest<FormData, SendFileResponse>({
method: "POST",
url: `${process.env.REACT_APP_DOMAIN}/sendFiles`,
url: `${API_URL}/sendFiles`,
body,
});
@ -72,3 +74,21 @@ export const sendFile = async (
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 {
await makeRequest<unknown, string>({
const sendRSPaymentResponse = await makeRequest<{ money: number }, string>({
method: "POST",
url: `${API_URL}/wallet/rspay`,
body: { money: money },
body: { money },
useToken: true,
withCredentials: false,
});
return null;
return [sendRSPaymentResponse];
} catch (nativeError) {
const [error] = parseAxiosError(nativeError);
return `Ошибка оплаты. ${error}`;
return [null, `Ошибка оплаты. ${error}`];
}
};

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

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

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

@ -113,7 +113,7 @@ export default function ChatImage({
}}
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>

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

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

@ -1,38 +1,45 @@
import { useEffect, useLayoutEffect, useRef } from "react"
import { devlog } from "@frontend/kitui"
import { useEffect, useLayoutEffect, useRef } from "react";
import { devlog } from "@frontend/kitui";
import { ServiceKeyToPrivilegesMap } from "@root/model/privilege"
import { getCustomTariffs } from "@root/api/tariff"
import { ServiceKeyToPrivilegesMap } from "@root/model/privilege";
import { getCustomTariffs } from "@root/api/tariff";
export function useCustomTariffs({
onError,
onNewUser,
onError,
onNewUser,
}: {
onNewUser: (response: ServiceKeyToPrivilegesMap) => void;
onError: (error: any) => void;
}) {
const onNewUserRef = useRef(onNewUser)
const onErrorRef = useRef(onError)
const onNewUserRef = useRef(onNewUser);
const onErrorRef = useRef(onError);
useLayoutEffect(() => {
onNewUserRef.current = onNewUser
onErrorRef.current = onError
})
useLayoutEffect(() => {
onNewUserRef.current = onNewUser;
onErrorRef.current = onError;
});
useEffect(() => {
const controller = new AbortController()
useEffect(() => {
const controller = new AbortController();
getCustomTariffs(controller.signal)
.then(([customTariffs]) => {
if (customTariffs) {
onNewUserRef.current(customTariffs)
}
})
.catch(([_, error]) => {
devlog("Error fetching custom tariffs", error)
onErrorRef.current(error)
})
const getCustomTariffsRequest = async () => {
const [customTariffs, customTariffsError] = await getCustomTariffs(
controller.signal
);
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({
tariffsPerPage,
apiPage,
onSuccess,
onError,
tariffsPerPage,
apiPage,
onSuccess,
onError,
}: {
baseUrl?: string;
tariffsPerPage: number;
@ -16,32 +16,42 @@ export function useTariffFetcher({
onSuccess: (response: Tariff[]) => void;
onError?: (error: Error) => void;
}) {
const [fetchState, setFetchState] = useState<"fetching" | "idle" | "all fetched">("idle")
const onSuccessRef = useRef(onSuccess)
const onErrorRef = useRef(onError)
const [fetchState, setFetchState] = useState<
"fetching" | "idle" | "all fetched"
>("idle");
const onSuccessRef = useRef(onSuccess);
const onErrorRef = useRef(onError);
useLayoutEffect(() => {
onSuccessRef.current = onSuccess
onErrorRef.current = onError
}, [onError, onSuccess])
useLayoutEffect(() => {
onSuccessRef.current = onSuccess;
onErrorRef.current = onError;
}, [onError, onSuccess]);
useEffect(() => {
const controller = new AbortController()
useEffect(() => {
const controller = new AbortController();
setFetchState("fetching")
getTariffs(apiPage, tariffsPerPage, controller.signal)
.then(([result]) => {
if (result && result.tariffs.length > 0) {
onSuccessRef.current(result.tariffs)
setFetchState("idle")
} else setFetchState("all fetched")
})
.catch(([_, error]) => {
onErrorRef.current?.(error)
})
const getTariffsRequest = async () => {
setFetchState("fetching");
const [tariffs, tariffsError] = await getTariffs(
apiPage,
tariffsPerPage,
controller.signal
);
return () => controller.abort()
}, [apiPage, tariffsPerPage])
if (tariffsError) {
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;
}