Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import "./styles/global.scss";
import {isSsr} from "./utilites/helpers.ts";
import {dynamicActivateLocale, getSupportedLocale} from "./locales";
import {StartupChecks} from "./StartupChecks.tsx";
import {ThirdPartyScripts} from "./components/common/ThirdPartyScripts";

declare global {
interface Window {
Expand Down Expand Up @@ -84,6 +85,7 @@ export const App: FC<
<I18nProvider i18n={i18n}>
<QueryClientProvider client={props.queryClient}>
<StartupChecks/>
<ThirdPartyScripts/>
<ModalsProvider>
<Helmet>
<title>Hi.Events</title>
Expand Down
15 changes: 1 addition & 14 deletions frontend/src/api/client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import axios from "axios";
import {setAuthToken} from "../utilites/apiClient.ts";
import {isSsr} from "../utilites/helpers.ts";
import {getConfig} from "../utilites/config.ts";

Expand Down Expand Up @@ -34,20 +33,8 @@ export const api = axios.create({
withCredentials: true,
});

const existingToken = typeof window !== "undefined" ? window.localStorage.getItem('token') : undefined;
if (existingToken) {
setAuthToken(existingToken);
}

api.interceptors.response.use(
(response) => {
const token = response?.data?.token || response?.headers["x-auth-token"];
if (token) {
window?.localStorage?.setItem('token', token);
setAuthToken(token);
}
return response;
},
(response) => response,
(error) => {
const { status } = error.response;
const currentPath = window?.location.pathname;
Expand Down
6 changes: 0 additions & 6 deletions frontend/src/api/public-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@ export const publicApi = axios.create({
withCredentials: true,
});

const existingToken = typeof window !== "undefined" ? window?.localStorage?.getItem('token') : undefined;

if (existingToken) {
publicApi.defaults.headers.common['Authorization'] = `Bearer ${existingToken}`;
}

publicApi.interceptors.request.use((config) => {
const baseUrl = isSsr()
? getConfig('VITE_API_URL_SERVER')
Expand Down
121 changes: 88 additions & 33 deletions frontend/src/components/common/ErrorDisplay/ErrorDisplay.module.scss
Original file line number Diff line number Diff line change
@@ -1,52 +1,107 @@
.wrapper {
position: relative;
overflow: hidden;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background-repeat: no-repeat;
background-position: center;
}

.backgroundOrb1 {
position: absolute;
top: 20%;
left: -10%;
width: 40%;
height: 40%;
border-radius: 50%;
background-color: rgba(147, 51, 234, 0.1);
filter: blur(100px);
animation: drift 20s ease-in-out infinite;
z-index: -1;
}

.backgroundOrb2 {
position: absolute;
bottom: 10%;
right: -10%;
width: 40%;
height: 40%;
border-radius: 50%;
background-color: rgba(236, 72, 153, 0.08);
filter: blur(120px);
animation: drift-slow 25s ease-in-out infinite;
z-index: -1;
}

@keyframes drift {
0%, 100% {
transform: translate(0, 0);
}
50% {
transform: translate(5%, 5%);
}
}

@keyframes drift-slow {
0%, 100% {
transform: translate(0, 0);
}
50% {
transform: translate(-5%, -5%);
}
}

.root {
padding-top: 80px;
padding-bottom: 80px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: var(--mantine-spacing-xl) 0;
position: relative;
z-index: 1;
}

.logo {
display: flex;
justify-content: center;
margin-bottom: var(--mantine-spacing-xl);

img {
width: 100px;
height: 100px;
}
}

.label {
.content {
max-width: 500px;
text-align: center;
font-weight: 900;
font-size: 40px;
line-height: 1;
margin-bottom: calc(1.5 * var(--mantine-spacing-xl));
color: var(--tk-color-text);

background-color: var(--mantine-color-body);
padding: var(--mantine-spacing-xl);
border-radius: var(--mantine-radius-lg);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08);
backdrop-filter: blur(12px);
border: 1px solid rgba(147, 51, 234, 0.1);
}

.description {
max-width: 500px;
margin: var(--mantine-spacing-xl) auto calc(1.5 * var(--mantine-spacing-xl));
.title {
font-weight: 800;
margin-bottom: var(--mantine-spacing-md);
font-size: 32px;
background: linear-gradient(to right, var(--mantine-color-purple-6), var(--mantine-color-pink-6));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;

@media (max-width: 768px) {
font-size: 28px
}
}

.title {
font-family:
Greycliff CF,
var(--mantine-font-family);
text-align: center;
font-weight: 900;
font-size:38px;
.description {
line-height: 1.6;
margin-bottom: var(--mantine-spacing-lg);
}

.actions {
display: flex;
justify-content: center;
margin-top: var(--mantine-spacing-xl);
.button {
transition: all 250ms ease;

.link {
font-weight: 900;
margin: 0 var(--mantine-spacing-xs);
&:hover {
transform: translateY(-3px);
box-shadow: var(--mantine-shadow-md);
}
}
}
78 changes: 61 additions & 17 deletions frontend/src/components/common/ErrorDisplay/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import {Container} from '@mantine/core';
import {t} from '@lingui/macro';
import {Box, Button, Container, Image, rem, Stack, Text, Title} from '@mantine/core';
import {IconHome} from '@tabler/icons-react';
import classes from './ErrorDisplay.module.scss';
import {Helmet} from "react-helmet-async";
import {useRouteError} from "react-router";
import {t} from "@lingui/macro";
import {PoweredByFooter} from "../PoweredByFooter";

export const ErrorDisplay = () => {
const error = useRouteError() as any
const error = useRouteError() as any;

const title = error?.status === 404
? t`Page not found`
Expand All @@ -15,18 +18,59 @@ export const ErrorDisplay = () => {
: t`An error occurred while loading the page`;

return (
<Container className={classes.root}>
<div className={classes.logo}>
<img src="/logo-dark.svg" alt="Error"/>
</div>
<h2 className={classes.title}>{title}</h2>
<div className={classes.description}>
{description}
</div>

<div className={classes.actions}>
Go back to the <a href="/" className={classes.link}>home page</a>
</div>
</Container>
<>
<Helmet
title={title}
meta={[
{
name: 'description',
content: description,
},
]}
/>
<Box className={classes.wrapper}>

{/* Animated background elements */}
<div className={classes.backgroundOrb1}/>
<div className={classes.backgroundOrb2}/>

<Container size="md" className={classes.root}>
<Stack gap="xl" align="center">
<Image
src="/logo-dark.svg"
alt="Error"
w={rem(140)}
h="auto"
fit="contain"
className={classes.logo}
/>

<Stack gap="lg" align="center" className={classes.content}>
<Title order={1} className={classes.title}>
{title}
</Title>

<Text size="lg" c="dimmed" className={classes.description}>
{description}
</Text>
<Button
component="a"
href="/"
leftSection={<IconHome size={18}/>}
variant="gradient"
gradient={{from: 'purple', to: 'pink'}}
className={classes.button}
>
{t`Go to home page`}
</Button>
</Stack>

<PoweredByFooter/>
</Stack>
</Container>
</Box>
</>
);
};
};

export default ErrorDisplay;
2 changes: 1 addition & 1 deletion frontend/src/components/common/PoweredByFooter/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const PoweredByFooter = (props: React.DetailedHTMLProps<React.HTMLAttribu
{/* eslint-disable-next-line lingui/no-unlocalized-strings */}
<a href="https://hi.events?utm_source=app-powered-by-footer"
target="_blank"
title={'Effortlessly manage events and sell products online with Hi.Events'}>
title={'Effortlessly manage events and sell tickets online with Hi.Events'}>
Hi.Events
</a> 🚀
</div>
Expand Down
9 changes: 9 additions & 0 deletions frontend/src/components/common/ThirdPartyScripts/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import ZohoChatWidget from "../ZohoChatWidget";

export const ThirdPartyScripts = () => {
return (
<>
<ZohoChatWidget/>
</>
);
}
70 changes: 70 additions & 0 deletions frontend/src/components/common/ZohoChatWidget/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {useEffect, useRef} from "react";
import {isSsr} from "../../../utilites/helpers.ts";

const ZOHO_ROUTES = ["/manage", "/account", "/welcome"];

const ZohoChatWidget = () => {
const scriptRef = useRef<HTMLScriptElement | null>(null);

useEffect(() => {
if (isSsr()) return;

try {
const pathname = window.location.pathname;
const widgetCode = window.hievents?.VITE_ZOHO_WIDGET_CODE;

if (!widgetCode || !ZOHO_ROUTES.some(route => pathname.includes(route))) return;

window.$zoho = window.$zoho || {};
window.$zoho.salesiq = window.$zoho.salesiq || {
ready: () => {
}
};

if (!document.getElementById("zsiqscript")) {
const script = document.createElement("script");
script.id = "zsiqscript";
script.src = `https://salesiq.zohopublic.eu/widget?wc=${widgetCode}`;
script.defer = true;
document.body.appendChild(script);
scriptRef.current = script;
}

const handleUrlChange = () => {
try {
const currentPath = window.location.pathname;
if (!ZOHO_ROUTES.some(route => currentPath.includes(route))) {
removeZohoScript();
}
} catch (error) {
console.error("Error in ZohoChatWidget URL change handler:", error);
}
};

window.addEventListener("popstate", handleUrlChange);

return () => {
window.removeEventListener("popstate", handleUrlChange);
removeZohoScript();
};
} catch (error) {
console.error("Error initializing ZohoChatWidget:", error);
}
}, []);

const removeZohoScript = () => {
try {
const script = document.getElementById("zsiqscript");
if (script) {
script.remove();
scriptRef.current = null;
}
} catch (error) {
console.error("Error removing ZohoChatWidget script:", error);
}
};

return null;
};

export default ZohoChatWidget;