fetch error message handling
parent
260bebf8dc
commit
a8f6f75d28
|
@ -54,6 +54,20 @@
|
||||||
"passwordMinLength": "Passwort muss mindestens {{minLength}} Zeichen lang sein",
|
"passwordMinLength": "Passwort muss mindestens {{minLength}} Zeichen lang sein",
|
||||||
"calendarMaxFutureBookingDaysRequired": "Maximaler Buchungszeitraum ist erforderlich",
|
"calendarMaxFutureBookingDaysRequired": "Maximaler Buchungszeitraum ist erforderlich",
|
||||||
"calendarMinEarliestBookingTimeRequired": "Minimaler frühester Buchungszeitpunkt 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": {
|
"pageNotFound": {
|
||||||
|
@ -98,7 +112,13 @@
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"login": "Anmelden",
|
"login": "Anmelden",
|
||||||
"signUp": "Registrieren"
|
"signUp": "Registrieren",
|
||||||
|
"request": {
|
||||||
|
"400": {
|
||||||
|
"title": "Anmeldung fehlgeschlagen",
|
||||||
|
"description": "Bitte überprüfen Sie Ihre Eingaben."
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"storeServices": {
|
"storeServices": {
|
||||||
"pageTitle": "Dienstleistungen",
|
"pageTitle": "Dienstleistungen",
|
||||||
|
|
|
@ -54,6 +54,20 @@
|
||||||
"passwordMinLength": "Password must be at least {{minLength}} characters",
|
"passwordMinLength": "Password must be at least {{minLength}} characters",
|
||||||
"calendarMaxFutureBookingDaysRequired": "Please enter the max. future booking days",
|
"calendarMaxFutureBookingDaysRequired": "Please enter the max. future booking days",
|
||||||
"calendarMinEarliestBookingTimeRequired": "Please enter the min. earliest booking time"
|
"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": {
|
"pageNotFound": {
|
||||||
|
@ -98,7 +112,13 @@
|
||||||
},
|
},
|
||||||
"login": {
|
"login": {
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
"signUp": "Sign up"
|
"signUp": "Sign up",
|
||||||
|
"request": {
|
||||||
|
"400": {
|
||||||
|
"title": "Login failed",
|
||||||
|
"description": "Please check your inputs and try again."
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"services": {
|
"services": {
|
||||||
"pageTitle": "Services"
|
"pageTitle": "Services"
|
||||||
|
|
|
@ -24,7 +24,10 @@ export default function App() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!userSession) return;
|
if (!userSession) return;
|
||||||
|
|
||||||
myFetch("/user", "GET")
|
myFetch({
|
||||||
|
url: "/user",
|
||||||
|
method: "GET",
|
||||||
|
})
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
setAppUserData(data);
|
setAppUserData(data);
|
||||||
})
|
})
|
||||||
|
|
|
@ -17,9 +17,7 @@ const StoreWebsite = lazy(() => import("../../Pages/Store/Website"));
|
||||||
//const Feedback = lazy(() => import("../../Pages/Feedback"));
|
//const Feedback = lazy(() => import("../../Pages/Feedback"));
|
||||||
const UserProfile = lazy(() => import("../../Pages/UserProfile"));
|
const UserProfile = lazy(() => import("../../Pages/UserProfile"));
|
||||||
|
|
||||||
export default function AppRoutes({ userSession, setUserSession }) {
|
export default function AppRoutes({ setUserSession }) {
|
||||||
//const appContext = useAppContext();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route
|
<Route
|
||||||
|
@ -111,10 +109,7 @@ export default function AppRoutes({ userSession, setUserSession }) {
|
||||||
path={Constants.ROUTE_PATHS.USER_PROFILE}
|
path={Constants.ROUTE_PATHS.USER_PROFILE}
|
||||||
element={
|
element={
|
||||||
<MySupsenseFallback>
|
<MySupsenseFallback>
|
||||||
<UserProfile
|
<UserProfile setUserSession={setUserSession} />
|
||||||
userSession={userSession}
|
|
||||||
setUserSession={setUserSession}
|
|
||||||
/>
|
|
||||||
</MySupsenseFallback>
|
</MySupsenseFallback>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,65 +1,11 @@
|
||||||
import {
|
import { MenuFoldOutlined, MenuUnfoldOutlined } from "@ant-design/icons";
|
||||||
BellOutlined,
|
import { Button } from "antd";
|
||||||
CheckCircleOutlined,
|
|
||||||
CloseCircleOutlined,
|
|
||||||
CloseOutlined,
|
|
||||||
DeleteOutlined,
|
|
||||||
ExclamationCircleOutlined,
|
|
||||||
InboxOutlined,
|
|
||||||
InfoCircleOutlined,
|
|
||||||
MenuFoldOutlined,
|
|
||||||
MenuUnfoldOutlined,
|
|
||||||
QuestionCircleOutlined,
|
|
||||||
} from "@ant-design/icons";
|
|
||||||
import { Badge, Button, Drawer, List, Popconfirm, Typography } from "antd";
|
|
||||||
import { Header } from "antd/es/layout/layout";
|
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({
|
export default function HeaderMenu({
|
||||||
isSideMenuCollapsed,
|
isSideMenuCollapsed,
|
||||||
setIsSideMenuCollapsed,
|
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 (
|
return (
|
||||||
<Header
|
<Header
|
||||||
style={{
|
style={{
|
||||||
|
@ -85,112 +31,3 @@ export default function HeaderMenu({
|
||||||
</Header>
|
</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
|
body[fetchParameter] = value; // like accountName: value
|
||||||
|
|
||||||
myFetch(fetchUrl, "POST", body)
|
myFetch({
|
||||||
|
url: fetchUrl,
|
||||||
|
method: "POST",
|
||||||
|
body: body,
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
resolve();
|
resolve();
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
QuestionCircleOutlined,
|
QuestionCircleOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { Popconfirm, Tooltip } from "antd";
|
import { Popconfirm } from "antd";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
@ -79,14 +79,14 @@ export function MyPlusIcon({ onClick }) {
|
||||||
export function MyIcon({
|
export function MyIcon({
|
||||||
popconfirmDisabled,
|
popconfirmDisabled,
|
||||||
propsPopconfirm,
|
propsPopconfirm,
|
||||||
propsTooltip,
|
// propsTooltip,
|
||||||
onConfirm,
|
onConfirm,
|
||||||
onFetchSuccess,
|
onFetchSuccess,
|
||||||
onCancel,
|
onCancel,
|
||||||
popConfirmTitle,
|
popConfirmTitle,
|
||||||
popConfirmDescription,
|
popConfirmDescription,
|
||||||
popConfirmOkText,
|
popConfirmOkText,
|
||||||
tooltipTitle,
|
//tooltipTitle,
|
||||||
icon,
|
icon,
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
|
@ -3,8 +3,8 @@ import { Button, Form, Modal, Tabs, notification } from "antd";
|
||||||
import {
|
import {
|
||||||
EncodeStringToBase64,
|
EncodeStringToBase64,
|
||||||
myFetch,
|
myFetch,
|
||||||
myFetchContentType,
|
|
||||||
setUserSessionToLocalStorage,
|
setUserSessionToLocalStorage,
|
||||||
|
showInputsInvalidNotification,
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import {
|
import {
|
||||||
|
@ -19,31 +19,27 @@ export default function Login() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
const [api, contextHolder] = notification.useNotification();
|
const [notificationApi, notificationContextHolder] =
|
||||||
|
notification.useNotification();
|
||||||
|
|
||||||
const [selectedMethod, setSelectedMethod] = useState("1");
|
const [selectedMethod, setSelectedMethod] = useState("1");
|
||||||
const [isRequesting, setIsRequesting] = useState(false);
|
const [isRequesting, setIsRequesting] = useState(false);
|
||||||
|
|
||||||
const showErrorNotification = (errStatus) => {
|
const showErrorNotification = (errStatus) => {
|
||||||
if (errStatus === 401) {
|
if (errStatus === 400) {
|
||||||
api["error"]({
|
notificationApi["error"]({
|
||||||
message: "Account deactivated",
|
message: t("login.request.400.title"),
|
||||||
description: "Please contact an administrator",
|
description: t("login.request.400.description"),
|
||||||
});
|
});
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
api["error"]({
|
|
||||||
message: "Login failed",
|
|
||||||
description: "Please check your accountName and password!",
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{contextHolder}
|
{notificationContextHolder}
|
||||||
<Modal
|
<Modal
|
||||||
open={true}
|
open={true}
|
||||||
|
mask={false}
|
||||||
closable={false}
|
closable={false}
|
||||||
centered
|
centered
|
||||||
keyboard={false}
|
keyboard={false}
|
||||||
|
@ -68,18 +64,16 @@ export default function Login() {
|
||||||
body.username = values.username;
|
body.username = values.username;
|
||||||
}
|
}
|
||||||
|
|
||||||
myFetch(
|
myFetch({
|
||||||
`/user/auth/${selectedMethod === "1" ? "login" : "signup"}`,
|
url: `/user/auth/${
|
||||||
"POST",
|
selectedMethod === "1" ? "login" : "signup"
|
||||||
body,
|
}`,
|
||||||
{},
|
method: "POST",
|
||||||
myFetchContentType.JSON,
|
body: body,
|
||||||
"",
|
notificationApi: notificationApi,
|
||||||
true
|
t: t,
|
||||||
)
|
})
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
console.log(data.XAuthorization);
|
|
||||||
|
|
||||||
setUserSessionToLocalStorage(data.XAuthorization);
|
setUserSessionToLocalStorage(data.XAuthorization);
|
||||||
window.location.href = "/";
|
window.location.href = "/";
|
||||||
})
|
})
|
||||||
|
@ -88,9 +82,7 @@ export default function Login() {
|
||||||
setIsRequesting(false);
|
setIsRequesting(false);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((info) => {
|
.catch(() => showInputsInvalidNotification(notificationApi, t));
|
||||||
console.log("Validate Failed:", info);
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{selectedMethod === "1" ? t("login.login") : t("login.signUp")}
|
{selectedMethod === "1" ? t("login.login") : t("login.signUp")}
|
||||||
|
@ -119,9 +111,7 @@ export default function Login() {
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
centered
|
centered
|
||||||
onChange={(activeKey) => {
|
onChange={(activeKey) => setSelectedMethod(activeKey)}
|
||||||
setSelectedMethod(activeKey);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Form form={form} layout="vertical" requiredMark={false}>
|
<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 { Link, useParams, useNavigate } from "react-router-dom";
|
||||||
import { Constants, myFetch } from "../../../../utils";
|
import { Constants, myFetch } from "../../../../utils";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import MyCenteredContainer from "../../../../Components/MyContainer";
|
import MyCenteredContainer from "../../../../Components/MyContainer";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { MySupsenseFallback } from "../../../../Components/MySupsenseFallback";
|
|
||||||
|
|
||||||
export default function StoreCalendarAuth() {
|
export default function StoreCalendarAuth() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const [notificationApi, notificationContextHolder] =
|
||||||
|
notification.useNotification();
|
||||||
|
|
||||||
const { status } = useParams();
|
const { status } = useParams();
|
||||||
|
|
||||||
const [isRequesting, setIsRequesting] = useState(true);
|
const [isRequesting, setIsRequesting] = useState(true);
|
||||||
const [storeId, setStoreId] = useState("");
|
const [storeId, setStoreId] = useState("");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
myFetch("/calendar/store", "GET")
|
myFetch({
|
||||||
|
url: "/calendar/store",
|
||||||
|
method: "GET",
|
||||||
|
notificationApi: notificationApi,
|
||||||
|
t: t,
|
||||||
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
setIsRequesting(false);
|
setIsRequesting(false);
|
||||||
setStoreId(res.storeId);
|
setStoreId(res.storeId);
|
||||||
|
@ -33,32 +40,36 @@ export default function StoreCalendarAuth() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Result
|
<>
|
||||||
status={status === "finish" ? "success" : "error"}
|
{notificationContextHolder}
|
||||||
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} />}
|
<Result
|
||||||
</div>,
|
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,
|
Spin,
|
||||||
Switch,
|
Switch,
|
||||||
Typography,
|
Typography,
|
||||||
|
notification,
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
|
@ -38,6 +39,9 @@ const { useBreakpoint } = Grid;
|
||||||
|
|
||||||
export default function StoreCalendar() {
|
export default function StoreCalendar() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const [notificationApi, notificationContextHolder] =
|
||||||
|
notification.useNotification();
|
||||||
|
|
||||||
const { storeId } = useParams();
|
const { storeId } = useParams();
|
||||||
|
|
||||||
const [calendarSettings, setCalendarSettings] = useState({});
|
const [calendarSettings, setCalendarSettings] = useState({});
|
||||||
|
@ -47,7 +51,12 @@ export default function StoreCalendar() {
|
||||||
// delete session cookie
|
// delete session cookie
|
||||||
document.cookie = `session=; path=/; expires=Thu, 01 Jan 1970 00:00:01 GMT;`;
|
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) => {
|
.then((res) => {
|
||||||
setIsRequesting(false);
|
setIsRequesting(false);
|
||||||
setCalendarSettings(res);
|
setCalendarSettings(res);
|
||||||
|
@ -67,6 +76,8 @@ export default function StoreCalendar() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{notificationContextHolder}
|
||||||
|
|
||||||
{calendarSettings.connected === false ? (
|
{calendarSettings.connected === false ? (
|
||||||
<MyCenteredContainer>
|
<MyCenteredContainer>
|
||||||
<Result
|
<Result
|
||||||
|
@ -135,6 +146,7 @@ function CalendarFrame({ storeId }) {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<iframe
|
<iframe
|
||||||
|
title="calendar"
|
||||||
onLoad={() => setIsLoading(false)}
|
onLoad={() => setIsLoading(false)}
|
||||||
style={{ border: 0, borderRadius: 12 }}
|
style={{ border: 0, borderRadius: 12 }}
|
||||||
width="100%"
|
width="100%"
|
||||||
|
@ -147,6 +159,9 @@ function CalendarFrame({ storeId }) {
|
||||||
|
|
||||||
function CardPersonalCalendarSettings({ settings }) {
|
function CardPersonalCalendarSettings({ settings }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const [notificationApi, notificationContextHolder] =
|
||||||
|
notification.useNotification();
|
||||||
|
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [formUnlinkCalendar] = Form.useForm();
|
const [formUnlinkCalendar] = Form.useForm();
|
||||||
const screenBreakpoint = useBreakpoint();
|
const screenBreakpoint = useBreakpoint();
|
||||||
|
@ -189,8 +204,14 @@ function CardPersonalCalendarSettings({ settings }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
delayTimeout.current = setTimeout(() => {
|
delayTimeout.current = setTimeout(() => {
|
||||||
myFetch("/calendar/settings/personal", "POST", {
|
myFetch({
|
||||||
calendarUsingPrimaryCalendar: usingPrimaryCalendar,
|
url: "/calendar/settings/personal",
|
||||||
|
method: "POST",
|
||||||
|
body: {
|
||||||
|
calendarUsingPrimaryCalendar: usingPrimaryCalendar,
|
||||||
|
},
|
||||||
|
notificationApi: notificationApi,
|
||||||
|
t: t,
|
||||||
})
|
})
|
||||||
.then(() => setRequestState(RequestState.SUCCESS))
|
.then(() => setRequestState(RequestState.SUCCESS))
|
||||||
.catch((errStatus) => {
|
.catch((errStatus) => {
|
||||||
|
@ -239,6 +260,8 @@ function CardPersonalCalendarSettings({ settings }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{notificationContextHolder}
|
||||||
|
|
||||||
<Card
|
<Card
|
||||||
title={
|
title={
|
||||||
screenBreakpoint.xl ? (
|
screenBreakpoint.xl ? (
|
||||||
|
@ -290,8 +313,14 @@ function CardPersonalCalendarSettings({ settings }) {
|
||||||
formUnlinkCalendar.validateFields().then((values) => {
|
formUnlinkCalendar.validateFields().then((values) => {
|
||||||
setIsRequesting(true);
|
setIsRequesting(true);
|
||||||
|
|
||||||
myFetch("/calendar/settings/personal/unlink", "POST", {
|
myFetch({
|
||||||
password: EncodeStringToBase64(values.password),
|
url: "/calendar/settings/personal/unlink",
|
||||||
|
method: "POST",
|
||||||
|
body: {
|
||||||
|
password: EncodeStringToBase64(values.password),
|
||||||
|
},
|
||||||
|
notificationApi: notificationApi,
|
||||||
|
t: t,
|
||||||
})
|
})
|
||||||
.then(() => window.location.reload())
|
.then(() => window.location.reload())
|
||||||
.catch((errStatus) => {
|
.catch((errStatus) => {
|
||||||
|
@ -321,6 +350,9 @@ function CardPersonalCalendarSettings({ settings }) {
|
||||||
|
|
||||||
function CardStoreCalendarSettings({ settings }) {
|
function CardStoreCalendarSettings({ settings }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const [notificationApi, notificationContextHolder] =
|
||||||
|
notification.useNotification();
|
||||||
|
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
const [requestState, setRequestState] = useState(RequestState.INIT);
|
const [requestState, setRequestState] = useState(RequestState.INIT);
|
||||||
|
@ -369,9 +401,15 @@ function CardStoreCalendarSettings({ settings }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
delayTimeout.current = setTimeout(() => {
|
delayTimeout.current = setTimeout(() => {
|
||||||
myFetch("/calendar/settings/store", "POST", {
|
myFetch({
|
||||||
calendarMaxFutureBookingDays,
|
url: "/calendar/settings/store",
|
||||||
calendarMinEarliestBookingTime,
|
method: "POST",
|
||||||
|
body: {
|
||||||
|
calendarMaxFutureBookingDays,
|
||||||
|
calendarMinEarliestBookingTime,
|
||||||
|
},
|
||||||
|
notificationApi: notificationApi,
|
||||||
|
t: t,
|
||||||
})
|
})
|
||||||
.then(() => setRequestState(RequestState.SUCCESS))
|
.then(() => setRequestState(RequestState.SUCCESS))
|
||||||
.catch((errStatus) => {
|
.catch((errStatus) => {
|
||||||
|
@ -382,22 +420,26 @@ function CardStoreCalendarSettings({ settings }) {
|
||||||
}, [calendarMaxFutureBookingDays, calendarMinEarliestBookingTime]);
|
}, [calendarMaxFutureBookingDays, calendarMinEarliestBookingTime]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<>
|
||||||
title={t("calendar.cardStoreCalendarSettings.title")}
|
{notificationContextHolder}
|
||||||
extra={
|
|
||||||
<RequestStateItem
|
|
||||||
state={requestState}
|
|
||||||
setRequestState={setRequestState}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Form form={form} requiredMark={false}>
|
|
||||||
<>
|
|
||||||
<MyCalendarMaxFutureBookingDaysFormInput />
|
|
||||||
|
|
||||||
<MyCalendarMinEarliestBookingTimeFormInput />
|
<Card
|
||||||
</>
|
title={t("calendar.cardStoreCalendarSettings.title")}
|
||||||
</Form>
|
extra={
|
||||||
</Card>
|
<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 { 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, {
|
import MyModal, {
|
||||||
MyModalCloseCreateButtonFooter,
|
MyModalCloseCreateButtonFooter,
|
||||||
MyModalCloseSaveButtonFooter,
|
MyModalCloseSaveButtonFooter,
|
||||||
|
@ -21,6 +29,9 @@ const { useBreakpoint } = Grid;
|
||||||
|
|
||||||
export default function StoreEmployees() {
|
export default function StoreEmployees() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const [notificationApi, notificationContextHolder] =
|
||||||
|
notification.useNotification();
|
||||||
|
|
||||||
const screenBreakpoint = useBreakpoint();
|
const screenBreakpoint = useBreakpoint();
|
||||||
const { storeId } = useParams();
|
const { storeId } = useParams();
|
||||||
|
|
||||||
|
@ -85,8 +96,12 @@ export default function StoreEmployees() {
|
||||||
onConfirm={() => {
|
onConfirm={() => {
|
||||||
setIsRequesting(true);
|
setIsRequesting(true);
|
||||||
|
|
||||||
myFetch("/users", "DELETE", {
|
myFetch({
|
||||||
userId: record.key,
|
url: "/users",
|
||||||
|
method: "DELETE",
|
||||||
|
body: { userId: record.key },
|
||||||
|
notificationApi: notificationApi,
|
||||||
|
t: t,
|
||||||
})
|
})
|
||||||
.then(() => fetchEmployees())
|
.then(() => fetchEmployees())
|
||||||
.catch((errStatus) => {
|
.catch((errStatus) => {
|
||||||
|
@ -123,7 +138,12 @@ export default function StoreEmployees() {
|
||||||
const fetchEmployees = () => {
|
const fetchEmployees = () => {
|
||||||
setIsRequesting(true);
|
setIsRequesting(true);
|
||||||
|
|
||||||
myFetch(`/users/${storeId}`, "GET")
|
myFetch({
|
||||||
|
url: `/users/${storeId}`,
|
||||||
|
method: "GET",
|
||||||
|
notificationApi: notification,
|
||||||
|
t: t,
|
||||||
|
})
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
setIsRequesting(false);
|
setIsRequesting(false);
|
||||||
setRequestData(data);
|
setRequestData(data);
|
||||||
|
@ -137,6 +157,8 @@ export default function StoreEmployees() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{notificationContextHolder}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
@ -271,7 +293,13 @@ function ModalAddEditEmployee({
|
||||||
values.calendarMinEarliestBookingTime;
|
values.calendarMinEarliestBookingTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
myFetch("/users", "POST", body)
|
myFetch({
|
||||||
|
url: "/users",
|
||||||
|
method: "POST",
|
||||||
|
body: body,
|
||||||
|
notificationApi: notification,
|
||||||
|
t: t,
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setIsRequesting(false);
|
setIsRequesting(false);
|
||||||
handleModalClose();
|
handleModalClose();
|
||||||
|
@ -357,7 +385,13 @@ function ModalAddEditEmployee({
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setIsRequesting(true);
|
setIsRequesting(true);
|
||||||
|
|
||||||
myFetch("/users/update", "POST", body)
|
myFetch({
|
||||||
|
url: "/users/update",
|
||||||
|
method: "POST",
|
||||||
|
body: body,
|
||||||
|
notificationApi: notification,
|
||||||
|
t: t,
|
||||||
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setIsRequesting(false);
|
setIsRequesting(false);
|
||||||
handleModalClose();
|
handleModalClose();
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
Spin,
|
Spin,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Typography,
|
Typography,
|
||||||
|
notification,
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
@ -33,6 +34,8 @@ const { useBreakpoint } = Grid;
|
||||||
|
|
||||||
export default function StoreServices() {
|
export default function StoreServices() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const [notificationApi, notificationContextHolder] =
|
||||||
|
notification.useNotification();
|
||||||
const screenBreakpoint = useBreakpoint();
|
const screenBreakpoint = useBreakpoint();
|
||||||
const { storeId } = useParams();
|
const { storeId } = useParams();
|
||||||
|
|
||||||
|
@ -58,7 +61,12 @@ export default function StoreServices() {
|
||||||
const fetchServices = () => {
|
const fetchServices = () => {
|
||||||
setIsRequestingServices(true);
|
setIsRequestingServices(true);
|
||||||
|
|
||||||
myFetch(`/store/services/${storeId}`, "GET")
|
myFetch({
|
||||||
|
url: `/store/services/${storeId}`,
|
||||||
|
method: "GET",
|
||||||
|
notificationApi: notificationApi,
|
||||||
|
t: t,
|
||||||
|
})
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
setIsRequestingServices(false);
|
setIsRequestingServices(false);
|
||||||
setServicesData(data);
|
setServicesData(data);
|
||||||
|
@ -72,6 +80,8 @@ export default function StoreServices() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{notificationContextHolder}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
@ -148,6 +158,8 @@ function Service({
|
||||||
fetchServices,
|
fetchServices,
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const [notificationApi, notificationContextHolder] =
|
||||||
|
notification.useNotification();
|
||||||
|
|
||||||
const [isRequestingActivities, setIsRequestingActivities] = useState(false);
|
const [isRequestingActivities, setIsRequestingActivities] = useState(false);
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
@ -159,10 +171,12 @@ function Service({
|
||||||
|
|
||||||
setIsRequestingActivities(true);
|
setIsRequestingActivities(true);
|
||||||
|
|
||||||
myFetch(
|
myFetch({
|
||||||
`/store/services/activities/${storeId}/${service.service_id}`,
|
url: `/store/services/activities/${storeId}/${service.service_id}`,
|
||||||
"GET"
|
method: "GET",
|
||||||
)
|
notificationApi: notificationApi,
|
||||||
|
t: t,
|
||||||
|
})
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
setIsRequestingActivities(false);
|
setIsRequestingActivities(false);
|
||||||
setServiceActivities(data.activities);
|
setServiceActivities(data.activities);
|
||||||
|
@ -175,24 +189,27 @@ function Service({
|
||||||
useEffect(() => fetchServiceActivities(), [isOpen]);
|
useEffect(() => fetchServiceActivities(), [isOpen]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Collapse
|
<>
|
||||||
key={service.service_id}
|
{notificationContextHolder}
|
||||||
onChange={(e) => setIsOpen(e.length !== 0)}
|
|
||||||
items={[
|
|
||||||
{
|
|
||||||
key: "1",
|
|
||||||
label: (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
alignItems: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span>{service.name}</span>
|
|
||||||
|
|
||||||
<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
|
<ArrowUpOutlined
|
||||||
disabled
|
disabled
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
@ -202,192 +219,202 @@ function Service({
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
/>
|
/>
|
||||||
*/}
|
*/}
|
||||||
<MyPlusIcon
|
<MyPlusIcon
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
setAddEditServiceActivityModalOptions({
|
setAddEditServiceActivityModalOptions({
|
||||||
mode: "add",
|
mode: "add",
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
service: service,
|
service: service,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<MyEditIcon
|
<MyEditIcon
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
setAddEditServiceModalOptions({
|
setAddEditServiceModalOptions({
|
||||||
mode: "edit",
|
mode: "edit",
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
service: service,
|
service: service,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<MyDeleteIcon
|
<MyDeleteIcon
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
propsPopconfirm={{
|
propsPopconfirm={{
|
||||||
placement: "left",
|
placement: "left",
|
||||||
}}
|
}}
|
||||||
onConfirm={() => {
|
onConfirm={() => {
|
||||||
return myFetch(
|
return myFetch({
|
||||||
`/store/services/${service.service_id}`,
|
url: `/store/services/${service.service_id}`,
|
||||||
"DELETE"
|
method: "DELETE",
|
||||||
);
|
notificationApi: notificationApi,
|
||||||
}}
|
t: t,
|
||||||
onFetchSuccess={fetchServices}
|
});
|
||||||
popConfirmTitle={t(
|
}}
|
||||||
"storeServices.popConfirmDeleteService.title"
|
onFetchSuccess={fetchServices}
|
||||||
)}
|
popConfirmTitle={t(
|
||||||
popConfirmDescription={t(
|
"storeServices.popConfirmDeleteService.title"
|
||||||
"storeServices.popConfirmDeleteService.description"
|
)}
|
||||||
)}
|
popConfirmDescription={t(
|
||||||
/>
|
"storeServices.popConfirmDeleteService.description"
|
||||||
</Space>
|
)}
|
||||||
</div>
|
/>
|
||||||
),
|
</Space>
|
||||||
children: (
|
</div>
|
||||||
<Space
|
),
|
||||||
key={`space-${service.service_id}`}
|
children: (
|
||||||
direction="vertical"
|
<Space
|
||||||
style={{ width: "100%" }}
|
key={`space-${service.service_id}`}
|
||||||
>
|
direction="vertical"
|
||||||
{isRequestingActivities ? (
|
style={{ width: "100%" }}
|
||||||
<Skeleton active>
|
>
|
||||||
<Card title="loading">
|
{isRequestingActivities ? (
|
||||||
<p>loading</p>
|
<Skeleton active>
|
||||||
<p>loading</p>
|
<Card title="loading">
|
||||||
<p>loading</p>
|
<p>loading</p>
|
||||||
</Card>
|
<p>loading</p>
|
||||||
</Skeleton>
|
<p>loading</p>
|
||||||
) : (
|
</Card>
|
||||||
<>
|
</Skeleton>
|
||||||
{serviceActivities.length === 0 ? (
|
) : (
|
||||||
<MyEmpty />
|
<>
|
||||||
) : (
|
{serviceActivities.length === 0 ? (
|
||||||
serviceActivities.map((activity) => {
|
<MyEmpty />
|
||||||
let userList = [];
|
) : (
|
||||||
|
serviceActivities.map((activity) => {
|
||||||
|
let userList = [];
|
||||||
|
|
||||||
if (activity.StoreServiceActivityUsers.length > 0) {
|
if (activity.StoreServiceActivityUsers.length > 0) {
|
||||||
// StoreServiceActivityUsers is only an array of user_ids
|
// StoreServiceActivityUsers is only an array of user_ids
|
||||||
// we need to get the user object from the users array
|
// we need to get the user object from the users array
|
||||||
|
|
||||||
for (let i = 0; i < users.length; i++) {
|
for (let i = 0; i < users.length; i++) {
|
||||||
for (
|
for (
|
||||||
let j = 0;
|
let j = 0;
|
||||||
j < activity.StoreServiceActivityUsers.length;
|
j < activity.StoreServiceActivityUsers.length;
|
||||||
j++
|
j++
|
||||||
) {
|
|
||||||
if (
|
|
||||||
users[i].user_id ===
|
|
||||||
activity.StoreServiceActivityUsers[j].user_id
|
|
||||||
) {
|
) {
|
||||||
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 (
|
return (
|
||||||
<Card
|
<Card
|
||||||
key={activity.activity_id}
|
key={activity.activity_id}
|
||||||
title={activity.name}
|
title={activity.name}
|
||||||
extra={
|
extra={
|
||||||
<Space>
|
<Space>
|
||||||
<Avatar.Group maxCount={2} size="small">
|
<Avatar.Group maxCount={2} size="small">
|
||||||
{userList.map((user) => (
|
{userList.map((user) => (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
key={user.user_id}
|
key={user.user_id}
|
||||||
title={user.username}
|
title={user.username}
|
||||||
>
|
|
||||||
<Avatar
|
|
||||||
size="small"
|
|
||||||
style={{ backgroundColor: "#6878d6" }}
|
|
||||||
>
|
>
|
||||||
{user.username.charAt(0)}
|
<Avatar
|
||||||
</Avatar>
|
size="small"
|
||||||
</Tooltip>
|
style={{ backgroundColor: "#6878d6" }}
|
||||||
))}
|
>
|
||||||
</Avatar.Group>
|
{user.username.charAt(0)}
|
||||||
|
</Avatar>
|
||||||
|
</Tooltip>
|
||||||
|
))}
|
||||||
|
</Avatar.Group>
|
||||||
|
|
||||||
{/*
|
{/*
|
||||||
<ArrowUpOutlined disabled />
|
<ArrowUpOutlined disabled />
|
||||||
<ArrowDownOutlined disabled />
|
<ArrowDownOutlined disabled />
|
||||||
*/}
|
*/}
|
||||||
|
|
||||||
<MyEditIcon
|
<MyEditIcon
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setAddEditServiceActivityModalOptions({
|
setAddEditServiceActivityModalOptions({
|
||||||
mode: "edit",
|
mode: "edit",
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
activity: activity,
|
activity: activity,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<MyDeleteIcon
|
<MyDeleteIcon
|
||||||
propsPopconfirm={{
|
propsPopconfirm={{
|
||||||
placement: "left",
|
placement: "left",
|
||||||
}}
|
}}
|
||||||
onConfirm={() => {
|
onConfirm={() => {
|
||||||
return myFetch(
|
return myFetch({
|
||||||
`/store/services/activity/${activity.activity_id}`,
|
url: `/store/services/activity/${activity.activity_id}`,
|
||||||
"DELETE"
|
method: "DELETE",
|
||||||
);
|
notificationApi: notificationApi,
|
||||||
}}
|
t: t,
|
||||||
onFetchSuccess={fetchServiceActivities}
|
});
|
||||||
popConfirmTitle={t(
|
}}
|
||||||
"storeServices.popConfirmDeleteServiceActivity.title"
|
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(
|
</Typography.Text>
|
||||||
"storeServices.popConfirmDeleteServiceActivity.description"
|
</Typography.Paragraph>
|
||||||
)}
|
</Card>
|
||||||
/>
|
);
|
||||||
</Space>
|
})
|
||||||
}
|
)}
|
||||||
>
|
</>
|
||||||
<Typography.Title level={5}>
|
)}
|
||||||
{t("storeServices.serviceActivityDescription")}
|
</Space>
|
||||||
</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>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,6 +457,8 @@ function ModalAddEditService({
|
||||||
setAddEditServiceModalOptions,
|
setAddEditServiceModalOptions,
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const [notificationApi, notificationContextHolder] =
|
||||||
|
notification.useNotification();
|
||||||
const screenBreakpoint = useBreakpoint();
|
const screenBreakpoint = useBreakpoint();
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
|
@ -456,6 +485,8 @@ function ModalAddEditService({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{notificationContextHolder}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
icon={<PlusOutlined />}
|
icon={<PlusOutlined />}
|
||||||
|
@ -489,9 +520,15 @@ function ModalAddEditService({
|
||||||
.then((values) => {
|
.then((values) => {
|
||||||
setIsRequesting(true);
|
setIsRequesting(true);
|
||||||
|
|
||||||
myFetch("/store/services", "POST", {
|
myFetch({
|
||||||
storeId: storeId,
|
url: "/store/services",
|
||||||
name: values.serviceName,
|
method: "POST",
|
||||||
|
body: {
|
||||||
|
storeId: storeId,
|
||||||
|
name: values.serviceName,
|
||||||
|
},
|
||||||
|
notificationApi: notificationApi,
|
||||||
|
t: t,
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setIsRequesting(false);
|
setIsRequesting(false);
|
||||||
|
@ -527,9 +564,16 @@ function ModalAddEditService({
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setIsRequesting(true);
|
setIsRequesting(true);
|
||||||
|
|
||||||
myFetch("/store/services/update", "POST", {
|
myFetch({
|
||||||
serviceId: addEditServiceModalOptions.service.service_id,
|
url: "/store/services/update",
|
||||||
name: formServiceName,
|
method: "POST",
|
||||||
|
body: {
|
||||||
|
serviceId:
|
||||||
|
addEditServiceModalOptions.service.service_id,
|
||||||
|
name: formServiceName,
|
||||||
|
},
|
||||||
|
notificationApi: notificationApi,
|
||||||
|
t: t,
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setIsRequesting(false);
|
setIsRequesting(false);
|
||||||
|
@ -564,7 +608,8 @@ function ModalAddEditServiceActivity({
|
||||||
users,
|
users,
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const [notificationApi, notificationContextHolder] =
|
||||||
|
notification.useNotification();
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [isRequesting, setIsRequesting] = useState(false);
|
const [isRequesting, setIsRequesting] = useState(false);
|
||||||
const [selectedEmployeesRowKeys, setSelectedEmployeesRowKeys] = useState([]);
|
const [selectedEmployeesRowKeys, setSelectedEmployeesRowKeys] = useState([]);
|
||||||
|
@ -629,186 +674,202 @@ function ModalAddEditServiceActivity({
|
||||||
}, [addEditServiceActivityModalOptions.isOpen]);
|
}, [addEditServiceActivityModalOptions.isOpen]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MyModal
|
<>
|
||||||
title={
|
{notificationContextHolder}
|
||||||
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("/store/services/activity", "POST", {
|
<MyModal
|
||||||
serviceId:
|
title={
|
||||||
addEditServiceActivityModalOptions.service.service_id,
|
addEditServiceActivityModalOptions.mode === "add"
|
||||||
name: values.serviceActivityName,
|
? t("storeServices.modalAddServiceActivity.title")
|
||||||
description: values.serviceActivityDescription,
|
: t("storeServices.modalEditServiceActivity.title")
|
||||||
price: values.serviceActivityPrice,
|
}
|
||||||
duration: values.serviceActivityDurationMinutes,
|
isOpen={addEditServiceActivityModalOptions.isOpen}
|
||||||
userIds:
|
onCancel={handleModalClose}
|
||||||
selectedEmployeesRowKeys.length === users.length
|
footer={
|
||||||
? []
|
addEditServiceActivityModalOptions.mode === "add" ? (
|
||||||
: selectedEmployeesRowKeys,
|
<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(() => {
|
.catch((info) => {
|
||||||
setIsRequesting(false);
|
console.log("Validate Failed:", info);
|
||||||
handleModalClose();
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<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) => {
|
.then(() => {
|
||||||
console.log(errStatus);
|
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"
|
|
||||||
);
|
|
||||||
|
|
||||||
// if the service didn't change, don't send a request
|
fetchServices();
|
||||||
if (
|
})
|
||||||
addEditServiceActivityModalOptions.activity.name ===
|
.catch((errStatus) => {
|
||||||
formServiceActivityName &&
|
console.log(errStatus);
|
||||||
addEditServiceActivityModalOptions.activity.description ===
|
});
|
||||||
formServiceActivityDescription &&
|
})
|
||||||
addEditServiceActivityModalOptions.activity.price ===
|
.catch((info) => {
|
||||||
formServiceActivityPrice &&
|
console.log("Validate Failed:", info);
|
||||||
addEditServiceActivityModalOptions.activity.duration ===
|
});
|
||||||
formServiceActivityDurationMinutes &&
|
}}
|
||||||
(selectedEmployeesRowKeys.length === users.length ||
|
/>
|
||||||
addEditServiceActivityModalOptions.activity
|
)
|
||||||
.StoreServiceActivityUsers.length ===
|
}
|
||||||
selectedEmployeesRowKeys.length)
|
>
|
||||||
) {
|
<Form form={form} layout="vertical" requiredMark={false}>
|
||||||
handleModalClose();
|
<ServiceActivityNameFormInput formItemName="serviceActivityName" />
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let validateFields = [];
|
<ServiceActivityDescriptionFormInput formItemName="serviceActivityDescription" />
|
||||||
let body = {
|
|
||||||
activityId:
|
|
||||||
addEditServiceActivityModalOptions.activity.activity_id,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (
|
<ServiceActivityPriceFormInput formItemName="serviceActivityPrice" />
|
||||||
formServiceActivityName !==
|
|
||||||
addEditServiceActivityModalOptions.activity.name
|
|
||||||
) {
|
|
||||||
validateFields.push("serviceActivityName");
|
|
||||||
body.name = formServiceActivityName;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
<ServiceActivityDurationMinutesFormInput formItemName="serviceActivityDurationMinutes" />
|
||||||
formServiceActivityDescription !==
|
|
||||||
addEditServiceActivityModalOptions.activity.description
|
|
||||||
) {
|
|
||||||
validateFields.push("serviceActivityDescription");
|
|
||||||
body.description = formServiceActivityDescription;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
<Space direction="vertical" style={{ width: "100%" }}>
|
||||||
formServiceActivityPrice !==
|
<Typography.Text>
|
||||||
addEditServiceActivityModalOptions.activity.price
|
{t("storeServices.serviceActivityResponsible")}
|
||||||
) {
|
</Typography.Text>
|
||||||
validateFields.push("serviceActivityPrice");
|
|
||||||
body.price = formServiceActivityPrice;
|
|
||||||
}
|
|
||||||
|
|
||||||
let formDuration = formServiceActivityDurationMinutes;
|
<MyTable
|
||||||
|
props={{
|
||||||
if (
|
rowSelection: {
|
||||||
formDuration !==
|
selectedRowKeys: selectedEmployeesRowKeys,
|
||||||
addEditServiceActivityModalOptions.activity.duration
|
onChange: (newSelectedRowKeys) =>
|
||||||
) {
|
setSelectedEmployeesRowKeys(newSelectedRowKeys),
|
||||||
validateFields.push("serviceActivityDurationMinutes");
|
},
|
||||||
body.duration = formDuration;
|
loading: isRequesting,
|
||||||
}
|
columns: getTableColumns(),
|
||||||
|
dataSource: getTableItems(),
|
||||||
body.userIds = selectedEmployeesRowKeys;
|
size: "small",
|
||||||
|
pagination: false,
|
||||||
form
|
}}
|
||||||
.validateFields()
|
/>
|
||||||
.then(() => {
|
</Space>
|
||||||
setIsRequesting(true);
|
</Form>
|
||||||
|
</MyModal>
|
||||||
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>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { myFetch } from "../../../utils";
|
import { myFetch } from "../../../utils";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { Card, Form } from "antd";
|
import { Card, Form, notification } from "antd";
|
||||||
import { MyFormInput } from "../../../Components/MyFormInputs";
|
import { MyFormInput } from "../../../Components/MyFormInputs";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
|
@ -11,6 +11,8 @@ import {
|
||||||
|
|
||||||
export default function StoreSettings() {
|
export default function StoreSettings() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const [notificationApi, notificationContextHolder] =
|
||||||
|
notification.useNotification();
|
||||||
|
|
||||||
const { storeId } = useParams();
|
const { storeId } = useParams();
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
@ -26,7 +28,12 @@ export default function StoreSettings() {
|
||||||
const address = Form.useWatch("address", form);
|
const address = Form.useWatch("address", form);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
myFetch(`/store/${storeId}`, "GET")
|
myFetch({
|
||||||
|
url: `/store/${storeId}`,
|
||||||
|
method: "GET",
|
||||||
|
notificationApi: notificationApi,
|
||||||
|
t: t,
|
||||||
|
})
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
setStoreData(data.store);
|
setStoreData(data.store);
|
||||||
|
|
||||||
|
@ -65,11 +72,17 @@ export default function StoreSettings() {
|
||||||
}
|
}
|
||||||
|
|
||||||
delayTimeout.current = setTimeout(() => {
|
delayTimeout.current = setTimeout(() => {
|
||||||
myFetch(`/store/${storeId}`, "POST", {
|
myFetch({
|
||||||
name: companyName,
|
url: `/store/${storeId}`,
|
||||||
phoneNumber,
|
method: "POST",
|
||||||
email,
|
body: {
|
||||||
address,
|
name: companyName,
|
||||||
|
phoneNumber,
|
||||||
|
email,
|
||||||
|
address,
|
||||||
|
},
|
||||||
|
notificationApi: notificationApi,
|
||||||
|
t: t,
|
||||||
})
|
})
|
||||||
.then(() => setRequestState(RequestState.SUCCESS))
|
.then(() => setRequestState(RequestState.SUCCESS))
|
||||||
.catch((errStatus) => {
|
.catch((errStatus) => {
|
||||||
|
@ -81,6 +94,8 @@ export default function StoreSettings() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{notificationContextHolder}
|
||||||
|
|
||||||
<Card
|
<Card
|
||||||
title={t("storeSettings.pageTitle")}
|
title={t("storeSettings.pageTitle")}
|
||||||
loading={isRequesting}
|
loading={isRequesting}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { lazy, useEffect, useState } from "react";
|
||||||
import { isDevelopmentEnv, myFetch } from "../../../utils";
|
import { isDevelopmentEnv, myFetch } from "../../../utils";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import MyCenteredContainer from "../../../Components/MyContainer";
|
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 { useTranslation } from "react-i18next";
|
||||||
import { MySupsenseFallback } from "../../../Components/MySupsenseFallback";
|
import { MySupsenseFallback } from "../../../Components/MySupsenseFallback";
|
||||||
import PageInDevelopment from "../../PageInDevelopment";
|
import PageInDevelopment from "../../PageInDevelopment";
|
||||||
|
@ -20,6 +20,8 @@ function SuspenseFallback({ children }) {
|
||||||
export default function Website() {
|
export default function Website() {
|
||||||
const { storeId } = useParams();
|
const { storeId } = useParams();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const [notificationApi, notificationContextHolder] =
|
||||||
|
notification.useNotification();
|
||||||
|
|
||||||
const [isRequesting, setIsRequesting] = useState(true);
|
const [isRequesting, setIsRequesting] = useState(true);
|
||||||
const [website, setWebsite] = useState({});
|
const [website, setWebsite] = useState({});
|
||||||
|
@ -48,10 +50,13 @@ export default function Website() {
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
myFetch(`/website/${storeId}`, "GET")
|
myFetch({
|
||||||
.then((res) => {
|
url: `/website/${storeId}`,
|
||||||
setIsRequesting(false);
|
method: "GET",
|
||||||
})
|
notificationApi: notificationApi,
|
||||||
|
t: t,
|
||||||
|
})
|
||||||
|
.then(() => setIsRequesting(false))
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
setIsRequesting(false);
|
setIsRequesting(false);
|
||||||
|
|
||||||
|
@ -76,7 +81,9 @@ export default function Website() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<>
|
||||||
|
{notificationContextHolder}
|
||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
<Tabs
|
<Tabs
|
||||||
type="card"
|
type="card"
|
||||||
|
@ -86,12 +93,15 @@ export default function Website() {
|
||||||
onChange={(key) => setActiveTab(key)}
|
onChange={(key) => setActiveTab(key)}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function NoWebsiteCreateOne({ setWebsite }) {
|
function NoWebsiteCreateOne({ setWebsite }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const [notificationApi, notificationContextHolder] =
|
||||||
|
notification.useNotification();
|
||||||
|
|
||||||
const { storeId } = useParams();
|
const { storeId } = useParams();
|
||||||
|
|
||||||
const [isRequesting, setIsRequesting] = useState(false);
|
const [isRequesting, setIsRequesting] = useState(false);
|
||||||
|
@ -99,8 +109,14 @@ function NoWebsiteCreateOne({ setWebsite }) {
|
||||||
const handleCreateWebsite = () => {
|
const handleCreateWebsite = () => {
|
||||||
setIsRequesting(true);
|
setIsRequesting(true);
|
||||||
|
|
||||||
myFetch("/website", "POST", {
|
myFetch({
|
||||||
storeId,
|
url: "/website",
|
||||||
|
method: "POST",
|
||||||
|
body: {
|
||||||
|
storeId,
|
||||||
|
},
|
||||||
|
notificationApi: notificationApi,
|
||||||
|
t: t,
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
console.log(res);
|
console.log(res);
|
||||||
|
@ -116,6 +132,8 @@ function NoWebsiteCreateOne({ setWebsite }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MyCenteredContainer>
|
<MyCenteredContainer>
|
||||||
|
{notificationContextHolder}
|
||||||
|
|
||||||
<Result
|
<Result
|
||||||
status="404"
|
status="404"
|
||||||
title={t("storeWebsite.noWebsite.title")}
|
title={t("storeWebsite.noWebsite.title")}
|
||||||
|
|
|
@ -1,28 +1,39 @@
|
||||||
import { Button, Card, Select, Typography } from "antd";
|
import { Button, Card, Select, Typography, notification } from "antd";
|
||||||
import { Constants } from "../../utils";
|
import { myFetch } from "../../utils";
|
||||||
import { useTranslation } from "react-i18next";
|
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 { t, i18n } = useTranslation();
|
||||||
|
const [notificationApi, notificationContextHolder] =
|
||||||
|
notification.useNotification();
|
||||||
|
|
||||||
|
const [isRequestingLogout, setIsRequestingLogout] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{notificationContextHolder}
|
||||||
|
|
||||||
<Card
|
<Card
|
||||||
title={t("userProfile.title")}
|
title={t("userProfile.title")}
|
||||||
extra={
|
extra={
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
|
loading={isRequestingLogout}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setUserSession();
|
setIsRequestingLogout(true);
|
||||||
window.location.href = "/";
|
|
||||||
|
|
||||||
fetch(`${Constants.API_ADDRESS}/user/auth/logout`, {
|
myFetch({
|
||||||
|
url: "/user/auth/logout",
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
headers: {
|
notificationApi: notificationApi,
|
||||||
"Content-Type": "application/json",
|
t: t,
|
||||||
"X-Authorization": userSession,
|
})
|
||||||
},
|
.then(() => {
|
||||||
}).catch(console.error);
|
setUserSession();
|
||||||
|
window.location.href = "/";
|
||||||
|
})
|
||||||
|
.catch(() => setIsRequestingLogout(false));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t("common.button.logout")}
|
{t("common.button.logout")}
|
||||||
|
|
|
@ -6,6 +6,7 @@ body {
|
||||||
|
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
background-color: #f5f5f5;
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
|
|
1149
src/utils.js
1149
src/utils.js
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue