diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..56d2a8f --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +VITE_WHATSAPP_PHONE=971564148980 +VITE_BRAND_PRIMARY=#FA2D39 +VITE_BRAND_DARK=#101840 +VITE_BRAND_NAVY=#0a004c diff --git a/.gitignore b/.gitignore index a547bf3..5b08070 100644 --- a/.gitignore +++ b/.gitignore @@ -1,24 +1,4 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr +node_modules/ +dist/ +.env *.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/package-lock.json b/package-lock.json index 2ba54a9..c0d64d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,14 +8,13 @@ "name": "react-app", "version": "0.0.0", "dependencies": { + "@fortawesome/fontawesome-free": "^7.3.0", "react": "^19.2.6", "react-dom": "^19.2.6", "react-router-dom": "^7.17.0" }, "devDependencies": { "@eslint/js": "^10.0.1", - "@types/react": "^19.2.14", - "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.1", "eslint": "^10.3.0", "eslint-plugin-react-hooks": "^7.1.1", @@ -426,6 +425,15 @@ "node": "^20.19.0 || ^22.13.0 || >=24" } }, + "node_modules/@fortawesome/fontawesome-free": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-7.3.0.tgz", + "integrity": "sha512-Yw4Qa43P6e4Xwh0TwEUkHQNRJInWaqIBo73VJmns3j2AIlZPAvUcR4yGIxCPqPRCgyJ5KIVIalF/I1GxhZ/Kgw==", + "license": "(CC-BY-4.0 AND OFL-1.1 AND MIT)", + "engines": { + "node": ">=6" + } + }, "node_modules/@humanfs/core": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", @@ -885,26 +893,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/react": { - "version": "19.2.17", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", - "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", - "dev": true, - "license": "MIT", - "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.2.0" - } - }, "node_modules/@vitejs/plugin-react": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.2.tgz", @@ -1097,13 +1085,6 @@ "node": ">= 8" } }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, - "license": "MIT" - }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", diff --git a/package.json b/package.json index e14c2d8..c2d01d1 100644 --- a/package.json +++ b/package.json @@ -10,14 +10,13 @@ "preview": "vite preview" }, "dependencies": { + "@fortawesome/fontawesome-free": "^7.3.0", "react": "^19.2.6", "react-dom": "^19.2.6", "react-router-dom": "^7.17.0" }, "devDependencies": { "@eslint/js": "^10.0.1", - "@types/react": "^19.2.14", - "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.1", "eslint": "^10.3.0", "eslint-plugin-react-hooks": "^7.1.1", diff --git a/public/wp-content/themes/rakar/assets/css/fontawesome.min.css b/public/wp-content/themes/rakar/assets/css/fontawesome.min.css index 3943545..4193bd9 100644 --- a/public/wp-content/themes/rakar/assets/css/fontawesome.min.css +++ b/public/wp-content/themes/rakar/assets/css/fontawesome.min.css @@ -1,3 +1,7 @@ +@font-face{font-family:"Font Awesome 6 Brands";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2")} +@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2")} +@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2")} + /*! * Font Awesome Pro 6.0.0-beta3 by @fontawesome - https://fontawesome.com * License - https://fontawesome.com/license (Commercial License) diff --git a/public/wp-content/themes/rakar/assets/webfonts/fa-brands-400.woff2 b/public/wp-content/themes/rakar/assets/webfonts/fa-brands-400.woff2 new file mode 100644 index 0000000..4aa6dea Binary files /dev/null and b/public/wp-content/themes/rakar/assets/webfonts/fa-brands-400.woff2 differ diff --git a/public/wp-content/themes/rakar/assets/webfonts/fa-regular-400.woff2 b/public/wp-content/themes/rakar/assets/webfonts/fa-regular-400.woff2 new file mode 100644 index 0000000..f6590ce Binary files /dev/null and b/public/wp-content/themes/rakar/assets/webfonts/fa-regular-400.woff2 differ diff --git a/public/wp-content/themes/rakar/assets/webfonts/fa-solid-900.woff2 b/public/wp-content/themes/rakar/assets/webfonts/fa-solid-900.woff2 new file mode 100644 index 0000000..d7e38a2 Binary files /dev/null and b/public/wp-content/themes/rakar/assets/webfonts/fa-solid-900.woff2 differ diff --git a/src/App.jsx b/src/App.jsx index ce0abb7..01fe868 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,7 +1,9 @@ -import { BrowserRouter, Routes, Route } from 'react-router-dom' +import { BrowserRouter, Routes, Route, Link } from 'react-router-dom' import { lazy, Suspense } from 'react' -import { Link } from 'react-router-dom' import Layout from './components/Layout' +import Loading from './components/Loading' +import ErrorBoundary from './components/ErrorBoundary' +import { BRAND_PRIMARY } from './config' const Home = lazy(() => import('./pages/Home')) const About = lazy(() => import('./pages/About')) @@ -15,7 +17,7 @@ function NotFound() {

404 — Page Not Found

The page you're looking for doesn't exist.

