wrap components with error boundaries, minox fixes
This commit is contained in:
parent
ebecee8a49
commit
4490536276
@ -30,8 +30,11 @@ import { ReactComponent as CrossIcon } from "@root/assets/Icons/cross.svg";
|
||||
import { payCart } from "@root/api/cart";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import { withErrorBoundary } from "react-error-boundary";
|
||||
import { handleComponentError } from "@root/utils/handleComponentError";
|
||||
import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline';
|
||||
|
||||
export default function Drawers() {
|
||||
function Drawers() {
|
||||
const [openNotificationsModal, setOpenNotificationsModal] =
|
||||
useState<boolean>(false);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
@ -324,3 +327,15 @@ export default function Drawers() {
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default withErrorBoundary(Drawers, {
|
||||
fallback: (
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
}}>
|
||||
<ErrorOutlineIcon color="error" />
|
||||
</Box>
|
||||
),
|
||||
onError: handleComponentError,
|
||||
})
|
||||
|
@ -13,256 +13,263 @@ import { getMessageFromFetchError } from "@frontend/kitui";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { VerificationStatus } from "@root/model/account";
|
||||
import { verify } from "./helper";
|
||||
import { withErrorBoundary } from "react-error-boundary";
|
||||
import { handleComponentError } from "@root/utils/handleComponentError";
|
||||
|
||||
export default function AccountSettings() {
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const upSm = useMediaQuery(theme.breakpoints.up("sm"));
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
function AccountSettings() {
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const upSm = useMediaQuery(theme.breakpoints.up("sm"));
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
|
||||
const fields = useUserStore((state) => state.settingsFields);
|
||||
const verificationStatus = useUserStore((state) => state.verificationStatus);
|
||||
const verificationType = useUserStore((state) => state.verificationType);
|
||||
const comment = useUserStore((state) => state.comment);
|
||||
const userId = useUserStore((state) => state.userId) ?? "";
|
||||
const fields = useUserStore((state) => state.settingsFields);
|
||||
const verificationStatus = useUserStore((state) => state.verificationStatus);
|
||||
const verificationType = useUserStore((state) => state.verificationType);
|
||||
const comment = useUserStore((state) => state.comment);
|
||||
const userId = useUserStore((state) => state.userId) ?? "";
|
||||
|
||||
useEffect(() => {
|
||||
verify(userId);
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
verify(userId);
|
||||
}, []);
|
||||
|
||||
const textFieldProps = {
|
||||
gap: upMd ? "16px" : "10px",
|
||||
color: "#F2F3F7",
|
||||
bold: true,
|
||||
};
|
||||
const textFieldProps = {
|
||||
gap: upMd ? "16px" : "10px",
|
||||
color: "#F2F3F7",
|
||||
bold: true,
|
||||
};
|
||||
|
||||
const verificationStatusData: Record<VerificationStatus, { text: string; color: string }> = {
|
||||
function handleSendDataClick() {
|
||||
sendUserData()
|
||||
.then(() => {
|
||||
enqueueSnackbar("Информация обновлена");
|
||||
})
|
||||
.catch((error) => {
|
||||
const message = getMessageFromFetchError(error);
|
||||
if (message) enqueueSnackbar(message);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<SectionWrapper
|
||||
maxWidth="lg"
|
||||
sx={{
|
||||
mt: "25px",
|
||||
mb: "70px",
|
||||
px: isTablet ? (isMobile ? "18px" : "40px") : "20px",
|
||||
}}
|
||||
>
|
||||
<DocumentsDialog />
|
||||
<Typography variant="h4" mt="20px">
|
||||
Настройки аккаунта
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
mt: "40px",
|
||||
mb: "40px",
|
||||
backgroundColor: "white",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
borderRadius: "12px",
|
||||
p: "20px",
|
||||
gap: "40px",
|
||||
boxShadow: cardShadow,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "31px",
|
||||
justifyContent: "space-between",
|
||||
flexDirection: upMd ? "row" : "column",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridAutoFlow: upSm ? "column" : "row",
|
||||
gridTemplateRows: "repeat(4, auto)",
|
||||
gridAutoColumns: "1fr",
|
||||
rowGap: "15px",
|
||||
columnGap: "31px",
|
||||
flexGrow: 1,
|
||||
}}
|
||||
>
|
||||
<InputTextfield
|
||||
TextfieldProps={{
|
||||
placeholder: "Имя",
|
||||
value: fields.firstname.value || "",
|
||||
helperText: fields.firstname.touched && fields.firstname.error,
|
||||
error: fields.firstname.touched && Boolean(fields.firstname.error),
|
||||
}}
|
||||
onChange={(e) => setSettingsField("firstname", e.target.value)}
|
||||
id="firstname"
|
||||
label="Имя"
|
||||
{...textFieldProps}
|
||||
/>
|
||||
<InputTextfield
|
||||
TextfieldProps={{
|
||||
placeholder: "Фамилия",
|
||||
value: fields.secondname.value || "",
|
||||
helperText: fields.secondname.touched && fields.secondname.error,
|
||||
error: fields.secondname.touched && Boolean(fields.secondname.error),
|
||||
}}
|
||||
onChange={(e) => setSettingsField("secondname", e.target.value)}
|
||||
id="secondname"
|
||||
label="Фамилия"
|
||||
{...textFieldProps}
|
||||
/>
|
||||
<InputTextfield
|
||||
TextfieldProps={{
|
||||
placeholder: "Отчество",
|
||||
value: fields.middlename.value || "",
|
||||
helperText: fields.middlename.touched && fields.middlename.error,
|
||||
error: fields.middlename.touched && Boolean(fields.middlename.error),
|
||||
}}
|
||||
onChange={(e) => setSettingsField("middlename", e.target.value)}
|
||||
id="middlename"
|
||||
label="Отчество"
|
||||
{...textFieldProps}
|
||||
/>
|
||||
<InputTextfield
|
||||
TextfieldProps={{
|
||||
placeholder: "ООО Фирма",
|
||||
value: fields.orgname.value || "",
|
||||
helperText: fields.orgname.touched && fields.orgname.error,
|
||||
error: fields.orgname.touched && Boolean(fields.orgname.error),
|
||||
}}
|
||||
onChange={(e) => setSettingsField("orgname", e.target.value)}
|
||||
id="orgname"
|
||||
label="Название компании"
|
||||
{...textFieldProps}
|
||||
/>
|
||||
<InputTextfield
|
||||
TextfieldProps={{
|
||||
placeholder: "username@penahaub.com",
|
||||
value: fields.email.value || "",
|
||||
helperText: fields.email.touched && fields.email.error,
|
||||
error: fields.email.touched && Boolean(fields.email.error),
|
||||
}}
|
||||
onChange={(e) => setSettingsField("email", e.target.value)}
|
||||
id="email"
|
||||
label="E-mail"
|
||||
{...textFieldProps}
|
||||
/>
|
||||
<InputTextfield
|
||||
TextfieldProps={{
|
||||
placeholder: "+7 900 000 00 00",
|
||||
value: fields.phoneNumber.value || "",
|
||||
helperText: fields.phoneNumber.touched && fields.phoneNumber.error,
|
||||
error: fields.phoneNumber.touched && Boolean(fields.phoneNumber.error),
|
||||
}}
|
||||
onChange={(e) => setSettingsField("phoneNumber", e.target.value)}
|
||||
id="phoneNumber"
|
||||
label="Телефон"
|
||||
{...textFieldProps}
|
||||
/>
|
||||
<PasswordInput
|
||||
TextfieldProps={{
|
||||
placeholder: "Не менее 8 символов",
|
||||
value: fields.password.value || "",
|
||||
helperText: fields.password.touched && fields.password.error,
|
||||
error: fields.password.touched && Boolean(fields.password.error),
|
||||
autoComplete: "new-password",
|
||||
}}
|
||||
onChange={(e) => setSettingsField("password", e.target.value)}
|
||||
id="password"
|
||||
label="Пароль"
|
||||
{...textFieldProps}
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
maxWidth: "246px",
|
||||
}}
|
||||
>
|
||||
<Typography variant="p1">Статус</Typography>
|
||||
<VerificationIndicator verificationStatus={verificationStatus} sx={{ mt: "16px", p: "14px 7.5px" }} />
|
||||
{verificationStatus === VerificationStatus.NOT_VERIFICATED && (
|
||||
<>
|
||||
<UnderlinedButtonWithIcon
|
||||
icon={<UploadIcon />}
|
||||
sx={{ mt: "55px" }}
|
||||
ButtonProps={{
|
||||
onClick: () => openDocumentsDialog("juridical"),
|
||||
}}
|
||||
>
|
||||
Загрузить документы для юр лиц
|
||||
</UnderlinedButtonWithIcon>
|
||||
<UnderlinedButtonWithIcon
|
||||
icon={<UploadIcon />}
|
||||
sx={{ mt: "15px" }}
|
||||
ButtonProps={{
|
||||
onClick: () => openDocumentsDialog("nko"),
|
||||
}}
|
||||
>
|
||||
Загрузить документы для НКО
|
||||
</UnderlinedButtonWithIcon>
|
||||
</>
|
||||
)}
|
||||
{verificationStatus === VerificationStatus.VERIFICATED && (
|
||||
<UnderlinedButtonWithIcon
|
||||
icon={<EyeIcon />}
|
||||
sx={{ mt: "55px" }}
|
||||
ButtonProps={{
|
||||
onClick: () => openDocumentsDialog(verificationType),
|
||||
}}
|
||||
>
|
||||
Посмотреть свою верификацию
|
||||
</UnderlinedButtonWithIcon>
|
||||
)}
|
||||
{comment && <p>{comment}</p>}
|
||||
</Box>
|
||||
</Box>
|
||||
<Button
|
||||
variant="pena-contained-dark"
|
||||
onClick={handleSendDataClick}
|
||||
disabled={fields.hasError}
|
||||
sx={{ alignSelf: "end" }}
|
||||
>
|
||||
Сохранить
|
||||
</Button>
|
||||
</Box>
|
||||
</SectionWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default withErrorBoundary(AccountSettings, {
|
||||
fallback: <Typography mt="8px" textAlign="center">Ошибка при отображении настроек аккаунта</Typography>,
|
||||
onError: handleComponentError,
|
||||
})
|
||||
|
||||
const verificationStatusData: Record<VerificationStatus, { text: string; color: string; }> = {
|
||||
verificated: { text: "Верификация пройдена", color: "#0D9F00" },
|
||||
waiting: { text: "В ожидании верификации", color: "#F18956" },
|
||||
notVerificated: { text: "Не верифицирован", color: "#E02C2C" },
|
||||
};
|
||||
};
|
||||
|
||||
function handleSendDataClick() {
|
||||
sendUserData()
|
||||
.then(() => {
|
||||
enqueueSnackbar("Информация обновлена");
|
||||
})
|
||||
.catch((error) => {
|
||||
const message = getMessageFromFetchError(error);
|
||||
if (message) enqueueSnackbar(message);
|
||||
});
|
||||
}
|
||||
|
||||
function VerificationIndicator({
|
||||
function VerificationIndicator({
|
||||
verificationStatus,
|
||||
sx,
|
||||
}: {
|
||||
}: {
|
||||
verificationStatus: VerificationStatus;
|
||||
sx?: SxProps<Theme>;
|
||||
}) {
|
||||
}) {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
py: "14px",
|
||||
px: "8.5px",
|
||||
borderWidth: "1px",
|
||||
borderStyle: "solid",
|
||||
color: verificationStatusData[verificationStatus].color,
|
||||
borderColor: verificationStatusData[verificationStatus].color,
|
||||
borderRadius: "8px",
|
||||
textAlign: "center",
|
||||
...sx,
|
||||
}}
|
||||
>
|
||||
<Typography lineHeight="100%">{verificationStatusData[verificationStatus].text}</Typography>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SectionWrapper
|
||||
maxWidth="lg"
|
||||
sx={{
|
||||
mt: "25px",
|
||||
mb: "70px",
|
||||
px: isTablet ? (isMobile ? "18px" : "40px") : "20px",
|
||||
}}
|
||||
>
|
||||
<DocumentsDialog />
|
||||
<Typography variant="h4" mt="20px">
|
||||
Настройки аккаунта
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
mt: "40px",
|
||||
mb: "40px",
|
||||
backgroundColor: "white",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
borderRadius: "12px",
|
||||
p: "20px",
|
||||
gap: "40px",
|
||||
boxShadow: cardShadow,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "31px",
|
||||
justifyContent: "space-between",
|
||||
flexDirection: upMd ? "row" : "column",
|
||||
}}
|
||||
sx={{
|
||||
py: "14px",
|
||||
px: "8.5px",
|
||||
borderWidth: "1px",
|
||||
borderStyle: "solid",
|
||||
color: verificationStatusData[verificationStatus].color,
|
||||
borderColor: verificationStatusData[verificationStatus].color,
|
||||
borderRadius: "8px",
|
||||
textAlign: "center",
|
||||
...sx,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridAutoFlow: upSm ? "column" : "row",
|
||||
gridTemplateRows: "repeat(4, auto)",
|
||||
gridAutoColumns: "1fr",
|
||||
rowGap: "15px",
|
||||
columnGap: "31px",
|
||||
flexGrow: 1,
|
||||
}}
|
||||
>
|
||||
<InputTextfield
|
||||
TextfieldProps={{
|
||||
placeholder: "Имя",
|
||||
value: fields.firstname.value || "",
|
||||
helperText: fields.firstname.touched && fields.firstname.error,
|
||||
error: fields.firstname.touched && Boolean(fields.firstname.error),
|
||||
}}
|
||||
onChange={(e) => setSettingsField("firstname", e.target.value)}
|
||||
id="firstname"
|
||||
label="Имя"
|
||||
{...textFieldProps}
|
||||
/>
|
||||
<InputTextfield
|
||||
TextfieldProps={{
|
||||
placeholder: "Фамилия",
|
||||
value: fields.secondname.value || "",
|
||||
helperText: fields.secondname.touched && fields.secondname.error,
|
||||
error: fields.secondname.touched && Boolean(fields.secondname.error),
|
||||
}}
|
||||
onChange={(e) => setSettingsField("secondname", e.target.value)}
|
||||
id="secondname"
|
||||
label="Фамилия"
|
||||
{...textFieldProps}
|
||||
/>
|
||||
<InputTextfield
|
||||
TextfieldProps={{
|
||||
placeholder: "Отчество",
|
||||
value: fields.middlename.value || "",
|
||||
helperText: fields.middlename.touched && fields.middlename.error,
|
||||
error: fields.middlename.touched && Boolean(fields.middlename.error),
|
||||
}}
|
||||
onChange={(e) => setSettingsField("middlename", e.target.value)}
|
||||
id="middlename"
|
||||
label="Отчество"
|
||||
{...textFieldProps}
|
||||
/>
|
||||
<InputTextfield
|
||||
TextfieldProps={{
|
||||
placeholder: "ООО Фирма",
|
||||
value: fields.orgname.value || "",
|
||||
helperText: fields.orgname.touched && fields.orgname.error,
|
||||
error: fields.orgname.touched && Boolean(fields.orgname.error),
|
||||
}}
|
||||
onChange={(e) => setSettingsField("orgname", e.target.value)}
|
||||
id="orgname"
|
||||
label="Название компании"
|
||||
{...textFieldProps}
|
||||
/>
|
||||
<InputTextfield
|
||||
TextfieldProps={{
|
||||
placeholder: "username@penahaub.com",
|
||||
value: fields.email.value || "",
|
||||
helperText: fields.email.touched && fields.email.error,
|
||||
error: fields.email.touched && Boolean(fields.email.error),
|
||||
}}
|
||||
onChange={(e) => setSettingsField("email", e.target.value)}
|
||||
id="email"
|
||||
label="E-mail"
|
||||
{...textFieldProps}
|
||||
/>
|
||||
<InputTextfield
|
||||
TextfieldProps={{
|
||||
placeholder: "+7 900 000 00 00",
|
||||
value: fields.phoneNumber.value || "",
|
||||
helperText: fields.phoneNumber.touched && fields.phoneNumber.error,
|
||||
error: fields.phoneNumber.touched && Boolean(fields.phoneNumber.error),
|
||||
}}
|
||||
onChange={(e) => setSettingsField("phoneNumber", e.target.value)}
|
||||
id="phoneNumber"
|
||||
label="Телефон"
|
||||
{...textFieldProps}
|
||||
/>
|
||||
<PasswordInput
|
||||
TextfieldProps={{
|
||||
placeholder: "Не менее 8 символов",
|
||||
value: fields.password.value || "",
|
||||
helperText: fields.password.touched && fields.password.error,
|
||||
error: fields.password.touched && Boolean(fields.password.error),
|
||||
autoComplete: "new-password",
|
||||
}}
|
||||
onChange={(e) => setSettingsField("password", e.target.value)}
|
||||
id="password"
|
||||
label="Пароль"
|
||||
{...textFieldProps}
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
maxWidth: "246px",
|
||||
}}
|
||||
>
|
||||
<Typography variant="p1">Статус</Typography>
|
||||
<VerificationIndicator verificationStatus={verificationStatus} sx={{ mt: "16px", p: "14px 7.5px" }} />
|
||||
{verificationStatus === VerificationStatus.NOT_VERIFICATED && (
|
||||
<>
|
||||
<UnderlinedButtonWithIcon
|
||||
icon={<UploadIcon />}
|
||||
sx={{ mt: "55px" }}
|
||||
ButtonProps={{
|
||||
onClick: () => openDocumentsDialog("juridical"),
|
||||
}}
|
||||
>
|
||||
Загрузить документы для юр лиц
|
||||
</UnderlinedButtonWithIcon>
|
||||
<UnderlinedButtonWithIcon
|
||||
icon={<UploadIcon />}
|
||||
sx={{ mt: "15px" }}
|
||||
ButtonProps={{
|
||||
onClick: () => openDocumentsDialog("nko"),
|
||||
}}
|
||||
>
|
||||
Загрузить документы для НКО
|
||||
</UnderlinedButtonWithIcon>
|
||||
</>
|
||||
)}
|
||||
{verificationStatus === VerificationStatus.VERIFICATED && (
|
||||
<UnderlinedButtonWithIcon
|
||||
icon={<EyeIcon />}
|
||||
sx={{ mt: "55px" }}
|
||||
ButtonProps={{
|
||||
onClick: () => openDocumentsDialog(verificationType),
|
||||
}}
|
||||
>
|
||||
Посмотреть свою верификацию
|
||||
</UnderlinedButtonWithIcon>
|
||||
)}
|
||||
{comment && <p>{comment}</p>}
|
||||
</Box>
|
||||
<Typography lineHeight="100%">{verificationStatusData[verificationStatus].text}</Typography>
|
||||
</Box>
|
||||
<Button
|
||||
variant="pena-contained-dark"
|
||||
onClick={handleSendDataClick}
|
||||
disabled={fields.hasError}
|
||||
sx={{ alignSelf: "end" }}
|
||||
>
|
||||
Сохранить
|
||||
</Button>
|
||||
</Box>
|
||||
</SectionWrapper>
|
||||
);
|
||||
);
|
||||
}
|
||||
|
@ -6,8 +6,10 @@ import CustomWrapper from "./CustomWrapper";
|
||||
import { useCart } from "@root/utils/hooks/useCart";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { usePrevLocation } from "@root/utils/hooks/handleCustomBackNavigation";
|
||||
import { handleComponentError } from "@root/utils/handleComponentError";
|
||||
import { withErrorBoundary } from "react-error-boundary";
|
||||
|
||||
export default function Cart() {
|
||||
function Cart() {
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(550));
|
||||
@ -71,3 +73,8 @@ export default function Cart() {
|
||||
</SectionWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default withErrorBoundary(Cart, {
|
||||
fallback: <Typography mt="8px" textAlign="center">Ошибка при отображении корзины</Typography>,
|
||||
onError: handleComponentError,
|
||||
})
|
||||
|
@ -5,8 +5,10 @@ import { useHistoryTracker } from "@root/utils/hooks/useHistoryTracker";
|
||||
import SaveWrapper from "./SaveWrapper";
|
||||
import { useTariffStore } from "@root/stores/tariffs";
|
||||
import { type Tariff } from "@frontend/kitui";
|
||||
import { withErrorBoundary } from "react-error-boundary";
|
||||
import { handleComponentError } from "@root/utils/handleComponentError";
|
||||
|
||||
export default function SavedTariffs() {
|
||||
function SavedTariffs() {
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(550));
|
||||
@ -65,3 +67,8 @@ export default function SavedTariffs() {
|
||||
</SectionWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default withErrorBoundary(SavedTariffs, {
|
||||
fallback: <Typography mt="8px" textAlign="center">Ошибка при отображении сохраненных тарифов</Typography>,
|
||||
onError: handleComponentError,
|
||||
})
|
||||
|
@ -35,8 +35,10 @@ import {
|
||||
useTicketMessages,
|
||||
} from "@frontend/kitui";
|
||||
import { shownMessage, sendTicketMessage } from "@root/api/ticket";
|
||||
import { withErrorBoundary } from "react-error-boundary";
|
||||
import { handleComponentError } from "@root/utils/handleComponentError";
|
||||
|
||||
export default function SupportChat() {
|
||||
function SupportChat() {
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const isMobile = useMediaQuery(theme.breakpoints.up(460));
|
||||
@ -128,7 +130,7 @@ export default function SupportChat() {
|
||||
async function handleSendMessage() {
|
||||
if (!ticket || !messageField) return;
|
||||
|
||||
const [_, sendTicketMessageError] = await sendTicketMessage(
|
||||
const [, sendTicketMessageError] = await sendTicketMessage(
|
||||
ticket.id,
|
||||
messageField
|
||||
);
|
||||
@ -318,3 +320,8 @@ export default function SupportChat() {
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default withErrorBoundary(SupportChat, {
|
||||
fallback: <Typography mt="8px" textAlign="center">Не удалось отобразить чат</Typography>,
|
||||
onError: handleComponentError,
|
||||
})
|
||||
|
@ -5,13 +5,16 @@ import {
|
||||
Box,
|
||||
useTheme,
|
||||
Pagination,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import TicketCard from "./TicketCard";
|
||||
import { setTicketApiPage, useTicketStore } from "@root/stores/tickets";
|
||||
import { Ticket } from "@frontend/kitui";
|
||||
import { withErrorBoundary } from "react-error-boundary";
|
||||
import { handleComponentError } from "@root/utils/handleComponentError";
|
||||
|
||||
|
||||
export default function TicketList() {
|
||||
function TicketList() {
|
||||
const theme = useTheme();
|
||||
const tickets = useTicketStore((state) => state.tickets);
|
||||
const ticketCount = useTicketStore((state) => state.ticketCount);
|
||||
@ -87,3 +90,8 @@ function sortTicketsByUpdateTime(ticket1: Ticket, ticket2: Ticket) {
|
||||
const date2 = new Date(ticket2.updated_at).getTime();
|
||||
return date2 - date1;
|
||||
}
|
||||
|
||||
export default withErrorBoundary(TicketList, {
|
||||
fallback: <Typography mt="8px" textAlign="center">Ошибка загрузки тикетов</Typography>,
|
||||
onError: handleComponentError,
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Box, IconButton, useMediaQuery, useTheme } from "@mui/material";
|
||||
import { Box, IconButton, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||
import { Link } from "react-router-dom";
|
||||
import SectionWrapper from "@components/SectionWrapper";
|
||||
import { useCustomTariffsStore } from "@root/stores/customTariffs";
|
||||
@ -8,8 +8,10 @@ import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
||||
import TotalPrice from "@root/components/TotalPrice";
|
||||
import { serviceNameByKey } from "@root/utils/serviceKeys";
|
||||
import { useHistoryTracker } from "@root/utils/hooks/useHistoryTracker";
|
||||
import { withErrorBoundary } from "react-error-boundary";
|
||||
import { handleComponentError } from "@root/utils/handleComponentError";
|
||||
|
||||
export default function TariffConstructor() {
|
||||
function TariffConstructor() {
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||
@ -84,3 +86,8 @@ export default function TariffConstructor() {
|
||||
</SectionWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default withErrorBoundary(TariffConstructor, {
|
||||
fallback: <Typography mt="8px" textAlign="center">Ошибка при отображении кастомных тарифов</Typography>,
|
||||
onError: handleComponentError,
|
||||
})
|
||||
|
@ -18,6 +18,8 @@ import { Slider } from "./slider";
|
||||
import { useCartStore } from "@root/stores/cart";
|
||||
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
|
||||
import { usePrevLocation } from "@root/utils/hooks/handleCustomBackNavigation";
|
||||
import { withErrorBoundary } from "react-error-boundary";
|
||||
import { handleComponentError } from "@root/utils/handleComponentError";
|
||||
|
||||
const subPages = ["Шаблонизатор", "Опросник", "Сокращатель ссылок"];
|
||||
|
||||
@ -26,7 +28,7 @@ const StepperText: Record<string, string> = {
|
||||
time: "Тарифы на время",
|
||||
};
|
||||
|
||||
export default function TariffPage() {
|
||||
function TariffPage() {
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
@ -178,3 +180,8 @@ export default function TariffPage() {
|
||||
</SectionWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default withErrorBoundary(TariffPage, {
|
||||
fallback: <Typography mt="8px" textAlign="center">Ошибка загрузки тарифов</Typography>,
|
||||
onError: handleComponentError,
|
||||
})
|
||||
|
@ -39,6 +39,6 @@ async function sendErrorsToServer() {
|
||||
// body: errorsQueue,
|
||||
// useToken: true,
|
||||
// });
|
||||
console.log(`Sending ${errorsQueue.length} errors to server`, errorsQueue);
|
||||
console.log(`Fake-sending ${errorsQueue.length} errors to server`, errorsQueue);
|
||||
errorsQueue = [];
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user