fetch error message handling

master
alex 2024-01-25 20:38:59 +01:00
parent 260bebf8dc
commit a8f6f75d28
18 changed files with 766 additions and 1768 deletions

View File

@ -0,0 +1 @@
# Zeit Adler Dashboard

View File

@ -54,6 +54,20 @@
"passwordMinLength": "Passwort muss mindestens {{minLength}} Zeichen lang sein",
"calendarMaxFutureBookingDaysRequired": "Maximaler Buchungszeitraum ist erforderlich",
"calendarMinEarliestBookingTimeRequired": "Minimaler frühester Buchungszeitpunkt ist erforderlich"
},
"request": {
"inputsInvalid": {
"title": "Eingaben ungültig",
"description": "Bitte überprüfen Sie Ihre Eingaben."
},
"failed": {
"title": "Ein Fehler ist aufgetreten",
"description": "Bitte versuchen Sie es erneut."
},
"failedInternetProblem": {
"title": "Anfrage fehlgeschlagen",
"description": "Bitte überprüfen Sie Ihre Internetverbindung und versuchen Sie es erneut."
}
}
},
"pageNotFound": {
@ -98,7 +112,13 @@
},
"login": {
"login": "Anmelden",
"signUp": "Registrieren"
"signUp": "Registrieren",
"request": {
"400": {
"title": "Anmeldung fehlgeschlagen",
"description": "Bitte überprüfen Sie Ihre Eingaben."
}
}
},
"storeServices": {
"pageTitle": "Dienstleistungen",

View File

@ -54,6 +54,20 @@
"passwordMinLength": "Password must be at least {{minLength}} characters",
"calendarMaxFutureBookingDaysRequired": "Please enter the max. future booking days",
"calendarMinEarliestBookingTimeRequired": "Please enter the min. earliest booking time"
},
"request": {
"inputsInvalid": {
"title": "Invalid inputs",
"description": "Please check your inputs and try again."
},
"failed": {
"title": "An error has occurred",
"description": "The request failed. Please try again."
},
"failedInternetProblem": {
"title": "Request failed",
"description": "The request failed. Please check your internet connection and try again."
}
}
},
"pageNotFound": {
@ -98,7 +112,13 @@
},
"login": {
"login": "Login",
"signUp": "Sign up"
"signUp": "Sign up",
"request": {
"400": {
"title": "Login failed",
"description": "Please check your inputs and try again."
}
}
},
"services": {
"pageTitle": "Services"

View File

@ -24,7 +24,10 @@ export default function App() {
useEffect(() => {
if (!userSession) return;
myFetch("/user", "GET")
myFetch({
url: "/user",
method: "GET",
})
.then((data) => {
setAppUserData(data);
})

View File

@ -17,9 +17,7 @@ const StoreWebsite = lazy(() => import("../../Pages/Store/Website"));
//const Feedback = lazy(() => import("../../Pages/Feedback"));
const UserProfile = lazy(() => import("../../Pages/UserProfile"));
export default function AppRoutes({ userSession, setUserSession }) {
//const appContext = useAppContext();
export default function AppRoutes({ setUserSession }) {
return (
<Routes>
<Route
@ -111,10 +109,7 @@ export default function AppRoutes({ userSession, setUserSession }) {
path={Constants.ROUTE_PATHS.USER_PROFILE}
element={
<MySupsenseFallback>
<UserProfile
userSession={userSession}
setUserSession={setUserSession}
/>
<UserProfile setUserSession={setUserSession} />
</MySupsenseFallback>
}
/>

View File

@ -1,65 +1,11 @@
import {
BellOutlined,
CheckCircleOutlined,
CloseCircleOutlined,
CloseOutlined,
DeleteOutlined,
ExclamationCircleOutlined,
InboxOutlined,
InfoCircleOutlined,
MenuFoldOutlined,
MenuUnfoldOutlined,
QuestionCircleOutlined,
} from "@ant-design/icons";
import { Badge, Button, Drawer, List, Popconfirm, Typography } from "antd";
import { MenuFoldOutlined, MenuUnfoldOutlined } from "@ant-design/icons";
import { Button } from "antd";
import { Header } from "antd/es/layout/layout";
import { useEffect, useState } from "react";
import { useHeaderContext } from "../../Contexts/HeaderContext";
import { myFetch } from "../../utils";
import { useWebSocketContext } from "../../Contexts/WebSocketContext";
import { SentMessagesCommands } from "../../Handlers/WebSocketMessageHandler";
import { useTranslation } from "react-i18next";
import LiveTimeAgo from "../LiveTimeAgo";
import MyPagination from "../MyPagination";
export default function HeaderMenu({
isSideMenuCollapsed,
setIsSideMenuCollapsed,
}) {
//const webSocketContext = useWebSocketContext();
//const headerContext = useHeaderContext();
//const { t } = useTranslation();
const [isNotificationDrawerOpen, setIsNotificationDrawerOpen] =
useState(false);
/*
const fetchNotifications = (page = 1) => {
myFetch(`/notifications?page=${page}`, "GET").then((data) =>
headerContext.setNotificationResponse(data)
);
};
const onPaginationChange = (page) => {
headerContext.setPaginationPage(page);
headerContext.paginationPageRef.current = page;
}; */
/*
useEffect(() => {
// fetch will only be called if the drawer is open and there are no notifications
// further notifications will be fetched by the websocket
if (!isNotificationDrawerOpen || headerContext.notficationResponse !== null)
return;
fetchNotifications(1);
}, [isNotificationDrawerOpen]);
useEffect(() => {
if (!isNotificationDrawerOpen) return;
fetchNotifications(headerContext.paginationPage);
}, [headerContext.paginationPage]); */
return (
<Header
style={{
@ -85,112 +31,3 @@ export default function HeaderMenu({
</Header>
);
}
/*
<Button
type="text"
icon={
<Badge count={headerContext.totalNotifications} offset={[2, -2]}>
<BellOutlined style={{ fontSize: "16px" }} />
</Badge>
}
onClick={() => setIsNotificationDrawerOpen(true)}
style={{ fontSize: "16px", width: 64, height: 64 }}
/>
<Drawer
title={t("header.notificationDrawer.title")}
placement="right"
open={isNotificationDrawerOpen}
onClose={() => setIsNotificationDrawerOpen(false)}
extra={
headerContext.totalNotifications > 0 && (
<Popconfirm
title={t("header.notificationDrawer.deleteAllPopconfirm.title")}
okText={t("common.button.confirm")}
cancelText={t("common.button.cancel")}
onConfirm={() => {
webSocketContext.SendSocketMessage(
SentMessagesCommands.DeleteAllNotifications,
{}
);
setIsNotificationDrawerOpen(false);
}}
>
<Button type="link" icon={<DeleteOutlined />}>
{t("header.notificationDrawer.deleteAllButtonText")}
</Button>
</Popconfirm>
)
}
>
{isNotificationDrawerOpen && (
<>
{headerContext.totalNotifications === 0 ||
headerContext.notficationResponse === null ? (
<div style={{ textAlign: "center" }}>
<InboxOutlined style={{ fontSize: 32, marginBottom: 10 }} />
<Typography.Title level={5}>
{t("header.notificationDrawer.noNotifications")}
</Typography.Title>
</div>
) : (
<List
dataSource={headerContext.notficationResponse.Notifications.sort(
(a, b) => {
return new Date(b.CreatedAt) - new Date(a.CreatedAt);
}
)}
footer={
<MyPagination
paginationPage={headerContext.paginationPage}
setPaginationPage={(page) => onPaginationChange(page)}
totalPages={headerContext.notficationResponse.TotalPages}
size="small"
/>
}
renderItem={(item) => (
<List.Item>
<List.Item.Meta
avatar={<NotificationTypeIcon type={item.Type} />}
title={item.Title}
description={<LiveTimeAgo startTime={item.CreatedAt} />}
/>
<CloseOutlined
onClick={() => {
/*webSocketContext.SendSocketMessage(
SentMessagesCommands.DeleteOneNotification,
{
notificationId: item.Id,
}
)
}}
/>
</List.Item>
)}
/>
)}
</>
)}
</Drawer>
*/
function NotificationTypeIcon({ type }) {
switch (type) {
case 1:
return <CheckCircleOutlined color="#33a834" style={{ fontSize: 16 }} />;
case 2:
return <InfoCircleOutlined color="#0c69d7" style={{ fontSize: 16 }} />;
case 3:
return (
<ExclamationCircleOutlined color="#dd9433" style={{ fontSize: 16 }} />
);
case 4:
return <CloseCircleOutlined color="#e5444b" style={{ fontSize: 16 }} />;
default:
return <QuestionCircleOutlined color="#fff" style={{ fontSize: 16 }} />;
}
}

View File

@ -187,7 +187,11 @@ export function MyAvailableCheckFormInput({
body[fetchParameter] = value; // like accountName: value
myFetch(fetchUrl, "POST", body)
myFetch({
url: fetchUrl,
method: "POST",
body: body,
})
.then(() => {
resolve();
})

View File

@ -4,7 +4,7 @@ import {
PlusOutlined,
QuestionCircleOutlined,
} from "@ant-design/icons";
import { Popconfirm, Tooltip } from "antd";
import { Popconfirm } from "antd";
import { useState } from "react";
import { useTranslation } from "react-i18next";
@ -79,14 +79,14 @@ export function MyPlusIcon({ onClick }) {
export function MyIcon({
popconfirmDisabled,
propsPopconfirm,
propsTooltip,
// propsTooltip,
onConfirm,
onFetchSuccess,
onCancel,
popConfirmTitle,
popConfirmDescription,
popConfirmOkText,
tooltipTitle,
//tooltipTitle,
icon,
}) {
const { t } = useTranslation();

View File

@ -3,8 +3,8 @@ import { Button, Form, Modal, Tabs, notification } from "antd";
import {
EncodeStringToBase64,
myFetch,
myFetchContentType,
setUserSessionToLocalStorage,
showInputsInvalidNotification,
} from "../../utils";
import { useState } from "react";
import {
@ -19,31 +19,27 @@ export default function Login() {
const { t } = useTranslation();
const [form] = Form.useForm();
const [api, contextHolder] = notification.useNotification();
const [notificationApi, notificationContextHolder] =
notification.useNotification();
const [selectedMethod, setSelectedMethod] = useState("1");
const [isRequesting, setIsRequesting] = useState(false);
const showErrorNotification = (errStatus) => {
if (errStatus === 401) {
api["error"]({
message: "Account deactivated",
description: "Please contact an administrator",
if (errStatus === 400) {
notificationApi["error"]({
message: t("login.request.400.title"),
description: t("login.request.400.description"),
});
return;
}
api["error"]({
message: "Login failed",
description: "Please check your accountName and password!",
});
};
return (
<>
{contextHolder}
{notificationContextHolder}
<Modal
open={true}
mask={false}
closable={false}
centered
keyboard={false}
@ -68,18 +64,16 @@ export default function Login() {
body.username = values.username;
}
myFetch(
`/user/auth/${selectedMethod === "1" ? "login" : "signup"}`,
"POST",
body,
{},
myFetchContentType.JSON,
"",
true
)
myFetch({
url: `/user/auth/${
selectedMethod === "1" ? "login" : "signup"
}`,
method: "POST",
body: body,
notificationApi: notificationApi,
t: t,
})
.then((data) => {
console.log(data.XAuthorization);
setUserSessionToLocalStorage(data.XAuthorization);
window.location.href = "/";
})
@ -88,9 +82,7 @@ export default function Login() {
setIsRequesting(false);
});
})
.catch((info) => {
console.log("Validate Failed:", info);
});
.catch(() => showInputsInvalidNotification(notificationApi, t));
}}
>
{selectedMethod === "1" ? t("login.login") : t("login.signUp")}
@ -119,9 +111,7 @@ export default function Login() {
},
]}
centered
onChange={(activeKey) => {
setSelectedMethod(activeKey);
}}
onChange={(activeKey) => setSelectedMethod(activeKey)}
/>
<Form form={form} layout="vertical" requiredMark={false}>

View File

@ -1,20 +1,27 @@
import { Button, Result, Spin } from "antd";
import { Button, Result, Spin, notification } from "antd";
import { Link, useParams, useNavigate } from "react-router-dom";
import { Constants, myFetch } from "../../../../utils";
import { useEffect, useState } from "react";
import MyCenteredContainer from "../../../../Components/MyContainer";
import { useTranslation } from "react-i18next";
import { MySupsenseFallback } from "../../../../Components/MySupsenseFallback";
export default function StoreCalendarAuth() {
const { t } = useTranslation();
const [notificationApi, notificationContextHolder] =
notification.useNotification();
const { status } = useParams();
const [isRequesting, setIsRequesting] = useState(true);
const [storeId, setStoreId] = useState("");
useEffect(() => {
myFetch("/calendar/store", "GET")
myFetch({
url: "/calendar/store",
method: "GET",
notificationApi: notificationApi,
t: t,
})
.then((res) => {
setIsRequesting(false);
setStoreId(res.storeId);
@ -33,6 +40,9 @@ export default function StoreCalendarAuth() {
}
return (
<>
{notificationContextHolder}
<Result
status={status === "finish" ? "success" : "error"}
title={
@ -59,6 +69,7 @@ export default function StoreCalendarAuth() {
</div>,
]}
/>
</>
);
}

View File

@ -10,6 +10,7 @@ import {
Spin,
Switch,
Typography,
notification,
} from "antd";
import { useTranslation } from "react-i18next";
import {
@ -38,6 +39,9 @@ const { useBreakpoint } = Grid;
export default function StoreCalendar() {
const { t } = useTranslation();
const [notificationApi, notificationContextHolder] =
notification.useNotification();
const { storeId } = useParams();
const [calendarSettings, setCalendarSettings] = useState({});
@ -47,7 +51,12 @@ export default function StoreCalendar() {
// delete session cookie
document.cookie = `session=; path=/; expires=Thu, 01 Jan 1970 00:00:01 GMT;`;
myFetch("/calendar/settings", "GET")
myFetch({
url: "/calendar/settings",
method: "GET",
notificationApi: notificationApi,
t: t,
})
.then((res) => {
setIsRequesting(false);
setCalendarSettings(res);
@ -67,6 +76,8 @@ export default function StoreCalendar() {
return (
<>
{notificationContextHolder}
{calendarSettings.connected === false ? (
<MyCenteredContainer>
<Result
@ -135,6 +146,7 @@ function CalendarFrame({ storeId }) {
</div>
)}
<iframe
title="calendar"
onLoad={() => setIsLoading(false)}
style={{ border: 0, borderRadius: 12 }}
width="100%"
@ -147,6 +159,9 @@ function CalendarFrame({ storeId }) {
function CardPersonalCalendarSettings({ settings }) {
const { t } = useTranslation();
const [notificationApi, notificationContextHolder] =
notification.useNotification();
const [form] = Form.useForm();
const [formUnlinkCalendar] = Form.useForm();
const screenBreakpoint = useBreakpoint();
@ -189,8 +204,14 @@ function CardPersonalCalendarSettings({ settings }) {
}
delayTimeout.current = setTimeout(() => {
myFetch("/calendar/settings/personal", "POST", {
myFetch({
url: "/calendar/settings/personal",
method: "POST",
body: {
calendarUsingPrimaryCalendar: usingPrimaryCalendar,
},
notificationApi: notificationApi,
t: t,
})
.then(() => setRequestState(RequestState.SUCCESS))
.catch((errStatus) => {
@ -239,6 +260,8 @@ function CardPersonalCalendarSettings({ settings }) {
return (
<>
{notificationContextHolder}
<Card
title={
screenBreakpoint.xl ? (
@ -290,8 +313,14 @@ function CardPersonalCalendarSettings({ settings }) {
formUnlinkCalendar.validateFields().then((values) => {
setIsRequesting(true);
myFetch("/calendar/settings/personal/unlink", "POST", {
myFetch({
url: "/calendar/settings/personal/unlink",
method: "POST",
body: {
password: EncodeStringToBase64(values.password),
},
notificationApi: notificationApi,
t: t,
})
.then(() => window.location.reload())
.catch((errStatus) => {
@ -321,6 +350,9 @@ function CardPersonalCalendarSettings({ settings }) {
function CardStoreCalendarSettings({ settings }) {
const { t } = useTranslation();
const [notificationApi, notificationContextHolder] =
notification.useNotification();
const [form] = Form.useForm();
const [requestState, setRequestState] = useState(RequestState.INIT);
@ -369,9 +401,15 @@ function CardStoreCalendarSettings({ settings }) {
}
delayTimeout.current = setTimeout(() => {
myFetch("/calendar/settings/store", "POST", {
myFetch({
url: "/calendar/settings/store",
method: "POST",
body: {
calendarMaxFutureBookingDays,
calendarMinEarliestBookingTime,
},
notificationApi: notificationApi,
t: t,
})
.then(() => setRequestState(RequestState.SUCCESS))
.catch((errStatus) => {
@ -382,6 +420,9 @@ function CardStoreCalendarSettings({ settings }) {
}, [calendarMaxFutureBookingDays, calendarMinEarliestBookingTime]);
return (
<>
{notificationContextHolder}
<Card
title={t("calendar.cardStoreCalendarSettings.title")}
extra={
@ -399,5 +440,6 @@ function CardStoreCalendarSettings({ settings }) {
</>
</Form>
</Card>
</>
);
}

View File

@ -1,5 +1,13 @@
import { PlusOutlined } from "@ant-design/icons";
import { Button, Checkbox, Form, Grid, Popconfirm, Space } from "antd";
import {
Button,
Checkbox,
Form,
Grid,
Popconfirm,
Space,
notification,
} from "antd";
import MyModal, {
MyModalCloseCreateButtonFooter,
MyModalCloseSaveButtonFooter,
@ -21,6 +29,9 @@ const { useBreakpoint } = Grid;
export default function StoreEmployees() {
const { t } = useTranslation();
const [notificationApi, notificationContextHolder] =
notification.useNotification();
const screenBreakpoint = useBreakpoint();
const { storeId } = useParams();
@ -85,8 +96,12 @@ export default function StoreEmployees() {
onConfirm={() => {
setIsRequesting(true);
myFetch("/users", "DELETE", {
userId: record.key,
myFetch({
url: "/users",
method: "DELETE",
body: { userId: record.key },
notificationApi: notificationApi,
t: t,
})
.then(() => fetchEmployees())
.catch((errStatus) => {
@ -123,7 +138,12 @@ export default function StoreEmployees() {
const fetchEmployees = () => {
setIsRequesting(true);
myFetch(`/users/${storeId}`, "GET")
myFetch({
url: `/users/${storeId}`,
method: "GET",
notificationApi: notification,
t: t,
})
.then((data) => {
setIsRequesting(false);
setRequestData(data);
@ -137,6 +157,8 @@ export default function StoreEmployees() {
return (
<>
{notificationContextHolder}
<div
style={{
display: "flex",
@ -271,7 +293,13 @@ function ModalAddEditEmployee({
values.calendarMinEarliestBookingTime;
}
myFetch("/users", "POST", body)
myFetch({
url: "/users",
method: "POST",
body: body,
notificationApi: notification,
t: t,
})
.then(() => {
setIsRequesting(false);
handleModalClose();
@ -357,7 +385,13 @@ function ModalAddEditEmployee({
.then(() => {
setIsRequesting(true);
myFetch("/users/update", "POST", body)
myFetch({
url: "/users/update",
method: "POST",
body: body,
notificationApi: notification,
t: t,
})
.then(() => {
setIsRequesting(false);
handleModalClose();

View File

@ -11,6 +11,7 @@ import {
Spin,
Tooltip,
Typography,
notification,
} from "antd";
import { useTranslation } from "react-i18next";
import { useEffect, useState } from "react";
@ -33,6 +34,8 @@ const { useBreakpoint } = Grid;
export default function StoreServices() {
const { t } = useTranslation();
const [notificationApi, notificationContextHolder] =
notification.useNotification();
const screenBreakpoint = useBreakpoint();
const { storeId } = useParams();
@ -58,7 +61,12 @@ export default function StoreServices() {
const fetchServices = () => {
setIsRequestingServices(true);
myFetch(`/store/services/${storeId}`, "GET")
myFetch({
url: `/store/services/${storeId}`,
method: "GET",
notificationApi: notificationApi,
t: t,
})
.then((data) => {
setIsRequestingServices(false);
setServicesData(data);
@ -72,6 +80,8 @@ export default function StoreServices() {
return (
<>
{notificationContextHolder}
<div
style={{
display: "flex",
@ -148,6 +158,8 @@ function Service({
fetchServices,
}) {
const { t } = useTranslation();
const [notificationApi, notificationContextHolder] =
notification.useNotification();
const [isRequestingActivities, setIsRequestingActivities] = useState(false);
const [isOpen, setIsOpen] = useState(false);
@ -159,10 +171,12 @@ function Service({
setIsRequestingActivities(true);
myFetch(
`/store/services/activities/${storeId}/${service.service_id}`,
"GET"
)
myFetch({
url: `/store/services/activities/${storeId}/${service.service_id}`,
method: "GET",
notificationApi: notificationApi,
t: t,
})
.then((data) => {
setIsRequestingActivities(false);
setServiceActivities(data.activities);
@ -175,6 +189,9 @@ function Service({
useEffect(() => fetchServiceActivities(), [isOpen]);
return (
<>
{notificationContextHolder}
<Collapse
key={service.service_id}
onChange={(e) => setIsOpen(e.length !== 0)}
@ -231,10 +248,12 @@ function Service({
placement: "left",
}}
onConfirm={() => {
return myFetch(
`/store/services/${service.service_id}`,
"DELETE"
);
return myFetch({
url: `/store/services/${service.service_id}`,
method: "DELETE",
notificationApi: notificationApi,
t: t,
});
}}
onFetchSuccess={fetchServices}
popConfirmTitle={t(
@ -334,10 +353,12 @@ function Service({
placement: "left",
}}
onConfirm={() => {
return myFetch(
`/store/services/activity/${activity.activity_id}`,
"DELETE"
);
return myFetch({
url: `/store/services/activity/${activity.activity_id}`,
method: "DELETE",
notificationApi: notificationApi,
t: t,
});
}}
onFetchSuccess={fetchServiceActivities}
popConfirmTitle={t(
@ -365,7 +386,9 @@ function Service({
<p>{activity.price} </p>
<Typography.Title level={5}>
{t("storeServices.serviceActivityDurationMinutes")}
{t(
"storeServices.serviceActivityDurationMinutes"
)}
</Typography.Title>
<Typography.Paragraph>
@ -374,7 +397,10 @@ function Service({
? t("common.unit.minute")
: t("common.unit.minutes")}{" "}
<Typography.Text type="secondary">
{durationToHoursAndMinutes(t, activity.duration)}
{durationToHoursAndMinutes(
t,
activity.duration
)}
</Typography.Text>
</Typography.Paragraph>
</Card>
@ -388,6 +414,7 @@ function Service({
},
]}
/>
</>
);
}
@ -430,6 +457,8 @@ function ModalAddEditService({
setAddEditServiceModalOptions,
}) {
const { t } = useTranslation();
const [notificationApi, notificationContextHolder] =
notification.useNotification();
const screenBreakpoint = useBreakpoint();
const [form] = Form.useForm();
@ -456,6 +485,8 @@ function ModalAddEditService({
return (
<>
{notificationContextHolder}
<Button
type="primary"
icon={<PlusOutlined />}
@ -489,9 +520,15 @@ function ModalAddEditService({
.then((values) => {
setIsRequesting(true);
myFetch("/store/services", "POST", {
myFetch({
url: "/store/services",
method: "POST",
body: {
storeId: storeId,
name: values.serviceName,
},
notificationApi: notificationApi,
t: t,
})
.then(() => {
setIsRequesting(false);
@ -527,9 +564,16 @@ function ModalAddEditService({
.then(() => {
setIsRequesting(true);
myFetch("/store/services/update", "POST", {
serviceId: addEditServiceModalOptions.service.service_id,
myFetch({
url: "/store/services/update",
method: "POST",
body: {
serviceId:
addEditServiceModalOptions.service.service_id,
name: formServiceName,
},
notificationApi: notificationApi,
t: t,
})
.then(() => {
setIsRequesting(false);
@ -564,7 +608,8 @@ function ModalAddEditServiceActivity({
users,
}) {
const { t } = useTranslation();
const [notificationApi, notificationContextHolder] =
notification.useNotification();
const [form] = Form.useForm();
const [isRequesting, setIsRequesting] = useState(false);
const [selectedEmployeesRowKeys, setSelectedEmployeesRowKeys] = useState([]);
@ -629,6 +674,9 @@ function ModalAddEditServiceActivity({
}, [addEditServiceActivityModalOptions.isOpen]);
return (
<>
{notificationContextHolder}
<MyModal
title={
addEditServiceActivityModalOptions.mode === "add"
@ -648,7 +696,10 @@ function ModalAddEditServiceActivity({
.then((values) => {
setIsRequesting(true);
myFetch("/store/services/activity", "POST", {
myFetch({
url: "/store/services/activity",
method: "POST",
body: {
serviceId:
addEditServiceActivityModalOptions.service.service_id,
name: values.serviceActivityName,
@ -659,6 +710,9 @@ function ModalAddEditServiceActivity({
selectedEmployeesRowKeys.length === users.length
? []
: selectedEmployeesRowKeys,
},
notificationApi: notificationApi,
t: t,
})
.then(() => {
setIsRequesting(false);
@ -759,7 +813,13 @@ function ModalAddEditServiceActivity({
.then(() => {
setIsRequesting(true);
myFetch("/store/services/activity/update", "POST", body)
myFetch({
url: "/store/services/activity/update",
method: "POST",
body: body,
notificationApi: notificationApi,
t: t,
})
.then(() => {
setIsRequesting(false);
handleModalClose();
@ -809,6 +869,7 @@ function ModalAddEditServiceActivity({
</Space>
</Form>
</MyModal>
</>
);
}

View File

@ -1,7 +1,7 @@
import { useEffect, useRef, useState } from "react";
import { myFetch } from "../../../utils";
import { useParams } from "react-router-dom";
import { Card, Form } from "antd";
import { Card, Form, notification } from "antd";
import { MyFormInput } from "../../../Components/MyFormInputs";
import { useTranslation } from "react-i18next";
import {
@ -11,6 +11,8 @@ import {
export default function StoreSettings() {
const { t } = useTranslation();
const [notificationApi, notificationContextHolder] =
notification.useNotification();
const { storeId } = useParams();
const [form] = Form.useForm();
@ -26,7 +28,12 @@ export default function StoreSettings() {
const address = Form.useWatch("address", form);
useEffect(() => {
myFetch(`/store/${storeId}`, "GET")
myFetch({
url: `/store/${storeId}`,
method: "GET",
notificationApi: notificationApi,
t: t,
})
.then((data) => {
setStoreData(data.store);
@ -65,11 +72,17 @@ export default function StoreSettings() {
}
delayTimeout.current = setTimeout(() => {
myFetch(`/store/${storeId}`, "POST", {
myFetch({
url: `/store/${storeId}`,
method: "POST",
body: {
name: companyName,
phoneNumber,
email,
address,
},
notificationApi: notificationApi,
t: t,
})
.then(() => setRequestState(RequestState.SUCCESS))
.catch((errStatus) => {
@ -81,6 +94,8 @@ export default function StoreSettings() {
return (
<>
{notificationContextHolder}
<Card
title={t("storeSettings.pageTitle")}
loading={isRequesting}

View File

@ -2,7 +2,7 @@ import { lazy, useEffect, useState } from "react";
import { isDevelopmentEnv, myFetch } from "../../../utils";
import { useParams } from "react-router-dom";
import MyCenteredContainer from "../../../Components/MyContainer";
import { Button, Card, Result, Spin, Tabs } from "antd";
import { Button, Card, Result, Spin, Tabs, notification } from "antd";
import { useTranslation } from "react-i18next";
import { MySupsenseFallback } from "../../../Components/MySupsenseFallback";
import PageInDevelopment from "../../PageInDevelopment";
@ -20,6 +20,8 @@ function SuspenseFallback({ children }) {
export default function Website() {
const { storeId } = useParams();
const { t } = useTranslation();
const [notificationApi, notificationContextHolder] =
notification.useNotification();
const [isRequesting, setIsRequesting] = useState(true);
const [website, setWebsite] = useState({});
@ -48,10 +50,13 @@ export default function Website() {
});
useEffect(() => {
myFetch(`/website/${storeId}`, "GET")
.then((res) => {
setIsRequesting(false);
myFetch({
url: `/website/${storeId}`,
method: "GET",
notificationApi: notificationApi,
t: t,
})
.then(() => setIsRequesting(false))
.catch((err) => {
setIsRequesting(false);
@ -76,7 +81,9 @@ export default function Website() {
}
return (
<div>
<>
{notificationContextHolder}
<Card>
<Tabs
type="card"
@ -86,12 +93,15 @@ export default function Website() {
onChange={(key) => setActiveTab(key)}
/>
</Card>
</div>
</>
);
}
function NoWebsiteCreateOne({ setWebsite }) {
const { t } = useTranslation();
const [notificationApi, notificationContextHolder] =
notification.useNotification();
const { storeId } = useParams();
const [isRequesting, setIsRequesting] = useState(false);
@ -99,8 +109,14 @@ function NoWebsiteCreateOne({ setWebsite }) {
const handleCreateWebsite = () => {
setIsRequesting(true);
myFetch("/website", "POST", {
myFetch({
url: "/website",
method: "POST",
body: {
storeId,
},
notificationApi: notificationApi,
t: t,
})
.then((res) => {
console.log(res);
@ -116,6 +132,8 @@ function NoWebsiteCreateOne({ setWebsite }) {
return (
<MyCenteredContainer>
{notificationContextHolder}
<Result
status="404"
title={t("storeWebsite.noWebsite.title")}

View File

@ -1,28 +1,39 @@
import { Button, Card, Select, Typography } from "antd";
import { Constants } from "../../utils";
import { Button, Card, Select, Typography, notification } from "antd";
import { myFetch } from "../../utils";
import { useTranslation } from "react-i18next";
import { useState } from "react";
export default function UserProfile({ userSession, setUserSession }) {
export default function UserProfile({ setUserSession }) {
const { t, i18n } = useTranslation();
const [notificationApi, notificationContextHolder] =
notification.useNotification();
const [isRequestingLogout, setIsRequestingLogout] = useState(false);
return (
<>
{notificationContextHolder}
<Card
title={t("userProfile.title")}
extra={
<Button
type="primary"
loading={isRequestingLogout}
onClick={() => {
setIsRequestingLogout(true);
myFetch({
url: "/user/auth/logout",
method: "DELETE",
notificationApi: notificationApi,
t: t,
})
.then(() => {
setUserSession();
window.location.href = "/";
fetch(`${Constants.API_ADDRESS}/user/auth/logout`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
"X-Authorization": userSession,
},
}).catch(console.error);
})
.catch(() => setIsRequestingLogout(false));
}}
>
{t("common.button.logout")}

View File

@ -6,6 +6,7 @@ body {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-color: #f5f5f5;
}
code {

File diff suppressed because it is too large Load Diff