- ← Back to Home + ← Back to Home
) } @@ -23,19 +25,21 @@ function NotFound() { export default function App() { return ( - - - }> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - - + + }> + + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + + ) } diff --git a/src/components/ErrorBoundary.jsx b/src/components/ErrorBoundary.jsx new file mode 100644 index 0000000..8f79361 --- /dev/null +++ b/src/components/ErrorBoundary.jsx @@ -0,0 +1,39 @@ +import { Component } from 'react' + +export default class ErrorBoundary extends Component { + constructor(props) { + super(props) + this.state = { hasError: false, error: null } + } + + static getDerivedStateFromError(error) { + return { hasError: true, error } + } + + componentDidCatch(error, info) { + console.error('ErrorBoundary caught:', error, info) + } + + render() { + if (this.state.hasError) { + return ( +
+

Something went wrong

+

+ {this.state.error?.message || 'An unexpected error occurred'} +

+ +
+ ) + } + return this.props.children + } +} diff --git a/src/components/Footer.jsx b/src/components/Footer.jsx deleted file mode 100644 index 9b01991..0000000 --- a/src/components/Footer.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import { useRef } from 'react' -import footerHtml from '../data/layout-footer.html?raw' -import { useNavFix } from '../hooks/useNavFix' - -export default function Footer() { - const ref = useRef(null) - useNavFix(ref) - return
-} diff --git a/src/components/Header.jsx b/src/components/Header.jsx deleted file mode 100644 index 0fe2924..0000000 --- a/src/components/Header.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import { useRef } from 'react' -import headerHtml from '../data/layout-header.html?raw' -import { useNavFix } from '../hooks/useNavFix' - -export default function Header() { - const ref = useRef(null) - useNavFix(ref) - return
-} diff --git a/src/components/Layout.jsx b/src/components/Layout.jsx index 0e7215c..b8fbd83 100644 --- a/src/components/Layout.jsx +++ b/src/components/Layout.jsx @@ -1,10 +1,12 @@ import { useEffect } from 'react' import { Outlet, useLocation } from 'react-router-dom' -import Header from './Header' -import Footer from './Footer' +import PageShell from './PageShell' +import headerHtml from '../data/layout-header.html?raw' +import footerHtml from '../data/layout-footer.html?raw' import WhatsAppButton from './WhatsAppButton' import useThemeAssets from '../hooks/useThemeAssets' import useLayoutInit from '../hooks/useLayoutInit' +import { PAGE_IDS } from '../config' export default function Layout() { const { pathname } = useLocation() @@ -18,11 +20,11 @@ export default function Layout() { if (removed) return removed = true if (window.__removePreloader) { - setTimeout(window.__removePreloader, 800) + window.__removePreloader() } } const tryRemove = () => { - if (window.__removePreloader && window.__cssReady && window.__layoutReady && document.readyState === 'complete') { + if (window.__removePreloader && window.__cssReady && window.__layoutReady) { removePreloader() return true } @@ -33,7 +35,7 @@ export default function Layout() { document.addEventListener('css-ready', onReady) window.addEventListener('layout-ready', onReady) window.addEventListener('load', onReady) - const fallback = setTimeout(removePreloader, 15000) + const fallback = setTimeout(removePreloader, 5000) return () => { document.removeEventListener('css-ready', onReady) window.removeEventListener('layout-ready', onReady) @@ -45,11 +47,8 @@ export default function Layout() { useEffect(() => { window.scrollTo(0, 0) - const pageIds = { - '/': 23, '/about': 31, '/service': 32, '/project': 36, '/contact': 48, - } const path = pathname.replace(/\/$/, '') || '/' - const pageId = pageIds[path] || 23 + const pageId = PAGE_IDS[path] || 23 const isHome = path === '/' if (window.elementorFrontendConfig) { @@ -66,36 +65,12 @@ export default function Layout() { ].filter(Boolean).join(' ') }, [pathname]) - useEffect(() => { - const style = document.createElement('style') - style.id = 'rakar_opt-dynamic-css' - style.textContent = ` - :root{--icon-font:"Font Awesome 6 Free";--fa-style-family:"Font Awesome 6 Free";} - .header-logo .logo img{width:260px;} - .th-menu-wrapper .mobile-logo img{width:180px;} - .th-menu-wrapper .mobile-logo{background-color:#0a004c;} - .breadcumb-wrapper{background-repeat:no-repeat;background-position:center center;background-image:url('/wp-content/uploads/2024/07/breadcumb-bg.jpg');background-size:cover;} - .hero-inner{position:relative;} - .hero-inner::before{content:'';position:absolute;inset:0;background:linear-gradient(90deg,rgba(0,0,0,0.7) 0%,rgba(0,0,0,0.3) 100%);z-index:1;pointer-events:none;} - .hero-inner .container{position:relative;z-index:2;} - .hero-style5 .sub-title2.sub{color:#fff !important;} - .hero-style5 .hero-title,.hero-style5 .hero-text{color:#fff !important;} - .slider-area .slider-arrow{display:inline-flex;align-items:center;justify-content:center;width:50px;height:50px;border-radius:50%;background:rgba(255,255,255,0.2);color:#fff;border:none;cursor:pointer;transition:0.3s;font-size:18px;} - .slider-area .slider-arrow:hover{background:#FA2D39;} - .slider-area .slider-controller{display:flex;align-items:center;justify-content:center;gap:10px;margin-top:40px;} - .slider-area .slider-pagination{display:flex;align-items:center;gap:6px;} - .slider-area .slider-pagination button{width:12px;height:12px;border-radius:50%;border:none;background:#999;opacity:0.4;cursor:pointer;padding:0;transition:0.3s;} - .slider-area .slider-pagination button.active{opacity:1;background:#FA2D39;} - ` - document.head.appendChild(style) - return () => style.remove() - }, []) return ( <> -
+ -