367 lines
9.7 KiB
JavaScript
367 lines
9.7 KiB
JavaScript
import { useState } from "react";
|
|
import { Buffer } from "buffer";
|
|
import { v4 as uuidv4 } from "uuid";
|
|
|
|
/**
|
|
* constants
|
|
*/
|
|
//const wssProtocol = window.location.protocol === "https:" ? "wss://" : "ws://";
|
|
|
|
let dashboardAddress = "";
|
|
let apiAddress = "";
|
|
// let staticContentAddress = "";
|
|
// let wsAddress = "";
|
|
|
|
if (window.location.hostname === "localhost" && window.location.port === "") {
|
|
// for docker container testing on localhost
|
|
dashboardAddress = "http://localhost/";
|
|
apiAddress = "http://localhost/api/v1";
|
|
// staticContentAddress = "http://localhost/api/";
|
|
// wsAddress = "ws://localhost/ws";
|
|
} else if (window.location.hostname === "localhost") {
|
|
// programming on localhost
|
|
dashboardAddress = `http://localhost:${window.location.port}/`;
|
|
apiAddress = "http://localhost:50128/api/v1";
|
|
// staticContentAddress = "http://localhost:50050/";
|
|
// wsAddress = "ws://localhost:50050/ws";
|
|
} else {
|
|
// production
|
|
dashboardAddress = `${window.location.protocol}//${window.location.hostname}/`;
|
|
apiAddress = `${window.location.protocol}//${window.location.hostname}/api/v1`;
|
|
//staticContentAddress = `${window.location.protocol}//${window.location.hostname}/api/`;
|
|
// wsAddress = `${wssProtocol}${window.location.hostname}/ws`;
|
|
}
|
|
|
|
export const Constants = {
|
|
APP_NAME: "ZeitAdler",
|
|
TEXT_EMPTY_PLACEHOLDER: "-/-",
|
|
DASHBOARD_ADDRESS: dashboardAddress,
|
|
API_ADDRESS: apiAddress,
|
|
//STATIC_CONTENT_ADDRESS: staticContentAddress,
|
|
// WS_ADDRESS: wsAddress,
|
|
EMBED_CALENDAR_ADDRESS: process.env.REACT_APP_EMBED_CALENDAR_ADDRESS,
|
|
EMBED_CALENDAR_SCRIPT_ADDRESS:
|
|
process.env.REACT_APP_EMBED_CALENDAR_SCRIPT_ADDRESS,
|
|
PRIVACY_POLICY_URL: process.env.REACT_APP_PRIVACY_POLICY_URL,
|
|
ROUTE_PATHS: {
|
|
AUTHENTICATION: {
|
|
LOGIN: "/login",
|
|
SIGN_UP: "/buy",
|
|
FORGOT_PASSWORD: "/forgot-password",
|
|
CHECKOUT_SUCCESS: "/checkout/success",
|
|
CHECKOUT_CANCELED: "/checkout/canceled",
|
|
},
|
|
VERIFY: "/verify",
|
|
OVERVIEW: "/",
|
|
STORE: {
|
|
// schema: /store/:storeId/:subPage
|
|
OVERVIEW: "/store",
|
|
SETTINGS: "settings",
|
|
EMPLOYEES: "employees",
|
|
SERVICES: "services",
|
|
CALENDAR: "calendar",
|
|
CALENDAR_AUTH: "calendar/auth",
|
|
},
|
|
PAYMENT_PLAN: "payment-plan",
|
|
FEEDBACK: "/feedback",
|
|
SUPPORT: "/support",
|
|
USER_PROFILE: "/user-profile",
|
|
},
|
|
GLOBALS: {
|
|
MIN_USERNAME_LENGTH: 3,
|
|
MAX_USERNAME_LENGTH: 20,
|
|
//MIN_ACCOUNT_NAME_LENGTH: 3,
|
|
//MAX_ACCOUNT_NAME_LENGTH: 20,
|
|
MIN_EMAIL_LENGTH: 3,
|
|
MAX_EMAIL_LENGTH: 64,
|
|
EMAIL_REGEX: /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/,
|
|
MIN_PASSWORD_LENGTH: 8,
|
|
MAX_PASSWORD_LENGTH: 64,
|
|
MIN_STORE_SERVICE_NAME_LENGTH: 3,
|
|
MAX_STORE_SERVICE_NAME_LENGTH: 64,
|
|
MIN_STORE_SERVICE_ACTIVITY_NAME_LENGTH: 3,
|
|
MAX_STORE_SERVICE_ACTIVITY_NAME_LENGTH: 64,
|
|
MAX_STORE_SERVICE_ACTIVITY_DESCRIPTION_LENGTH: 1024,
|
|
MIN_STORE_SERVICE_ACTIVITY_PRICE: 0,
|
|
MAX_STORE_SERVICE_ACTIVITY_PRICE: 10000000,
|
|
MIN_STORE_SERVICE_ACTIVITY_DURATION_MINUTES: 0,
|
|
MAX_STORE_SERVICE_ACTIVITY_DURATION_MINUTES: 60 * 24, // 24 hours in minutes
|
|
MIN_CALENDAR_FUTURE_BOOKING_DAYS: 1,
|
|
MIN_CALENDAR_EARLIEST_BOOKING_TIME: 15,
|
|
MAX_CALENDAR_EARLIEST_BOOKING_TIME: 60 * 24, // 24 hours in minutes
|
|
MIN_FEEDBACK_LENGTH: 10,
|
|
MAX_FEEDBACK_LENGTH: 1024,
|
|
MIN_COMPANY_NAME_LENGTH: 3,
|
|
MAX_COMPANY_NAME_LENGTH: 64,
|
|
},
|
|
DELAY_EMAIL_CHECK: 250,
|
|
CLARITY_PROJECT_ID: process.env.REACT_APP_CLARITY_PROJECT_ID,
|
|
ACCOUNT_DELETED_AFTER_DAYS: 30,
|
|
ACCOUNT_STATE: {
|
|
ACTIVE: 0,
|
|
PENDING_DELETION: 1,
|
|
INIT_LOGGING: 2,
|
|
BANNED: 3,
|
|
PENDING_EMAIL_VERIFICATION: 4,
|
|
},
|
|
SUPPORT_EMAIL: process.env.REACT_APP_SUPPORT_EMAIL,
|
|
PAYMENT_PLAN: [
|
|
{
|
|
name: "Demo",
|
|
maxEmployees: 5,
|
|
calendarMaxFutureBookingDays: 14,
|
|
},
|
|
{
|
|
name: "Basic",
|
|
maxEmployees: 15,
|
|
calendarMaxFutureBookingDays: 60,
|
|
price: {
|
|
en: {
|
|
totalNet: "9,99 €", // netto
|
|
vat: "1,90 €",
|
|
totalAmount: "11,89 €", // brutto
|
|
},
|
|
de: {
|
|
totalNet: "9,99 €",
|
|
vat: "1,90 €",
|
|
totalAmount: "11,89 €",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Premium",
|
|
maxEmployees: 50, // temporary
|
|
calendarMaxFutureBookingDays: 365, // temporary
|
|
price: {
|
|
en: {
|
|
totalNet: "24,99 €",
|
|
vat: "4,75 €",
|
|
totalAmount: "29,74 €",
|
|
},
|
|
de: {
|
|
totalNet: "24,99 €",
|
|
vat: "4,75 €",
|
|
totalAmount: "29,74 €",
|
|
},
|
|
},
|
|
},
|
|
],
|
|
};
|
|
|
|
export function isEmailValid(email) {
|
|
return Constants.GLOBALS.EMAIL_REGEX.test(email);
|
|
}
|
|
|
|
export const AppStyle = {
|
|
app: {
|
|
margin: 12,
|
|
},
|
|
colors: {
|
|
primary: "#6878d6",
|
|
},
|
|
};
|
|
|
|
export function GetUuid() {
|
|
return uuidv4();
|
|
}
|
|
|
|
/**
|
|
* user session
|
|
*/
|
|
|
|
export function UseUserSession() {
|
|
const getUserSession = () => {
|
|
return getUserSessionFromLocalStorage();
|
|
};
|
|
|
|
const [userSession, setUserSession] = useState(getUserSession());
|
|
|
|
const saveUserSession = (session) => {
|
|
setUserSession(session);
|
|
|
|
if (session === undefined) {
|
|
localStorage.removeItem("session");
|
|
} else {
|
|
setUserSessionToLocalStorage(session);
|
|
}
|
|
};
|
|
|
|
return {
|
|
setUserSession: saveUserSession,
|
|
userSession,
|
|
};
|
|
}
|
|
|
|
export function getUserSessionFromLocalStorage() {
|
|
return localStorage.getItem("session");
|
|
}
|
|
|
|
export function setUserSessionToLocalStorage(session) {
|
|
localStorage.setItem("session", session);
|
|
}
|
|
|
|
// needed for a user who uses multiple tabs in the browser
|
|
// with the same session id because otherwise the last browser
|
|
// tab would subscribe to the topic and the other tabs would
|
|
// not receive any messages
|
|
// used for topic subscribtion and grouptasks
|
|
export const BrowserTabSession = GetUuid();
|
|
|
|
export const wsConnectionCustomEventName = "wsConnection";
|
|
|
|
// used for sideMenu
|
|
export const BreakpointLgWidth = 992;
|
|
|
|
export function EncodeStringToBase64(value) {
|
|
return Buffer.from(value).toString("base64");
|
|
}
|
|
|
|
export function DecodedBase64ToString(value) {
|
|
return Buffer.from(value, "base64").toString();
|
|
}
|
|
|
|
export const myFetchContentType = {
|
|
JSON: 0,
|
|
MULTIPART_FORM_DATA: 1,
|
|
};
|
|
|
|
export function myFetch({
|
|
url,
|
|
method,
|
|
body = null,
|
|
headers = {},
|
|
contentType = myFetchContentType.JSON,
|
|
fetchUrl = Constants.API_ADDRESS,
|
|
ignoreUnauthorized = false,
|
|
notificationApi = null,
|
|
t = null,
|
|
}) {
|
|
const getContentType = () => {
|
|
if (contentType === myFetchContentType.JSON) return "application/json";
|
|
|
|
return "multipart/form-data";
|
|
};
|
|
|
|
const getBody = () => {
|
|
if (!body) return null;
|
|
|
|
if (contentType === myFetchContentType.JSON) return JSON.stringify(body);
|
|
|
|
return body;
|
|
};
|
|
|
|
// abort fetch if it takes to long
|
|
const controller = new AbortController();
|
|
const signal = controller.signal;
|
|
|
|
setTimeout(() => controller.abort(), 30000); // 30 seconds
|
|
|
|
const requestOptions = {
|
|
method: method,
|
|
headers: {
|
|
"X-Authorization": getUserSessionFromLocalStorage(),
|
|
"Content-Type": getContentType(),
|
|
...headers,
|
|
},
|
|
body: getBody(),
|
|
signal: signal,
|
|
};
|
|
|
|
if (fetchUrl === "") {
|
|
fetchUrl = Constants.API_ADDRESS;
|
|
}
|
|
|
|
return fetch(`${fetchUrl}${url}`, requestOptions)
|
|
.then((response) => {
|
|
// if status is not in range 200-299
|
|
if (!response.ok) {
|
|
if (!ignoreUnauthorized && response.status === 401) {
|
|
console.error("Unauthorized");
|
|
// TODO: check here
|
|
//setUserSessionToLocalStorage("");
|
|
//window.location.href = "/";
|
|
}
|
|
|
|
throw response.status;
|
|
}
|
|
|
|
// check if response is json
|
|
if (response.headers.get("content-type")?.includes("application/json")) {
|
|
return response.json();
|
|
}
|
|
|
|
return response.text();
|
|
})
|
|
.catch((error) => {
|
|
// ignore errors here as they are handled in the components
|
|
if (error === 400) throw error;
|
|
|
|
if (error === 401) {
|
|
handleLogout({
|
|
setUserSession: setUserSessionToLocalStorage,
|
|
});
|
|
return;
|
|
}
|
|
|
|
// show error notification for all other errors
|
|
|
|
if (notificationApi !== null && t !== null) {
|
|
if (error === 500) {
|
|
notificationApi["error"]({
|
|
message: t("common.request.failed.title"),
|
|
description: t("common.request.failed.description"),
|
|
});
|
|
} else {
|
|
// other errors
|
|
notificationApi["error"]({
|
|
message: t("common.request.failedInternetProblem.title"),
|
|
description: t("common.request.failedInternetProblem.description"),
|
|
});
|
|
}
|
|
}
|
|
|
|
throw error;
|
|
});
|
|
}
|
|
|
|
export function isDevelopmentEnv() {
|
|
return process.env.NODE_ENV === "development";
|
|
}
|
|
|
|
export function showInputsInvalidNotification(notificationApi, t) {
|
|
notificationApi["warning"]({
|
|
message: t("common.request.inputsInvalid.title"),
|
|
description: t("common.request.inputsInvalid.description"),
|
|
});
|
|
}
|
|
|
|
export function showPasswordIncorrectNotification(notificationApi, t) {
|
|
notificationApi["warning"]({
|
|
message: t("common.request.passwordIncorrect.title"),
|
|
description: t("common.request.passwordIncorrect.description"),
|
|
});
|
|
}
|
|
|
|
export function showUnkownErrorNotification(notificationApi, t) {
|
|
notificationApi["error"]({
|
|
message: t("common.request.unknownError.title"),
|
|
description: t("common.request.unknownError.description"),
|
|
});
|
|
}
|
|
|
|
export function handleLogout({ setUserSession }) {
|
|
console.log("handleLogout");
|
|
|
|
if (setUserSession !== undefined) setUserSession();
|
|
|
|
window.location.href = Constants.ROUTE_PATHS.AUTHENTICATION.LOGIN;
|
|
}
|
|
|
|
export function FormatDatetime(datetime) {
|
|
if (datetime === undefined || datetime === "0001-01-01T00:00:00Z") {
|
|
return Constants.TEXT_EMPTY_PLACEHOLDER;
|
|
}
|
|
|
|
return new Date(datetime).toLocaleString("de-DE");
|
|
}
|