diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 6f60660247..2cbb1118b1 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -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 { @@ -84,6 +85,7 @@ export const App: FC< + Hi.Events diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index eae8e0b763..e5f489c567 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -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"; @@ -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; diff --git a/frontend/src/api/public-client.ts b/frontend/src/api/public-client.ts index 9b31efda24..05c44fed04 100644 --- a/frontend/src/api/public-client.ts +++ b/frontend/src/api/public-client.ts @@ -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') diff --git a/frontend/src/components/common/ErrorDisplay/ErrorDisplay.module.scss b/frontend/src/components/common/ErrorDisplay/ErrorDisplay.module.scss index 164f28fa2d..de3a456478 100644 --- a/frontend/src/components/common/ErrorDisplay/ErrorDisplay.module.scss +++ b/frontend/src/components/common/ErrorDisplay/ErrorDisplay.module.scss @@ -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); } -} \ No newline at end of file +} diff --git a/frontend/src/components/common/ErrorDisplay/index.tsx b/frontend/src/components/common/ErrorDisplay/index.tsx index e26d41fbaf..d9022353a3 100644 --- a/frontend/src/components/common/ErrorDisplay/index.tsx +++ b/frontend/src/components/common/ErrorDisplay/index.tsx @@ -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` @@ -15,18 +18,59 @@ export const ErrorDisplay = () => { : t`An error occurred while loading the page`; return ( - -
- Error -
-

{title}

-
- {description} -
- -
- Go back to the home page -
-
+ <> + + + + {/* Animated background elements */} +
+
+ + + + Error + + + + {title} + + + + {description} + + + + + + + + + ); -}; \ No newline at end of file +}; + +export default ErrorDisplay; diff --git a/frontend/src/components/common/PoweredByFooter/index.tsx b/frontend/src/components/common/PoweredByFooter/index.tsx index f2e95c4bc6..a25a2b6b11 100644 --- a/frontend/src/components/common/PoweredByFooter/index.tsx +++ b/frontend/src/components/common/PoweredByFooter/index.tsx @@ -29,7 +29,7 @@ export const PoweredByFooter = (props: React.DetailedHTMLProps + title={'Effortlessly manage events and sell tickets online with Hi.Events'}> Hi.Events 🚀
diff --git a/frontend/src/components/common/ThirdPartyScripts/index.tsx b/frontend/src/components/common/ThirdPartyScripts/index.tsx new file mode 100644 index 0000000000..3fdbad56a7 --- /dev/null +++ b/frontend/src/components/common/ThirdPartyScripts/index.tsx @@ -0,0 +1,9 @@ +import ZohoChatWidget from "../ZohoChatWidget"; + +export const ThirdPartyScripts = () => { + return ( + <> + + + ); +} diff --git a/frontend/src/components/common/ZohoChatWidget/index.tsx b/frontend/src/components/common/ZohoChatWidget/index.tsx new file mode 100644 index 0000000000..8d6b8f5fff --- /dev/null +++ b/frontend/src/components/common/ZohoChatWidget/index.tsx @@ -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(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;