fetch error message handling
parent
260bebf8dc
commit
a8f6f75d28
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -24,7 +24,10 @@ export default function App() {
|
|||
useEffect(() => {
|
||||
if (!userSession) return;
|
||||
|
||||
myFetch("/user", "GET")
|
||||
myFetch({
|
||||
url: "/user",
|
||||
method: "GET",
|
||||
})
|
||||
.then((data) => {
|
||||
setAppUserData(data);
|
||||
})
|
||||
|
|
|
@ -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>
|
||||
}
|
||||
/>
|
||||
|
|
|
@ -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 }} />;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
})
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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}>
|
||||
|
|
|
@ -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,32 +40,36 @@ export default function StoreCalendarAuth() {
|
|||
}
|
||||
|
||||
return (
|
||||
<Result
|
||||
status={status === "finish" ? "success" : "error"}
|
||||
title={
|
||||
status === "finish"
|
||||
? t("calendar.authFinish.title")
|
||||
: t("calendar.authFailed.title")
|
||||
}
|
||||
subTitle={
|
||||
status === "finish"
|
||||
? t("calendar.authFinish.description")
|
||||
: t("calendar.authFailed.description")
|
||||
}
|
||||
extra={[
|
||||
<div key="1">
|
||||
<Link to={`${Constants.ROUTE_PATHS.STORE.CALENDAR}/${storeId}`}>
|
||||
<Button>
|
||||
{status === "finish"
|
||||
? t("calendar.authFinish.button")
|
||||
: t("calendar.authFailed.button")}
|
||||
</Button>
|
||||
</Link>
|
||||
<>
|
||||
{notificationContextHolder}
|
||||
|
||||
{status === "finish" && <CountdownRedirect storeId={storeId} />}
|
||||
</div>,
|
||||
]}
|
||||
/>
|
||||
<Result
|
||||
status={status === "finish" ? "success" : "error"}
|
||||
title={
|
||||
status === "finish"
|
||||
? t("calendar.authFinish.title")
|
||||
: t("calendar.authFailed.title")
|
||||
}
|
||||
subTitle={
|
||||
status === "finish"
|
||||
? t("calendar.authFinish.description")
|
||||
: t("calendar.authFailed.description")
|
||||
}
|
||||
extra={[
|
||||
<div key="1">
|
||||
<Link to={`${Constants.ROUTE_PATHS.STORE.CALENDAR}/${storeId}`}>
|
||||
<Button>
|
||||
{status === "finish"
|
||||
? t("calendar.authFinish.button")
|
||||
: t("calendar.authFailed.button")}
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
{status === "finish" && <CountdownRedirect storeId={storeId} />}
|
||||
</div>,
|
||||
]}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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", {
|
||||
calendarUsingPrimaryCalendar: usingPrimaryCalendar,
|
||||
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", {
|
||||
password: EncodeStringToBase64(values.password),
|
||||
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", {
|
||||
calendarMaxFutureBookingDays,
|
||||
calendarMinEarliestBookingTime,
|
||||
myFetch({
|
||||
url: "/calendar/settings/store",
|
||||
method: "POST",
|
||||
body: {
|
||||
calendarMaxFutureBookingDays,
|
||||
calendarMinEarliestBookingTime,
|
||||
},
|
||||
notificationApi: notificationApi,
|
||||
t: t,
|
||||
})
|
||||
.then(() => setRequestState(RequestState.SUCCESS))
|
||||
.catch((errStatus) => {
|
||||
|
@ -382,22 +420,26 @@ function CardStoreCalendarSettings({ settings }) {
|
|||
}, [calendarMaxFutureBookingDays, calendarMinEarliestBookingTime]);
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={t("calendar.cardStoreCalendarSettings.title")}
|
||||
extra={
|
||||
<RequestStateItem
|
||||
state={requestState}
|
||||
setRequestState={setRequestState}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Form form={form} requiredMark={false}>
|
||||
<>
|
||||
<MyCalendarMaxFutureBookingDaysFormInput />
|
||||
<>
|
||||
{notificationContextHolder}
|
||||
|
||||
<MyCalendarMinEarliestBookingTimeFormInput />
|
||||
</>
|
||||
</Form>
|
||||
</Card>
|
||||
<Card
|
||||
title={t("calendar.cardStoreCalendarSettings.title")}
|
||||
extra={
|
||||
<RequestStateItem
|
||||
state={requestState}
|
||||
setRequestState={setRequestState}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Form form={form} requiredMark={false}>
|
||||
<>
|
||||
<MyCalendarMaxFutureBookingDaysFormInput />
|
||||
|
||||
<MyCalendarMinEarliestBookingTimeFormInput />
|
||||
</>
|
||||
</Form>
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,24 +189,27 @@ function Service({
|
|||
useEffect(() => fetchServiceActivities(), [isOpen]);
|
||||
|
||||
return (
|
||||
<Collapse
|
||||
key={service.service_id}
|
||||
onChange={(e) => setIsOpen(e.length !== 0)}
|
||||
items={[
|
||||
{
|
||||
key: "1",
|
||||
label: (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<span>{service.name}</span>
|
||||
<>
|
||||
{notificationContextHolder}
|
||||
|
||||
<Space>
|
||||
{/*
|
||||
<Collapse
|
||||
key={service.service_id}
|
||||
onChange={(e) => setIsOpen(e.length !== 0)}
|
||||
items={[
|
||||
{
|
||||
key: "1",
|
||||
label: (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<span>{service.name}</span>
|
||||
|
||||
<Space>
|
||||
{/*
|
||||
<ArrowUpOutlined
|
||||
disabled
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
|
@ -202,192 +219,202 @@ function Service({
|
|||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
*/}
|
||||
<MyPlusIcon
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
<MyPlusIcon
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
setAddEditServiceActivityModalOptions({
|
||||
mode: "add",
|
||||
isOpen: true,
|
||||
service: service,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
setAddEditServiceActivityModalOptions({
|
||||
mode: "add",
|
||||
isOpen: true,
|
||||
service: service,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
<MyEditIcon
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
<MyEditIcon
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
setAddEditServiceModalOptions({
|
||||
mode: "edit",
|
||||
isOpen: true,
|
||||
service: service,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<MyDeleteIcon
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
propsPopconfirm={{
|
||||
placement: "left",
|
||||
}}
|
||||
onConfirm={() => {
|
||||
return myFetch(
|
||||
`/store/services/${service.service_id}`,
|
||||
"DELETE"
|
||||
);
|
||||
}}
|
||||
onFetchSuccess={fetchServices}
|
||||
popConfirmTitle={t(
|
||||
"storeServices.popConfirmDeleteService.title"
|
||||
)}
|
||||
popConfirmDescription={t(
|
||||
"storeServices.popConfirmDeleteService.description"
|
||||
)}
|
||||
/>
|
||||
</Space>
|
||||
</div>
|
||||
),
|
||||
children: (
|
||||
<Space
|
||||
key={`space-${service.service_id}`}
|
||||
direction="vertical"
|
||||
style={{ width: "100%" }}
|
||||
>
|
||||
{isRequestingActivities ? (
|
||||
<Skeleton active>
|
||||
<Card title="loading">
|
||||
<p>loading</p>
|
||||
<p>loading</p>
|
||||
<p>loading</p>
|
||||
</Card>
|
||||
</Skeleton>
|
||||
) : (
|
||||
<>
|
||||
{serviceActivities.length === 0 ? (
|
||||
<MyEmpty />
|
||||
) : (
|
||||
serviceActivities.map((activity) => {
|
||||
let userList = [];
|
||||
setAddEditServiceModalOptions({
|
||||
mode: "edit",
|
||||
isOpen: true,
|
||||
service: service,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<MyDeleteIcon
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
propsPopconfirm={{
|
||||
placement: "left",
|
||||
}}
|
||||
onConfirm={() => {
|
||||
return myFetch({
|
||||
url: `/store/services/${service.service_id}`,
|
||||
method: "DELETE",
|
||||
notificationApi: notificationApi,
|
||||
t: t,
|
||||
});
|
||||
}}
|
||||
onFetchSuccess={fetchServices}
|
||||
popConfirmTitle={t(
|
||||
"storeServices.popConfirmDeleteService.title"
|
||||
)}
|
||||
popConfirmDescription={t(
|
||||
"storeServices.popConfirmDeleteService.description"
|
||||
)}
|
||||
/>
|
||||
</Space>
|
||||
</div>
|
||||
),
|
||||
children: (
|
||||
<Space
|
||||
key={`space-${service.service_id}`}
|
||||
direction="vertical"
|
||||
style={{ width: "100%" }}
|
||||
>
|
||||
{isRequestingActivities ? (
|
||||
<Skeleton active>
|
||||
<Card title="loading">
|
||||
<p>loading</p>
|
||||
<p>loading</p>
|
||||
<p>loading</p>
|
||||
</Card>
|
||||
</Skeleton>
|
||||
) : (
|
||||
<>
|
||||
{serviceActivities.length === 0 ? (
|
||||
<MyEmpty />
|
||||
) : (
|
||||
serviceActivities.map((activity) => {
|
||||
let userList = [];
|
||||
|
||||
if (activity.StoreServiceActivityUsers.length > 0) {
|
||||
// StoreServiceActivityUsers is only an array of user_ids
|
||||
// we need to get the user object from the users array
|
||||
if (activity.StoreServiceActivityUsers.length > 0) {
|
||||
// StoreServiceActivityUsers is only an array of user_ids
|
||||
// we need to get the user object from the users array
|
||||
|
||||
for (let i = 0; i < users.length; i++) {
|
||||
for (
|
||||
let j = 0;
|
||||
j < activity.StoreServiceActivityUsers.length;
|
||||
j++
|
||||
) {
|
||||
if (
|
||||
users[i].user_id ===
|
||||
activity.StoreServiceActivityUsers[j].user_id
|
||||
for (let i = 0; i < users.length; i++) {
|
||||
for (
|
||||
let j = 0;
|
||||
j < activity.StoreServiceActivityUsers.length;
|
||||
j++
|
||||
) {
|
||||
userList.push(users[i]);
|
||||
if (
|
||||
users[i].user_id ===
|
||||
activity.StoreServiceActivityUsers[j].user_id
|
||||
) {
|
||||
userList.push(users[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if there are no users assigned to this activity, we just use the whole users array
|
||||
userList = users;
|
||||
}
|
||||
} else {
|
||||
// if there are no users assigned to this activity, we just use the whole users array
|
||||
userList = users;
|
||||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
key={activity.activity_id}
|
||||
title={activity.name}
|
||||
extra={
|
||||
<Space>
|
||||
<Avatar.Group maxCount={2} size="small">
|
||||
{userList.map((user) => (
|
||||
<Tooltip
|
||||
key={user.user_id}
|
||||
title={user.username}
|
||||
>
|
||||
<Avatar
|
||||
size="small"
|
||||
style={{ backgroundColor: "#6878d6" }}
|
||||
return (
|
||||
<Card
|
||||
key={activity.activity_id}
|
||||
title={activity.name}
|
||||
extra={
|
||||
<Space>
|
||||
<Avatar.Group maxCount={2} size="small">
|
||||
{userList.map((user) => (
|
||||
<Tooltip
|
||||
key={user.user_id}
|
||||
title={user.username}
|
||||
>
|
||||
{user.username.charAt(0)}
|
||||
</Avatar>
|
||||
</Tooltip>
|
||||
))}
|
||||
</Avatar.Group>
|
||||
<Avatar
|
||||
size="small"
|
||||
style={{ backgroundColor: "#6878d6" }}
|
||||
>
|
||||
{user.username.charAt(0)}
|
||||
</Avatar>
|
||||
</Tooltip>
|
||||
))}
|
||||
</Avatar.Group>
|
||||
|
||||
{/*
|
||||
{/*
|
||||
<ArrowUpOutlined disabled />
|
||||
<ArrowDownOutlined disabled />
|
||||
*/}
|
||||
|
||||
<MyEditIcon
|
||||
onClick={() => {
|
||||
setAddEditServiceActivityModalOptions({
|
||||
mode: "edit",
|
||||
isOpen: true,
|
||||
activity: activity,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<MyEditIcon
|
||||
onClick={() => {
|
||||
setAddEditServiceActivityModalOptions({
|
||||
mode: "edit",
|
||||
isOpen: true,
|
||||
activity: activity,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
<MyDeleteIcon
|
||||
propsPopconfirm={{
|
||||
placement: "left",
|
||||
}}
|
||||
onConfirm={() => {
|
||||
return myFetch(
|
||||
`/store/services/activity/${activity.activity_id}`,
|
||||
"DELETE"
|
||||
);
|
||||
}}
|
||||
onFetchSuccess={fetchServiceActivities}
|
||||
popConfirmTitle={t(
|
||||
"storeServices.popConfirmDeleteServiceActivity.title"
|
||||
<MyDeleteIcon
|
||||
propsPopconfirm={{
|
||||
placement: "left",
|
||||
}}
|
||||
onConfirm={() => {
|
||||
return myFetch({
|
||||
url: `/store/services/activity/${activity.activity_id}`,
|
||||
method: "DELETE",
|
||||
notificationApi: notificationApi,
|
||||
t: t,
|
||||
});
|
||||
}}
|
||||
onFetchSuccess={fetchServiceActivities}
|
||||
popConfirmTitle={t(
|
||||
"storeServices.popConfirmDeleteServiceActivity.title"
|
||||
)}
|
||||
popConfirmDescription={t(
|
||||
"storeServices.popConfirmDeleteServiceActivity.description"
|
||||
)}
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Typography.Title level={5}>
|
||||
{t("storeServices.serviceActivityDescription")}
|
||||
</Typography.Title>
|
||||
|
||||
<Typography.Paragraph>
|
||||
{activity.description}
|
||||
</Typography.Paragraph>
|
||||
|
||||
<Typography.Title level={5}>
|
||||
{t("storeServices.serviceActivityPrice")}
|
||||
</Typography.Title>
|
||||
|
||||
<p>{activity.price} €</p>
|
||||
|
||||
<Typography.Title level={5}>
|
||||
{t(
|
||||
"storeServices.serviceActivityDurationMinutes"
|
||||
)}
|
||||
</Typography.Title>
|
||||
|
||||
<Typography.Paragraph>
|
||||
{activity.duration}{" "}
|
||||
{activity.duration === 1
|
||||
? t("common.unit.minute")
|
||||
: t("common.unit.minutes")}{" "}
|
||||
<Typography.Text type="secondary">
|
||||
{durationToHoursAndMinutes(
|
||||
t,
|
||||
activity.duration
|
||||
)}
|
||||
popConfirmDescription={t(
|
||||
"storeServices.popConfirmDeleteServiceActivity.description"
|
||||
)}
|
||||
/>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Typography.Title level={5}>
|
||||
{t("storeServices.serviceActivityDescription")}
|
||||
</Typography.Title>
|
||||
|
||||
<Typography.Paragraph>
|
||||
{activity.description}
|
||||
</Typography.Paragraph>
|
||||
|
||||
<Typography.Title level={5}>
|
||||
{t("storeServices.serviceActivityPrice")}
|
||||
</Typography.Title>
|
||||
|
||||
<p>{activity.price} €</p>
|
||||
|
||||
<Typography.Title level={5}>
|
||||
{t("storeServices.serviceActivityDurationMinutes")}
|
||||
</Typography.Title>
|
||||
|
||||
<Typography.Paragraph>
|
||||
{activity.duration}{" "}
|
||||
{activity.duration === 1
|
||||
? t("common.unit.minute")
|
||||
: t("common.unit.minutes")}{" "}
|
||||
<Typography.Text type="secondary">
|
||||
{durationToHoursAndMinutes(t, activity.duration)}
|
||||
</Typography.Text>
|
||||
</Typography.Paragraph>
|
||||
</Card>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Typography.Text>
|
||||
</Typography.Paragraph>
|
||||
</Card>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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", {
|
||||
storeId: storeId,
|
||||
name: values.serviceName,
|
||||
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,
|
||||
name: formServiceName,
|
||||
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,186 +674,202 @@ function ModalAddEditServiceActivity({
|
|||
}, [addEditServiceActivityModalOptions.isOpen]);
|
||||
|
||||
return (
|
||||
<MyModal
|
||||
title={
|
||||
addEditServiceActivityModalOptions.mode === "add"
|
||||
? t("storeServices.modalAddServiceActivity.title")
|
||||
: t("storeServices.modalEditServiceActivity.title")
|
||||
}
|
||||
isOpen={addEditServiceActivityModalOptions.isOpen}
|
||||
onCancel={handleModalClose}
|
||||
footer={
|
||||
addEditServiceActivityModalOptions.mode === "add" ? (
|
||||
<MyModalCloseCreateButtonFooter
|
||||
onCancel={handleModalClose}
|
||||
isCreateButtonLoading={isRequesting}
|
||||
onCreate={() => {
|
||||
form
|
||||
.validateFields()
|
||||
.then((values) => {
|
||||
setIsRequesting(true);
|
||||
<>
|
||||
{notificationContextHolder}
|
||||
|
||||
myFetch("/store/services/activity", "POST", {
|
||||
serviceId:
|
||||
addEditServiceActivityModalOptions.service.service_id,
|
||||
name: values.serviceActivityName,
|
||||
description: values.serviceActivityDescription,
|
||||
price: values.serviceActivityPrice,
|
||||
duration: values.serviceActivityDurationMinutes,
|
||||
userIds:
|
||||
selectedEmployeesRowKeys.length === users.length
|
||||
? []
|
||||
: selectedEmployeesRowKeys,
|
||||
<MyModal
|
||||
title={
|
||||
addEditServiceActivityModalOptions.mode === "add"
|
||||
? t("storeServices.modalAddServiceActivity.title")
|
||||
: t("storeServices.modalEditServiceActivity.title")
|
||||
}
|
||||
isOpen={addEditServiceActivityModalOptions.isOpen}
|
||||
onCancel={handleModalClose}
|
||||
footer={
|
||||
addEditServiceActivityModalOptions.mode === "add" ? (
|
||||
<MyModalCloseCreateButtonFooter
|
||||
onCancel={handleModalClose}
|
||||
isCreateButtonLoading={isRequesting}
|
||||
onCreate={() => {
|
||||
form
|
||||
.validateFields()
|
||||
.then((values) => {
|
||||
setIsRequesting(true);
|
||||
|
||||
myFetch({
|
||||
url: "/store/services/activity",
|
||||
method: "POST",
|
||||
body: {
|
||||
serviceId:
|
||||
addEditServiceActivityModalOptions.service.service_id,
|
||||
name: values.serviceActivityName,
|
||||
description: values.serviceActivityDescription,
|
||||
price: values.serviceActivityPrice,
|
||||
duration: values.serviceActivityDurationMinutes,
|
||||
userIds:
|
||||
selectedEmployeesRowKeys.length === users.length
|
||||
? []
|
||||
: selectedEmployeesRowKeys,
|
||||
},
|
||||
notificationApi: notificationApi,
|
||||
t: t,
|
||||
})
|
||||
.then(() => {
|
||||
setIsRequesting(false);
|
||||
handleModalClose();
|
||||
|
||||
fetchServices();
|
||||
})
|
||||
.catch((errStatus) => {
|
||||
console.log(errStatus);
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
setIsRequesting(false);
|
||||
handleModalClose();
|
||||
.catch((info) => {
|
||||
console.log("Validate Failed:", info);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<MyModalCloseSaveButtonFooter
|
||||
onCancel={handleModalClose}
|
||||
isSaveButtonLoading={isRequesting}
|
||||
onSave={() => {
|
||||
const formServiceActivityName = form.getFieldValue(
|
||||
"serviceActivityName"
|
||||
);
|
||||
const formServiceActivityDescription = form.getFieldValue(
|
||||
"serviceActivityDescription"
|
||||
);
|
||||
const formServiceActivityPrice = form.getFieldValue(
|
||||
"serviceActivityPrice"
|
||||
);
|
||||
const formServiceActivityDurationMinutes = form.getFieldValue(
|
||||
"serviceActivityDurationMinutes"
|
||||
);
|
||||
|
||||
fetchServices();
|
||||
// if the service didn't change, don't send a request
|
||||
if (
|
||||
addEditServiceActivityModalOptions.activity.name ===
|
||||
formServiceActivityName &&
|
||||
addEditServiceActivityModalOptions.activity.description ===
|
||||
formServiceActivityDescription &&
|
||||
addEditServiceActivityModalOptions.activity.price ===
|
||||
formServiceActivityPrice &&
|
||||
addEditServiceActivityModalOptions.activity.duration ===
|
||||
formServiceActivityDurationMinutes &&
|
||||
(selectedEmployeesRowKeys.length === users.length ||
|
||||
addEditServiceActivityModalOptions.activity
|
||||
.StoreServiceActivityUsers.length ===
|
||||
selectedEmployeesRowKeys.length)
|
||||
) {
|
||||
handleModalClose();
|
||||
return;
|
||||
}
|
||||
|
||||
let validateFields = [];
|
||||
let body = {
|
||||
activityId:
|
||||
addEditServiceActivityModalOptions.activity.activity_id,
|
||||
};
|
||||
|
||||
if (
|
||||
formServiceActivityName !==
|
||||
addEditServiceActivityModalOptions.activity.name
|
||||
) {
|
||||
validateFields.push("serviceActivityName");
|
||||
body.name = formServiceActivityName;
|
||||
}
|
||||
|
||||
if (
|
||||
formServiceActivityDescription !==
|
||||
addEditServiceActivityModalOptions.activity.description
|
||||
) {
|
||||
validateFields.push("serviceActivityDescription");
|
||||
body.description = formServiceActivityDescription;
|
||||
}
|
||||
|
||||
if (
|
||||
formServiceActivityPrice !==
|
||||
addEditServiceActivityModalOptions.activity.price
|
||||
) {
|
||||
validateFields.push("serviceActivityPrice");
|
||||
body.price = formServiceActivityPrice;
|
||||
}
|
||||
|
||||
let formDuration = formServiceActivityDurationMinutes;
|
||||
|
||||
if (
|
||||
formDuration !==
|
||||
addEditServiceActivityModalOptions.activity.duration
|
||||
) {
|
||||
validateFields.push("serviceActivityDurationMinutes");
|
||||
body.duration = formDuration;
|
||||
}
|
||||
|
||||
body.userIds = selectedEmployeesRowKeys;
|
||||
|
||||
form
|
||||
.validateFields()
|
||||
.then(() => {
|
||||
setIsRequesting(true);
|
||||
|
||||
myFetch({
|
||||
url: "/store/services/activity/update",
|
||||
method: "POST",
|
||||
body: body,
|
||||
notificationApi: notificationApi,
|
||||
t: t,
|
||||
})
|
||||
.catch((errStatus) => {
|
||||
console.log(errStatus);
|
||||
});
|
||||
})
|
||||
.catch((info) => {
|
||||
console.log("Validate Failed:", info);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<MyModalCloseSaveButtonFooter
|
||||
onCancel={handleModalClose}
|
||||
isSaveButtonLoading={isRequesting}
|
||||
onSave={() => {
|
||||
const formServiceActivityName = form.getFieldValue(
|
||||
"serviceActivityName"
|
||||
);
|
||||
const formServiceActivityDescription = form.getFieldValue(
|
||||
"serviceActivityDescription"
|
||||
);
|
||||
const formServiceActivityPrice = form.getFieldValue(
|
||||
"serviceActivityPrice"
|
||||
);
|
||||
const formServiceActivityDurationMinutes = form.getFieldValue(
|
||||
"serviceActivityDurationMinutes"
|
||||
);
|
||||
.then(() => {
|
||||
setIsRequesting(false);
|
||||
handleModalClose();
|
||||
|
||||
// if the service didn't change, don't send a request
|
||||
if (
|
||||
addEditServiceActivityModalOptions.activity.name ===
|
||||
formServiceActivityName &&
|
||||
addEditServiceActivityModalOptions.activity.description ===
|
||||
formServiceActivityDescription &&
|
||||
addEditServiceActivityModalOptions.activity.price ===
|
||||
formServiceActivityPrice &&
|
||||
addEditServiceActivityModalOptions.activity.duration ===
|
||||
formServiceActivityDurationMinutes &&
|
||||
(selectedEmployeesRowKeys.length === users.length ||
|
||||
addEditServiceActivityModalOptions.activity
|
||||
.StoreServiceActivityUsers.length ===
|
||||
selectedEmployeesRowKeys.length)
|
||||
) {
|
||||
handleModalClose();
|
||||
return;
|
||||
}
|
||||
fetchServices();
|
||||
})
|
||||
.catch((errStatus) => {
|
||||
console.log(errStatus);
|
||||
});
|
||||
})
|
||||
.catch((info) => {
|
||||
console.log("Validate Failed:", info);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
>
|
||||
<Form form={form} layout="vertical" requiredMark={false}>
|
||||
<ServiceActivityNameFormInput formItemName="serviceActivityName" />
|
||||
|
||||
let validateFields = [];
|
||||
let body = {
|
||||
activityId:
|
||||
addEditServiceActivityModalOptions.activity.activity_id,
|
||||
};
|
||||
<ServiceActivityDescriptionFormInput formItemName="serviceActivityDescription" />
|
||||
|
||||
if (
|
||||
formServiceActivityName !==
|
||||
addEditServiceActivityModalOptions.activity.name
|
||||
) {
|
||||
validateFields.push("serviceActivityName");
|
||||
body.name = formServiceActivityName;
|
||||
}
|
||||
<ServiceActivityPriceFormInput formItemName="serviceActivityPrice" />
|
||||
|
||||
if (
|
||||
formServiceActivityDescription !==
|
||||
addEditServiceActivityModalOptions.activity.description
|
||||
) {
|
||||
validateFields.push("serviceActivityDescription");
|
||||
body.description = formServiceActivityDescription;
|
||||
}
|
||||
<ServiceActivityDurationMinutesFormInput formItemName="serviceActivityDurationMinutes" />
|
||||
|
||||
if (
|
||||
formServiceActivityPrice !==
|
||||
addEditServiceActivityModalOptions.activity.price
|
||||
) {
|
||||
validateFields.push("serviceActivityPrice");
|
||||
body.price = formServiceActivityPrice;
|
||||
}
|
||||
<Space direction="vertical" style={{ width: "100%" }}>
|
||||
<Typography.Text>
|
||||
{t("storeServices.serviceActivityResponsible")}
|
||||
</Typography.Text>
|
||||
|
||||
let formDuration = formServiceActivityDurationMinutes;
|
||||
|
||||
if (
|
||||
formDuration !==
|
||||
addEditServiceActivityModalOptions.activity.duration
|
||||
) {
|
||||
validateFields.push("serviceActivityDurationMinutes");
|
||||
body.duration = formDuration;
|
||||
}
|
||||
|
||||
body.userIds = selectedEmployeesRowKeys;
|
||||
|
||||
form
|
||||
.validateFields()
|
||||
.then(() => {
|
||||
setIsRequesting(true);
|
||||
|
||||
myFetch("/store/services/activity/update", "POST", body)
|
||||
.then(() => {
|
||||
setIsRequesting(false);
|
||||
handleModalClose();
|
||||
|
||||
fetchServices();
|
||||
})
|
||||
.catch((errStatus) => {
|
||||
console.log(errStatus);
|
||||
});
|
||||
})
|
||||
.catch((info) => {
|
||||
console.log("Validate Failed:", info);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
>
|
||||
<Form form={form} layout="vertical" requiredMark={false}>
|
||||
<ServiceActivityNameFormInput formItemName="serviceActivityName" />
|
||||
|
||||
<ServiceActivityDescriptionFormInput formItemName="serviceActivityDescription" />
|
||||
|
||||
<ServiceActivityPriceFormInput formItemName="serviceActivityPrice" />
|
||||
|
||||
<ServiceActivityDurationMinutesFormInput formItemName="serviceActivityDurationMinutes" />
|
||||
|
||||
<Space direction="vertical" style={{ width: "100%" }}>
|
||||
<Typography.Text>
|
||||
{t("storeServices.serviceActivityResponsible")}
|
||||
</Typography.Text>
|
||||
|
||||
<MyTable
|
||||
props={{
|
||||
rowSelection: {
|
||||
selectedRowKeys: selectedEmployeesRowKeys,
|
||||
onChange: (newSelectedRowKeys) =>
|
||||
setSelectedEmployeesRowKeys(newSelectedRowKeys),
|
||||
},
|
||||
loading: isRequesting,
|
||||
columns: getTableColumns(),
|
||||
dataSource: getTableItems(),
|
||||
size: "small",
|
||||
pagination: false,
|
||||
}}
|
||||
/>
|
||||
</Space>
|
||||
</Form>
|
||||
</MyModal>
|
||||
<MyTable
|
||||
props={{
|
||||
rowSelection: {
|
||||
selectedRowKeys: selectedEmployeesRowKeys,
|
||||
onChange: (newSelectedRowKeys) =>
|
||||
setSelectedEmployeesRowKeys(newSelectedRowKeys),
|
||||
},
|
||||
loading: isRequesting,
|
||||
columns: getTableColumns(),
|
||||
dataSource: getTableItems(),
|
||||
size: "small",
|
||||
pagination: false,
|
||||
}}
|
||||
/>
|
||||
</Space>
|
||||
</Form>
|
||||
</MyModal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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", {
|
||||
name: companyName,
|
||||
phoneNumber,
|
||||
email,
|
||||
address,
|
||||
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}
|
||||
|
|
|
@ -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", {
|
||||
storeId,
|
||||
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")}
|
||||
|
|
|
@ -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={() => {
|
||||
setUserSession();
|
||||
window.location.href = "/";
|
||||
setIsRequestingLogout(true);
|
||||
|
||||
fetch(`${Constants.API_ADDRESS}/user/auth/logout`, {
|
||||
myFetch({
|
||||
url: "/user/auth/logout",
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Authorization": userSession,
|
||||
},
|
||||
}).catch(console.error);
|
||||
notificationApi: notificationApi,
|
||||
t: t,
|
||||
})
|
||||
.then(() => {
|
||||
setUserSession();
|
||||
window.location.href = "/";
|
||||
})
|
||||
.catch(() => setIsRequestingLogout(false));
|
||||
}}
|
||||
>
|
||||
{t("common.button.logout")}
|
||||
|
|
|
@ -6,6 +6,7 @@ body {
|
|||
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
code {
|
||||
|
|
1149
src/utils.js
1149
src/utils.js
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue