Skip to content

Commit c966545

Browse files
authored
Merge pull request #428 from HiEventsDev/develop
2 parents bc517a8 + e41d27d commit c966545

File tree

8 files changed

+232
-71
lines changed

8 files changed

+232
-71
lines changed

frontend/src/App.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import "./styles/global.scss";
1717
import {isSsr} from "./utilites/helpers.ts";
1818
import {dynamicActivateLocale, getSupportedLocale} from "./locales";
1919
import {StartupChecks} from "./StartupChecks.tsx";
20+
import {ThirdPartyScripts} from "./components/common/ThirdPartyScripts";
2021

2122
declare global {
2223
interface Window {
@@ -84,6 +85,7 @@ export const App: FC<
8485
<I18nProvider i18n={i18n}>
8586
<QueryClientProvider client={props.queryClient}>
8687
<StartupChecks/>
88+
<ThirdPartyScripts/>
8789
<ModalsProvider>
8890
<Helmet>
8991
<title>Hi.Events</title>

frontend/src/api/client.ts

+1-14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import axios from "axios";
2-
import {setAuthToken} from "../utilites/apiClient.ts";
32
import {isSsr} from "../utilites/helpers.ts";
43
import {getConfig} from "../utilites/config.ts";
54

@@ -34,20 +33,8 @@ export const api = axios.create({
3433
withCredentials: true,
3534
});
3635

37-
const existingToken = typeof window !== "undefined" ? window.localStorage.getItem('token') : undefined;
38-
if (existingToken) {
39-
setAuthToken(existingToken);
40-
}
41-
4236
api.interceptors.response.use(
43-
(response) => {
44-
const token = response?.data?.token || response?.headers["x-auth-token"];
45-
if (token) {
46-
window?.localStorage?.setItem('token', token);
47-
setAuthToken(token);
48-
}
49-
return response;
50-
},
37+
(response) => response,
5138
(error) => {
5239
const { status } = error.response;
5340
const currentPath = window?.location.pathname;

frontend/src/api/public-client.ts

-6
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,6 @@ export const publicApi = axios.create({
66
withCredentials: true,
77
});
88

9-
const existingToken = typeof window !== "undefined" ? window?.localStorage?.getItem('token') : undefined;
10-
11-
if (existingToken) {
12-
publicApi.defaults.headers.common['Authorization'] = `Bearer ${existingToken}`;
13-
}
14-
159
publicApi.interceptors.request.use((config) => {
1610
const baseUrl = isSsr()
1711
? getConfig('VITE_API_URL_SERVER')
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,107 @@
1+
.wrapper {
2+
position: relative;
3+
overflow: hidden;
4+
min-height: 100vh;
5+
display: flex;
6+
align-items: center;
7+
justify-content: center;
8+
background-repeat: no-repeat;
9+
background-position: center;
10+
}
11+
12+
.backgroundOrb1 {
13+
position: absolute;
14+
top: 20%;
15+
left: -10%;
16+
width: 40%;
17+
height: 40%;
18+
border-radius: 50%;
19+
background-color: rgba(147, 51, 234, 0.1);
20+
filter: blur(100px);
21+
animation: drift 20s ease-in-out infinite;
22+
z-index: -1;
23+
}
24+
25+
.backgroundOrb2 {
26+
position: absolute;
27+
bottom: 10%;
28+
right: -10%;
29+
width: 40%;
30+
height: 40%;
31+
border-radius: 50%;
32+
background-color: rgba(236, 72, 153, 0.08);
33+
filter: blur(120px);
34+
animation: drift-slow 25s ease-in-out infinite;
35+
z-index: -1;
36+
}
37+
38+
@keyframes drift {
39+
0%, 100% {
40+
transform: translate(0, 0);
41+
}
42+
50% {
43+
transform: translate(5%, 5%);
44+
}
45+
}
46+
47+
@keyframes drift-slow {
48+
0%, 100% {
49+
transform: translate(0, 0);
50+
}
51+
50% {
52+
transform: translate(-5%, -5%);
53+
}
54+
}
55+
156
.root {
2-
padding-top: 80px;
3-
padding-bottom: 80px;
457
display: flex;
558
flex-direction: column;
59+
justify-content: center;
60+
align-items: center;
61+
padding: var(--mantine-spacing-xl) 0;
62+
position: relative;
63+
z-index: 1;
664
}
765

866
.logo {
967
display: flex;
1068
justify-content: center;
11-
margin-bottom: var(--mantine-spacing-xl);
12-
13-
img {
14-
width: 100px;
15-
height: 100px;
16-
}
1769
}
1870

19-
.label {
71+
.content {
72+
max-width: 500px;
2073
text-align: center;
21-
font-weight: 900;
22-
font-size: 40px;
23-
line-height: 1;
24-
margin-bottom: calc(1.5 * var(--mantine-spacing-xl));
25-
color: var(--tk-color-text);
26-
74+
background-color: var(--mantine-color-body);
75+
padding: var(--mantine-spacing-xl);
76+
border-radius: var(--mantine-radius-lg);
77+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08);
78+
backdrop-filter: blur(12px);
79+
border: 1px solid rgba(147, 51, 234, 0.1);
2780
}
2881

29-
.description {
30-
max-width: 500px;
31-
margin: var(--mantine-spacing-xl) auto calc(1.5 * var(--mantine-spacing-xl));
82+
.title {
83+
font-weight: 800;
84+
margin-bottom: var(--mantine-spacing-md);
85+
font-size: 32px;
86+
background: linear-gradient(to right, var(--mantine-color-purple-6), var(--mantine-color-pink-6));
87+
-webkit-background-clip: text;
88+
-webkit-text-fill-color: transparent;
89+
90+
@media (max-width: 768px) {
91+
font-size: 28px
92+
}
3293
}
3394

34-
.title {
35-
font-family:
36-
Greycliff CF,
37-
var(--mantine-font-family);
38-
text-align: center;
39-
font-weight: 900;
40-
font-size:38px;
95+
.description {
96+
line-height: 1.6;
97+
margin-bottom: var(--mantine-spacing-lg);
4198
}
4299

43-
.actions {
44-
display: flex;
45-
justify-content: center;
46-
margin-top: var(--mantine-spacing-xl);
100+
.button {
101+
transition: all 250ms ease;
47102

48-
.link {
49-
font-weight: 900;
50-
margin: 0 var(--mantine-spacing-xs);
103+
&:hover {
104+
transform: translateY(-3px);
105+
box-shadow: var(--mantine-shadow-md);
51106
}
52-
}
107+
}
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
import {Container} from '@mantine/core';
1+
import {t} from '@lingui/macro';
2+
import {Box, Button, Container, Image, rem, Stack, Text, Title} from '@mantine/core';
3+
import {IconHome} from '@tabler/icons-react';
24
import classes from './ErrorDisplay.module.scss';
5+
import {Helmet} from "react-helmet-async";
36
import {useRouteError} from "react-router";
4-
import {t} from "@lingui/macro";
7+
import {PoweredByFooter} from "../PoweredByFooter";
58

69
export const ErrorDisplay = () => {
7-
const error = useRouteError() as any
10+
const error = useRouteError() as any;
811

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

1720
return (
18-
<Container className={classes.root}>
19-
<div className={classes.logo}>
20-
<img src="/logo-dark.svg" alt="Error"/>
21-
</div>
22-
<h2 className={classes.title}>{title}</h2>
23-
<div className={classes.description}>
24-
{description}
25-
</div>
26-
27-
<div className={classes.actions}>
28-
Go back to the <a href="/" className={classes.link}>home page</a>
29-
</div>
30-
</Container>
21+
<>
22+
<Helmet
23+
title={title}
24+
meta={[
25+
{
26+
name: 'description',
27+
content: description,
28+
},
29+
]}
30+
/>
31+
<Box className={classes.wrapper}>
32+
33+
{/* Animated background elements */}
34+
<div className={classes.backgroundOrb1}/>
35+
<div className={classes.backgroundOrb2}/>
36+
37+
<Container size="md" className={classes.root}>
38+
<Stack gap="xl" align="center">
39+
<Image
40+
src="/logo-dark.svg"
41+
alt="Error"
42+
w={rem(140)}
43+
h="auto"
44+
fit="contain"
45+
className={classes.logo}
46+
/>
47+
48+
<Stack gap="lg" align="center" className={classes.content}>
49+
<Title order={1} className={classes.title}>
50+
{title}
51+
</Title>
52+
53+
<Text size="lg" c="dimmed" className={classes.description}>
54+
{description}
55+
</Text>
56+
<Button
57+
component="a"
58+
href="/"
59+
leftSection={<IconHome size={18}/>}
60+
variant="gradient"
61+
gradient={{from: 'purple', to: 'pink'}}
62+
className={classes.button}
63+
>
64+
{t`Go to home page`}
65+
</Button>
66+
</Stack>
67+
68+
<PoweredByFooter/>
69+
</Stack>
70+
</Container>
71+
</Box>
72+
</>
3173
);
32-
};
74+
};
75+
76+
export default ErrorDisplay;

frontend/src/components/common/PoweredByFooter/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export const PoweredByFooter = (props: React.DetailedHTMLProps<React.HTMLAttribu
2929
{/* eslint-disable-next-line lingui/no-unlocalized-strings */}
3030
<a href="https://hi.events?utm_source=app-powered-by-footer"
3131
target="_blank"
32-
title={'Effortlessly manage events and sell products online with Hi.Events'}>
32+
title={'Effortlessly manage events and sell tickets online with Hi.Events'}>
3333
Hi.Events
3434
</a> 🚀
3535
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import ZohoChatWidget from "../ZohoChatWidget";
2+
3+
export const ThirdPartyScripts = () => {
4+
return (
5+
<>
6+
<ZohoChatWidget/>
7+
</>
8+
);
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import {useEffect, useRef} from "react";
2+
import {isSsr} from "../../../utilites/helpers.ts";
3+
4+
const ZOHO_ROUTES = ["/manage", "/account", "/welcome"];
5+
6+
const ZohoChatWidget = () => {
7+
const scriptRef = useRef<HTMLScriptElement | null>(null);
8+
9+
useEffect(() => {
10+
if (isSsr()) return;
11+
12+
try {
13+
const pathname = window.location.pathname;
14+
const widgetCode = window.hievents?.VITE_ZOHO_WIDGET_CODE;
15+
16+
if (!widgetCode || !ZOHO_ROUTES.some(route => pathname.includes(route))) return;
17+
18+
window.$zoho = window.$zoho || {};
19+
window.$zoho.salesiq = window.$zoho.salesiq || {
20+
ready: () => {
21+
}
22+
};
23+
24+
if (!document.getElementById("zsiqscript")) {
25+
const script = document.createElement("script");
26+
script.id = "zsiqscript";
27+
script.src = `https://salesiq.zohopublic.eu/widget?wc=${widgetCode}`;
28+
script.defer = true;
29+
document.body.appendChild(script);
30+
scriptRef.current = script;
31+
}
32+
33+
const handleUrlChange = () => {
34+
try {
35+
const currentPath = window.location.pathname;
36+
if (!ZOHO_ROUTES.some(route => currentPath.includes(route))) {
37+
removeZohoScript();
38+
}
39+
} catch (error) {
40+
console.error("Error in ZohoChatWidget URL change handler:", error);
41+
}
42+
};
43+
44+
window.addEventListener("popstate", handleUrlChange);
45+
46+
return () => {
47+
window.removeEventListener("popstate", handleUrlChange);
48+
removeZohoScript();
49+
};
50+
} catch (error) {
51+
console.error("Error initializing ZohoChatWidget:", error);
52+
}
53+
}, []);
54+
55+
const removeZohoScript = () => {
56+
try {
57+
const script = document.getElementById("zsiqscript");
58+
if (script) {
59+
script.remove();
60+
scriptRef.current = null;
61+
}
62+
} catch (error) {
63+
console.error("Error removing ZohoChatWidget script:", error);
64+
}
65+
};
66+
67+
return null;
68+
};
69+
70+
export default ZohoChatWidget;

0 commit comments

Comments
 (0)