ui done
35
package-lock.json
generated
@ -90,7 +90,6 @@
|
||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.5",
|
||||
@ -451,7 +450,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
|
||||
"integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
@ -495,7 +493,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz",
|
||||
"integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
@ -1380,7 +1377,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.5.tgz",
|
||||
"integrity": "sha512-8VVxFmp1GIm9PpmnQoCoYo0UWHoOrdA57tDL62vkpzEgvb/d71Wsbv4FRg7r1Gyx7PuSo0tflH34cdl/NvfHNQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.4",
|
||||
"@mui/core-downloads-tracker": "^7.3.5",
|
||||
@ -1514,7 +1510,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-7.3.5.tgz",
|
||||
"integrity": "sha512-yPaf5+gY3v80HNkJcPi6WT+r9ebeM4eJzrREXPxMt7pNTV/1eahyODO4fbH3Qvd8irNxDFYn5RQ3idHW55rA6g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.4",
|
||||
"@mui/private-theming": "^7.3.5",
|
||||
@ -2381,8 +2376,7 @@
|
||||
}
|
||||
],
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@tsparticles/interaction-external-attract": {
|
||||
"version": "3.9.1",
|
||||
@ -3000,7 +2994,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.4.tgz",
|
||||
"integrity": "sha512-tBFxBp9Nfyy5rsmefN+WXc1JeW/j2BpBHFdLZbEVfs9wn3E3NRFxwV0pJg8M1qQAexFpvz73hJXFofV0ZAu92A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
@ -3050,7 +3043,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.181.0.tgz",
|
||||
"integrity": "sha512-MLF1ks8yRM2k71D7RprFpDb9DOX0p22DbdPqT/uAkc6AtQXjxWCVDjCy23G9t1o8HcQPk7woD2NIyiaWcWPYmA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@dimforge/rapier3d-compat": "~0.12.0",
|
||||
"@tweenjs/tween.js": "~23.1.3",
|
||||
@ -3323,7 +3315,6 @@
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@ -3600,7 +3591,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.8.25",
|
||||
"caniuse-lite": "^1.0.30001754",
|
||||
@ -4020,8 +4010,7 @@
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/d3-array": {
|
||||
"version": "3.2.4",
|
||||
@ -4149,7 +4138,6 @@
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
|
||||
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/kossnocorp"
|
||||
@ -4264,8 +4252,7 @@
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz",
|
||||
"integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/embla-carousel-react": {
|
||||
"version": "8.6.0",
|
||||
@ -4442,7 +4429,6 @@
|
||||
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
@ -6074,7 +6060,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-8.18.0.tgz",
|
||||
"integrity": "sha512-FYZZqD0UUHUswKz3LQl2Z7H24AhD14XGTsIRw3SJaXUxyfVMi+1yiZGmqTcPt/CkPpdU7rrxqcyQ1zJE5DjvIQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.17.8",
|
||||
"@types/react-reconciler": "^0.26.7",
|
||||
@ -6157,7 +6142,6 @@
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
|
||||
"integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/kossnocorp"
|
||||
@ -6753,7 +6737,6 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@ -6862,7 +6845,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.11",
|
||||
"picocolors": "^1.1.1",
|
||||
@ -7115,7 +7097,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
|
||||
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@ -7125,7 +7106,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
|
||||
"integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.27.0"
|
||||
},
|
||||
@ -7223,7 +7203,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
||||
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/use-sync-external-store": "^0.0.6",
|
||||
"use-sync-external-store": "^1.4.0"
|
||||
@ -7450,8 +7429,7 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
|
||||
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/redux-thunk": {
|
||||
"version": "3.1.0",
|
||||
@ -7681,7 +7659,6 @@
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
@ -7881,7 +7858,6 @@
|
||||
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.19.tgz",
|
||||
"integrity": "sha512-1v/e3Dl1BknC37cXMhwGomhO8AkYmN41CqyX9xhUDxry1ns3BFQy2lLDRQXJRdVVWB9OHemv/53xaStimvWyuA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@emotion/is-prop-valid": "1.2.2",
|
||||
"@emotion/unitless": "0.8.1",
|
||||
@ -8424,7 +8400,6 @@
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz",
|
||||
"integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.5.0",
|
||||
@ -8577,7 +8552,6 @@
|
||||
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz",
|
||||
"integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@discoveryjs/json-ext": "^0.5.0",
|
||||
"@webpack-cli/configtest": "^1.2.0",
|
||||
@ -8740,7 +8714,6 @@
|
||||
"integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
|
||||
BIN
src/assets/images/god1.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
src/assets/images/greenbg2.jpg
Normal file
|
After Width: | Height: | Size: 4.6 MiB |
BIN
src/assets/images/greenbg3.jpg
Normal file
|
After Width: | Height: | Size: 253 KiB |
BIN
src/assets/images/greenbg4.jpg
Normal file
|
After Width: | Height: | Size: 4.2 MiB |
BIN
src/assets/images/perumal.png
Normal file
|
After Width: | Height: | Size: 2.7 MiB |
BIN
src/assets/images/sliderbg1.jpg
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
src/assets/images/temple.jpg
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
src/assets/images/temple.png
Normal file
|
After Width: | Height: | Size: 370 KiB |
@ -174,8 +174,8 @@ const LoginPanel = () => {
|
||||
|
||||
const navigate = useNavigate();
|
||||
const [formData, setFormData] = useState({
|
||||
emailOrMobile: '',
|
||||
password: '',
|
||||
emailOrMobile: 'admin@gmail.com',
|
||||
password: 'Password@123',
|
||||
keepLoggedIn: false,
|
||||
})
|
||||
|
||||
@ -222,7 +222,11 @@ const LoginPanel = () => {
|
||||
setLoading(false)
|
||||
setSuccessModal(true) // Show success modal
|
||||
console.log('Form Data:', formData)
|
||||
localStorage.setItem("token", "dummy-token");
|
||||
navigate("/dashboard-home");
|
||||
}, 1500)
|
||||
|
||||
|
||||
}
|
||||
|
||||
const handleCloseSuccessModal = () => {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { Facebook, Instagram, Linkedin, Twitter, Youtube } from 'lucide-react';
|
||||
import { Facebook, Instagram, Linkedin, Twitter, Youtube } from "lucide-react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
@ -7,15 +8,66 @@ export default function Footer() {
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-8 mb-8">
|
||||
{/* Need Help Section */}
|
||||
<div>
|
||||
<h3 className="text-[#A70710] font-bold text-lg mb-4">Need Help?</h3>
|
||||
<h3 className="text-[#A70710] font-bold text-lg mb-4">
|
||||
Need Help?
|
||||
</h3>
|
||||
<ul className="space-y-2">
|
||||
<li><a href="#" className="text-gray-600 hover:text-green-600 transition-colors">Member Login</a></li>
|
||||
<li><a href="#" className="text-gray-600 hover:text-green-600 transition-colors">Sign Up</a></li>
|
||||
<li><a href="#" className="text-gray-600 hover:text-green-600 transition-colors">Partner Search</a></li>
|
||||
<li><a href="#" className="text-gray-600 hover:text-green-600 transition-colors">How to Use Thirukalyanam Matrimony App</a></li>
|
||||
<li><a href="#" className="text-gray-600 hover:text-green-600 transition-colors">Premium Memberships</a></li>
|
||||
<li><a href="#" className="text-gray-600 hover:text-green-600 transition-colors">Customer Support</a></li>
|
||||
<li><a href="#" className="text-gray-600 hover:text-green-600 transition-colors">Site Map</a></li>
|
||||
<li>
|
||||
<Link to="/login"
|
||||
|
||||
className="text-gray-600 hover:text-green-600 transition-colors"
|
||||
>
|
||||
Member Login
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
to="/registration"
|
||||
className="text-gray-600 hover:text-green-600 transition-colors"
|
||||
>
|
||||
Sign Up
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="/matches"
|
||||
className="text-gray-600 hover:text-green-600 transition-colors"
|
||||
>
|
||||
Partner Search
|
||||
</a>
|
||||
</li>
|
||||
{/* <li>
|
||||
<a
|
||||
href="#"
|
||||
className="text-gray-600 hover:text-green-600 transition-colors"
|
||||
>
|
||||
How to Use Thirukalyanam Matrimony App
|
||||
</a>
|
||||
</li> */}
|
||||
{/* <li>
|
||||
<a
|
||||
href="#"
|
||||
className="text-gray-600 hover:text-green-600 transition-colors"
|
||||
>
|
||||
Premium Memberships
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
className="text-gray-600 hover:text-green-600 transition-colors"
|
||||
>
|
||||
Customer Support
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
className="text-gray-600 hover:text-green-600 transition-colors"
|
||||
>
|
||||
Site Map
|
||||
</a>
|
||||
</li> */}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@ -23,22 +75,87 @@ export default function Footer() {
|
||||
<div>
|
||||
<h3 className="text-[#A70710] font-bold text-lg mb-4">Company</h3>
|
||||
<ul className="space-y-2">
|
||||
<li><a href="#" className="text-gray-600 hover:text-green-600 transition-colors">About Us</a></li>
|
||||
<li><a href="#" className="text-gray-600 hover:text-green-600 transition-colors">Thirukalyanam Blog</a></li>
|
||||
<li><a href="#" className="text-gray-600 hover:text-green-600 transition-colors">Careers</a></li>
|
||||
<li><a href="#" className="text-gray-600 hover:text-green-600 transition-colors">Awards & Recognition</a></li>
|
||||
<li><a href="#" className="text-gray-600 hover:text-green-600 transition-colors">Contact Us</a></li>
|
||||
{/* <li>
|
||||
<a
|
||||
href="#"
|
||||
className="text-gray-600 hover:text-green-600 transition-colors"
|
||||
>
|
||||
About Us
|
||||
</a>
|
||||
</li> */}
|
||||
{/* <li>
|
||||
<a
|
||||
href="#"
|
||||
className="text-gray-600 hover:text-green-600 transition-colors"
|
||||
>
|
||||
Thirukalyanam Blog
|
||||
</a>
|
||||
</li> */}
|
||||
{/* <li>
|
||||
<a
|
||||
href="#"
|
||||
className="text-gray-600 hover:text-green-600 transition-colors"
|
||||
>
|
||||
Careers
|
||||
</a>
|
||||
</li> */}
|
||||
{/* <li>
|
||||
<a
|
||||
href="#"
|
||||
className="text-gray-600 hover:text-green-600 transition-colors"
|
||||
>
|
||||
Awards & Recognition
|
||||
</a>
|
||||
</li> */}
|
||||
<li>
|
||||
<Link
|
||||
to="/contact"
|
||||
className="text-gray-600 hover:text-green-600 transition-colors"
|
||||
>
|
||||
Contact Us
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Privacy & You Section */}
|
||||
<div>
|
||||
<h3 className="text-[#A70710] font-bold text-lg mb-4">Privacy & You</h3>
|
||||
<h3 className="text-[#A70710] font-bold text-lg mb-4">
|
||||
Privacy & You
|
||||
</h3>
|
||||
<ul className="space-y-2">
|
||||
<li><a href="#" className="text-gray-600 hover:text-green-600 transition-colors">Terms of Use</a></li>
|
||||
<li><a href="#" className="text-gray-600 hover:text-green-600 transition-colors">Privacy Policy</a></li>
|
||||
<li><a href="#" className="text-gray-600 hover:text-green-600 transition-colors">Be Safe Online</a></li>
|
||||
<li><a href="#" className="text-gray-600 hover:text-green-600 transition-colors">Report Misuse</a></li>
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
className="text-gray-600 hover:text-green-600 transition-colors"
|
||||
>
|
||||
Terms of Use
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
className="text-gray-600 hover:text-green-600 transition-colors"
|
||||
>
|
||||
Privacy Policy
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
className="text-gray-600 hover:text-green-600 transition-colors"
|
||||
>
|
||||
Be Safe Online
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
className="text-gray-600 hover:text-green-600 transition-colors"
|
||||
>
|
||||
Report Misuse
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@ -46,36 +163,66 @@ export default function Footer() {
|
||||
<div>
|
||||
<h3 className="text-[#A70710] font-bold text-lg mb-4">More</h3>
|
||||
<ul className="space-y-2">
|
||||
<li><a href="#" className="text-gray-600 hover:text-green-600 transition-colors">VIP Thirukalyanam</a></li>
|
||||
<li><a href="#" className="text-gray-600 hover:text-green-600 transition-colors">Thirukalyanam Centres</a></li>
|
||||
<li><a href="#" className="text-gray-600 hover:text-green-600 transition-colors">Success Stories</a></li>
|
||||
<li><a href="#" className="text-gray-600 hover:text-green-600 transition-colors">Thirukalyanam Live</a></li>
|
||||
<li><a href="#" className="text-gray-600 hover:text-green-600 transition-colors">Chart Generate</a></li>
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
className="text-gray-600 hover:text-green-600 transition-colors"
|
||||
>
|
||||
Be Safe Online
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
className="text-gray-600 hover:text-green-600 transition-colors"
|
||||
>
|
||||
Report Misuse
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Social Media & Apps Section */}
|
||||
<div>
|
||||
<h3 className="text-[#A70710] font-bold text-lg mb-4">Find us on:</h3>
|
||||
<h3 className="text-[#A70710] font-bold text-lg mb-4">
|
||||
Find us on:
|
||||
</h3>
|
||||
<div className="flex gap-3 mb-6">
|
||||
<a href="#" className="bg-white border border-gray-300 p-2 rounded hover:bg-green-50 hover:border-green-500 transition-all">
|
||||
<a
|
||||
href="#"
|
||||
className="bg-white border border-gray-300 p-2 rounded hover:bg-green-50 hover:border-green-500 transition-all"
|
||||
>
|
||||
<Facebook className="w-5 h-5 text-gray-700" />
|
||||
</a>
|
||||
<a href="#" className="bg-white border border-gray-300 p-2 rounded hover:bg-green-50 hover:border-green-500 transition-all">
|
||||
<a
|
||||
href="#"
|
||||
className="bg-white border border-gray-300 p-2 rounded hover:bg-green-50 hover:border-green-500 transition-all"
|
||||
>
|
||||
<Instagram className="w-5 h-5 text-gray-700" />
|
||||
</a>
|
||||
<a href="#" className="bg-white border border-gray-300 p-2 rounded hover:bg-green-50 hover:border-green-500 transition-all">
|
||||
<a
|
||||
href="#"
|
||||
className="bg-white border border-gray-300 p-2 rounded hover:bg-green-50 hover:border-green-500 transition-all"
|
||||
>
|
||||
<Linkedin className="w-5 h-5 text-gray-700" />
|
||||
</a>
|
||||
<a href="#" className="bg-white border border-gray-300 p-2 rounded hover:bg-green-50 hover:border-green-500 transition-all">
|
||||
<a
|
||||
href="#"
|
||||
className="bg-white border border-gray-300 p-2 rounded hover:bg-green-50 hover:border-green-500 transition-all"
|
||||
>
|
||||
<Twitter className="w-5 h-5 text-gray-700" />
|
||||
</a>
|
||||
<a href="#" className="bg-white border border-gray-300 p-2 rounded hover:bg-green-50 hover:border-green-500 transition-all">
|
||||
<a
|
||||
href="#"
|
||||
className="bg-white border border-gray-300 p-2 rounded hover:bg-green-50 hover:border-green-500 transition-all"
|
||||
>
|
||||
<Youtube className="w-5 h-5 text-gray-700" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h3 className="text-[#A70710] font-bold text-lg mb-4">Get the Thirukalyanam App</h3>
|
||||
<h3 className="text-[#A70710] font-bold text-lg mb-4">
|
||||
Get the Thirukalyanam App
|
||||
</h3>
|
||||
<div className="space-y-3">
|
||||
<a href="#" className="block">
|
||||
<img
|
||||
@ -105,11 +252,13 @@ export default function Footer() {
|
||||
<div className="border-t border-gray-200 pt-6">
|
||||
<div className="flex flex-col md:flex-row justify-between items-center">
|
||||
<p className="text-sm text-gray-600 mb-4 md:mb-0">
|
||||
© 1991-{new Date().getFullYear()} Thirukalyanam, The World's Leading Matchmaking Service™
|
||||
</p>
|
||||
© {new Date().getFullYear()} Thirukalyanam, The World's
|
||||
Leading Matchmaking Service™
|
||||
</p>
|
||||
|
||||
<p className="text-sm text-gray-600">
|
||||
Passionately created by <span className="text-[#034E08] font-semibold">Amrithaa</span>
|
||||
Passionately created by{" "}
|
||||
<span className="text-[#034E08] font-semibold">Amrithaa</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -18,14 +18,16 @@ import Modal from "@mui/material/Modal";
|
||||
import Button from "@mui/material/Button";
|
||||
import LazyImage from "./LazyImage";
|
||||
import Logo from "../../assets/images/logo.png";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import { useTheme, useMediaQuery, ListItemIcon } from "@mui/material";
|
||||
import { Home, Users, Heart, MessageCircle, Search, Bell } from "lucide-react";
|
||||
|
||||
import { isAuthenticated } from "../../utills/auth";
|
||||
import userimg from "../../assets/images/bride1.jpg"
|
||||
const NAV_LINKS = [
|
||||
{ label: "Home", path: "/" },
|
||||
// { label: "Home", path: "/" },
|
||||
{ label: "Matches", path: "/matches" },
|
||||
// { label: "ProfileCard", path: "/profile-card" },
|
||||
{ label: "Interest", path: "/interest" },
|
||||
{ label: "Horoscope", path: "/horoscoper-generate" },
|
||||
{ label: "Messages", path: "/chat" },
|
||||
@ -53,17 +55,13 @@ const ACCOUNT_SETTINGS = [
|
||||
{ label: "Logout", action: "logout" }
|
||||
];
|
||||
|
||||
const SparkleNavbar = ({ items, color = "#0fec1eff", onItemClick }) => {
|
||||
const SparkleNavbar = ({ items, color = "#0fec1eff", onItemClick, activeItem }) => {
|
||||
const [activeIndex, setActiveIndex] = useState(0);
|
||||
const [indicatorStyle, setIndicatorStyle] = useState({});
|
||||
const [isAnimating, setIsAnimating] = useState(false);
|
||||
const navRef = useRef(null);
|
||||
const buttonRefs = useRef([]);
|
||||
|
||||
useEffect(() => {
|
||||
updateIndicator(activeIndex);
|
||||
}, []);
|
||||
|
||||
const updateIndicator = (index) => {
|
||||
const button = buttonRefs.current[index];
|
||||
if (button && navRef.current) {
|
||||
@ -76,6 +74,23 @@ const SparkleNavbar = ({ items, color = "#0fec1eff", onItemClick }) => {
|
||||
}
|
||||
};
|
||||
|
||||
// sync with activeItem from router
|
||||
useEffect(() => {
|
||||
if (!activeItem) return;
|
||||
const newIndex = items.indexOf(activeItem);
|
||||
if (newIndex !== -1 && newIndex !== activeIndex) {
|
||||
setActiveIndex(newIndex);
|
||||
updateIndicator(newIndex);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [activeItem, items]);
|
||||
|
||||
// initial indicator
|
||||
useEffect(() => {
|
||||
updateIndicator(activeIndex);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const handleClick = (index) => {
|
||||
if (index === activeIndex || isAnimating) return;
|
||||
setIsAnimating(true);
|
||||
@ -85,9 +100,9 @@ const SparkleNavbar = ({ items, color = "#0fec1eff", onItemClick }) => {
|
||||
const navRect = navRef.current.getBoundingClientRect();
|
||||
const newRect = newButton.getBoundingClientRect();
|
||||
|
||||
setIndicatorStyle(prev => ({
|
||||
setIndicatorStyle((prev) => ({
|
||||
...prev,
|
||||
left: newRect.left - navRect.left + newRect.width / 2 - 18,
|
||||
left: newRect.left - navRect.left + newRect.width / 2 - 18
|
||||
}));
|
||||
|
||||
setTimeout(() => {
|
||||
@ -103,16 +118,11 @@ const SparkleNavbar = ({ items, color = "#0fec1eff", onItemClick }) => {
|
||||
{items.map((item, index) => (
|
||||
<button
|
||||
key={item}
|
||||
ref={el => buttonRefs.current[index] = el}
|
||||
ref={(el) => (buttonRefs.current[index] = el)}
|
||||
onClick={() => handleClick(index)}
|
||||
className={`cursor-pointer relative uppercase text-sm font-medium transition-all duration-300 pb-2 ${
|
||||
activeIndex === index ? 'text-black' : 'text-gray-900 hover:text-gray-600'
|
||||
activeIndex === index ? "text-black" : "text-gray-900 hover:text-gray-600"
|
||||
}`}
|
||||
style={{
|
||||
// textShadow: activeIndex === index
|
||||
// ? `0 0 10px ${color}, 0 0 20px ${color}, 0 0 30px ${color}`
|
||||
// : 'none'
|
||||
}}
|
||||
>
|
||||
{item}
|
||||
</button>
|
||||
@ -124,7 +134,7 @@ const SparkleNavbar = ({ items, color = "#0fec1eff", onItemClick }) => {
|
||||
left: indicatorStyle.left,
|
||||
opacity: indicatorStyle.opacity,
|
||||
backgroundColor: color,
|
||||
boxShadow: `0 0 10px ${color}, 0 0 20px ${color}, 0 0 30px ${color}, 0 0 40px ${color}`,
|
||||
boxShadow: `0 0 10px ${color}, 0 0 20px ${color}, 0 0 30px ${color}, 0 0 40px ${color}`
|
||||
}}
|
||||
/>
|
||||
</nav>
|
||||
@ -132,7 +142,9 @@ const SparkleNavbar = ({ items, color = "#0fec1eff", onItemClick }) => {
|
||||
};
|
||||
|
||||
const ProfileHeader = () => {
|
||||
const auth = isAuthenticated();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false);
|
||||
const [profileDrawerOpen, setProfileDrawerOpen] = useState(false);
|
||||
const [deleteModalOpen, setDeleteModalOpen] = useState(false);
|
||||
@ -145,6 +157,14 @@ const ProfileHeader = () => {
|
||||
const toggleProfileDrawer = (open) => () => setProfileDrawerOpen(open);
|
||||
|
||||
const [selectedItem, setSelectedItem] = useState("Home");
|
||||
const currentNav = NAV_LINKS.find(link =>
|
||||
location.pathname.startsWith(link.path)
|
||||
);
|
||||
|
||||
const currentLabel = currentNav?.label ?? NAV_LINKS[0].label;
|
||||
|
||||
|
||||
|
||||
|
||||
const handleMenuClick = (item) => {
|
||||
if (item.action === "delete") {
|
||||
@ -273,11 +293,11 @@ const ProfileHeader = () => {
|
||||
<AppBar position="sticky" sx={{ backgroundColor: "#fff" }}>
|
||||
<Container maxWidth="xl">
|
||||
<Toolbar disableGutters>
|
||||
<Box sx={{ display: { xs: "none", md: "flex" }, mr: 1 }}>
|
||||
<Box onClick={() => navigate("/")} sx={{ display: { xs: "none", md: "flex" }, mr: 1 }}>
|
||||
<LazyImage
|
||||
src={Logo}
|
||||
className="w-full h-[70px] my-2 rounded-lg object-cover cursor-pointer"
|
||||
onClick={() => navigate("/")}
|
||||
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@ -294,6 +314,7 @@ const ProfileHeader = () => {
|
||||
<LazyImage
|
||||
src={Logo}
|
||||
className="w-full h-[50px] rounded-lg object-cover"
|
||||
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@ -301,6 +322,7 @@ const ProfileHeader = () => {
|
||||
<SparkleNavbar
|
||||
items={NAV_LINKS.map(link => link.label)}
|
||||
color="#034E08"
|
||||
activeItem={currentLabel}
|
||||
onItemClick={(item) => {
|
||||
setSelectedItem(item);
|
||||
const link = NAV_LINKS.find(l => l.label === item);
|
||||
@ -309,13 +331,21 @@ const ProfileHeader = () => {
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ flexGrow: 0 }}>
|
||||
{(auth ? (
|
||||
<Box sx={{ flexGrow: 0 }}>
|
||||
<Tooltip title="Account Menu">
|
||||
<IconButton onClick={toggleProfileDrawer(true)}>
|
||||
<Avatar src="/static/images/avatar/2.jpg" />
|
||||
<Avatar sx={{width:"50px", height:"50px"}} src={userimg || "/static/images/avatar/2.jpg" }/>
|
||||
|
||||
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
):( <button className="ml-1 bg-red-900 text-white px-4 py-2 rounded-md hover:bg-red-800 transition-colors"
|
||||
onClick={() => navigate("/login")}>Sign In / Sign Up</button>))}
|
||||
|
||||
|
||||
|
||||
</Toolbar>
|
||||
</Container>
|
||||
</AppBar>
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import girlchat from "../../assets/images/girlchat.webp"
|
||||
const AstroChatUI = () => {
|
||||
const navigate = useNavigate();
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -26,7 +28,7 @@ const AstroChatUI = () => {
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold">
|
||||
<span className="text-gray-900">Astro</span>
|
||||
<span className="text-[#A70710]">FreeChart</span>
|
||||
<span className="text-[#A70710]">Horoscope</span>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
@ -70,8 +72,10 @@ const AstroChatUI = () => {
|
||||
</div>
|
||||
|
||||
{/* CTA Button */}
|
||||
<button className="relative z-[99] w-[fit-content] bg-[#034E08] hover:bg-[#A70710] text-white font-bold text-[16px] py-4 px-6 rounded-full shadow-lg hover:shadow-xl transition-all duration-300 transform hover:scale-105">
|
||||
Download AstroFreeChart
|
||||
<button onClick={()=>{
|
||||
navigate("/horoscoper-generate")
|
||||
}} className="relative z-[99] w-[fit-content] bg-[#034E08] hover:bg-[#A70710] text-white font-bold text-[16px] py-4 px-6 rounded-full shadow-lg hover:shadow-xl transition-all duration-300 transform hover:scale-105">
|
||||
Upload Horoscope
|
||||
</button>
|
||||
|
||||
{/* Girl Image - Positioned at bottom right */}
|
||||
|
||||
@ -6,27 +6,34 @@ import { motion } from 'framer-motion';
|
||||
import { Crown, Bookmark, User, Briefcase, MapPin, X, Send, ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
import { useRef } from "react";
|
||||
import LazyImage from "../common/LazyImage";
|
||||
import weddingImg1 from "../../assets/images/wedding6.jpeg";
|
||||
import weddingImg2 from "../../assets/images/wedding8.jpg";
|
||||
import weddingImg3 from "../../assets/images/wedding7.jpg";
|
||||
|
||||
|
||||
|
||||
const articleData = [
|
||||
{
|
||||
title: "Marriage is not just finding the right partner, it's creating a lifetime of moments together Find someone who understands your heart and walks with you in every season.",
|
||||
img: "https://images.unsplash.com/photo-1519744792095-2f2205e87b6f?ixlib=rb-4.0.3&auto=format&fit=crop&w=900&q=80"
|
||||
img: weddingImg1,
|
||||
|
||||
},
|
||||
{
|
||||
title: "Top 10 Qualities for a Happy Marriage, A perfect match begins with trust, respect, and shared dreams",
|
||||
img: "https://images.unsplash.com/photo-1519744792095-2f2205e87b6f?ixlib=rb-4.0.3&auto=format&fit=crop&w=900&q=80"
|
||||
img: weddingImg2
|
||||
},
|
||||
|
||||
{
|
||||
title: "Expert Tips for a Strong Relationship, A perfect match begins with trust, respect, and shared dreams",
|
||||
img: "https://images.unsplash.com/photo-1519744792095-2f2205e87b6f?ixlib=rb-4.0.3&auto=format&fit=crop&w=900&q=80"
|
||||
img: weddingImg3
|
||||
},
|
||||
{
|
||||
title: "How to Build Trust in Marriage, A perfect match begins with trust, respect, and shared dreams",
|
||||
img: "https://images.unsplash.com/photo-1519744792095-2f2205e87b6f?ixlib=rb-4.0.3&auto=format&fit=crop&w=900&q=80"
|
||||
img: weddingImg1
|
||||
},
|
||||
{
|
||||
title: "Communication Secrets for Couples, A perfect match begins with trust, respect, and shared dreams,A perfect match begins with trust, respect, and shared dreams.A perfect match begins with trust, respect, and shared dreams.A perfect match begins with trust, respect, and shared dreams,Real relationships are built on honesty, compassion, and understanding.,Real relationships are built on honesty, compassion, and understanding.",
|
||||
img: "https://images.unsplash.com/photo-1482849297070-f4fae2173efe?ixlib=rb-4.0.3&auto=format&fit=crop&w=900&q=80"
|
||||
img: weddingImg1
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import membershipgirl from "../../assets/images/membership.avif";
|
||||
|
||||
const PaidMemberCard = () => {
|
||||
const navigate = useNavigate();
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -41,7 +43,9 @@ const PaidMemberCard = () => {
|
||||
</ul>
|
||||
|
||||
{/* Button */}
|
||||
<button className="mt-6 bg-[#034E08] hover:bg-[#A70710] text-white font-semibold py-3 px-6 rounded-full text-lg transition-all">
|
||||
<button onClick={()=>{
|
||||
navigate("/subscription-plan")
|
||||
}} className="mt-6 bg-[#034E08] hover:bg-[#A70710] text-white font-semibold py-3 px-6 rounded-full text-lg transition-all">
|
||||
See membership plans
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -21,6 +21,7 @@ const ProfileCompletion = () => {
|
||||
bgColor: "bg-green-50",
|
||||
iconColor: "text-green-600",
|
||||
borderColor: "border-green-200",
|
||||
url:"/profile-edit"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
@ -29,6 +30,7 @@ const ProfileCompletion = () => {
|
||||
bgColor: "bg-purple-50",
|
||||
iconColor: "text-purple-600",
|
||||
borderColor: "border-purple-200",
|
||||
url:"/horoscoper-generate"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
@ -37,6 +39,7 @@ const ProfileCompletion = () => {
|
||||
bgColor: "bg-red-50",
|
||||
iconColor: "text-red-600",
|
||||
borderColor: "border-red-200",
|
||||
url:"/profile-edit"
|
||||
},
|
||||
];
|
||||
|
||||
@ -130,7 +133,10 @@ const ProfileCompletion = () => {
|
||||
|
||||
{cards.map((card, index) => (
|
||||
|
||||
<div className=" border border-1 border-red-50 bg-white rounded-3xl hover:bg-red-50 hover:border-2
|
||||
<div
|
||||
onClick={() => navigate(card.url)}
|
||||
|
||||
className=" border border-1 border-red-50 bg-white rounded-3xl hover:bg-red-50 hover:border-2
|
||||
flex flex-col items-center space-x-2 h-32 justify-center transition-colors duration-500
|
||||
cursor-pointer ">
|
||||
{/* Icon Container */}
|
||||
|
||||
@ -3,6 +3,9 @@ import { motion } from 'framer-motion';
|
||||
import { Swiper, SwiperSlide } from 'swiper/react';
|
||||
import { Navigation, Pagination, Autoplay } from 'swiper/modules';
|
||||
import { Play, X, Heart, Share2, Eye, ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
import weddingImg1 from "../../assets/images/wedding6.jpeg";
|
||||
import weddingImg2 from "../../assets/images/wedding8.jpg";
|
||||
import weddingImg3 from "../../assets/images/wedding6.jpeg";
|
||||
|
||||
// Import Swiper styles
|
||||
import 'swiper/css';
|
||||
@ -18,7 +21,7 @@ const VideoSwiperGallery = () => {
|
||||
{
|
||||
id: 1,
|
||||
title: 'Priya & Rahul - Wedding Story',
|
||||
thumbnail: 'https://images.unsplash.com/photo-1519741497674-611481863552?w=600&h=400&fit=crop',
|
||||
thumbnail: weddingImg1,
|
||||
videoUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
|
||||
views: '2.4K',
|
||||
likes: '142',
|
||||
@ -27,7 +30,7 @@ const VideoSwiperGallery = () => {
|
||||
{
|
||||
id: 2,
|
||||
title: 'Aisha - Profile Introduction',
|
||||
thumbnail: 'https://images.unsplash.com/photo-1606216794079-e48e3e2a3f6a?w=600&h=400&fit=crop',
|
||||
thumbnail: weddingImg2,
|
||||
videoUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4',
|
||||
views: '1.8K',
|
||||
likes: '98',
|
||||
@ -36,7 +39,7 @@ const VideoSwiperGallery = () => {
|
||||
{
|
||||
id: 3,
|
||||
title: 'Rohan - Life Journey',
|
||||
thumbnail: 'https://images.unsplash.com/photo-1511285560929-80b456fea0bc?w=600&h=400&fit=crop',
|
||||
thumbnail: weddingImg3,
|
||||
videoUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4',
|
||||
views: '3.2K',
|
||||
likes: '256',
|
||||
@ -45,7 +48,7 @@ const VideoSwiperGallery = () => {
|
||||
{
|
||||
id: 4,
|
||||
title: 'Divya - Family Values',
|
||||
thumbnail: 'https://images.unsplash.com/photo-1522673607200-164d1b6ce486?w=600&h=400&fit=crop',
|
||||
thumbnail: weddingImg1,
|
||||
videoUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4',
|
||||
views: '1.5K',
|
||||
likes: '87',
|
||||
@ -54,7 +57,7 @@ const VideoSwiperGallery = () => {
|
||||
{
|
||||
id: 5,
|
||||
title: 'Karthik & Meera - First Meet',
|
||||
thumbnail: 'https://images.unsplash.com/photo-1583939003579-730e3918a45a?w=600&h=400&fit=crop',
|
||||
thumbnail: weddingImg2,
|
||||
videoUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerFun.mp4',
|
||||
views: '4.1K',
|
||||
likes: '312',
|
||||
@ -63,7 +66,7 @@ const VideoSwiperGallery = () => {
|
||||
{
|
||||
id: 6,
|
||||
title: 'Sneha - Hobbies & Interests',
|
||||
thumbnail: 'https://images.unsplash.com/photo-1465495976277-4387d4b0b4c6?w=600&h=400&fit=crop',
|
||||
thumbnail: weddingImg3,
|
||||
videoUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerJoyrides.mp4',
|
||||
views: '2.7K',
|
||||
likes: '178',
|
||||
@ -72,7 +75,7 @@ const VideoSwiperGallery = () => {
|
||||
{
|
||||
id: 7,
|
||||
title: 'Arjun - Career & Dreams',
|
||||
thumbnail: 'https://images.unsplash.com/photo-1492691527719-9d1e07e534b4?w=600&h=400&fit=crop',
|
||||
thumbnail: weddingImg1,
|
||||
videoUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerMeltdowns.mp4',
|
||||
views: '1.9K',
|
||||
likes: '134',
|
||||
@ -81,7 +84,7 @@ const VideoSwiperGallery = () => {
|
||||
{
|
||||
id: 8,
|
||||
title: 'Lakshmi - Traditional Values',
|
||||
thumbnail: 'https://images.unsplash.com/photo-1606216794074-735e91aa2c92?w=600&h=400&fit=crop',
|
||||
thumbnail: weddingImg2,
|
||||
videoUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/Sintel.mp4',
|
||||
views: '3.5K',
|
||||
likes: '267',
|
||||
@ -90,7 +93,7 @@ const VideoSwiperGallery = () => {
|
||||
{
|
||||
id: 9,
|
||||
title: 'Vikram - Adventure Life',
|
||||
thumbnail: 'https://images.unsplash.com/photo-1519167758481-83f29da8c4f3?w=600&h=400&fit=crop',
|
||||
thumbnail: weddingImg3,
|
||||
videoUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
|
||||
views: '5.2K',
|
||||
likes: '423',
|
||||
@ -99,7 +102,7 @@ const VideoSwiperGallery = () => {
|
||||
{
|
||||
id: 10,
|
||||
title: 'Anjali - Creative Journey',
|
||||
thumbnail: 'https://images.unsplash.com/photo-1529626455594-4ff0802cfb7e?w=600&h=400&fit=crop',
|
||||
thumbnail: weddingImg1,
|
||||
videoUrl: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4',
|
||||
views: '3.8K',
|
||||
likes: '289',
|
||||
@ -319,7 +322,7 @@ const VideoSwiperGallery = () => {
|
||||
</div>
|
||||
|
||||
{/* View All Button */}
|
||||
<motion.div
|
||||
{/* <motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.5 }}
|
||||
@ -332,7 +335,7 @@ const VideoSwiperGallery = () => {
|
||||
>
|
||||
View All Videos
|
||||
</motion.button>
|
||||
</motion.div>
|
||||
</motion.div> */}
|
||||
</div>
|
||||
|
||||
{/* Video Modal */}
|
||||
|
||||
66
src/components/ui/HeroSlider.jsx
Normal file
@ -0,0 +1,66 @@
|
||||
import { Swiper, SwiperSlide } from "swiper/react";
|
||||
import { Autoplay, Pagination, Navigation } from "swiper/modules";
|
||||
import god1 from "../../assets/images/god1.png";
|
||||
import god2 from "../../assets/images/perumal.png"
|
||||
|
||||
import "swiper/css";
|
||||
import "swiper/css/pagination";
|
||||
import "swiper/css/navigation";
|
||||
|
||||
import "../../styles/heroSlider.css";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
const slides = [
|
||||
{
|
||||
image: god1,
|
||||
title: "Find Your Perfect Life Partner",
|
||||
description:
|
||||
"Trusted matches, meaningful connections, lifelong happiness.",
|
||||
button: "Register Now",
|
||||
},
|
||||
{
|
||||
image: god2,
|
||||
title: "Where True Love Begins",
|
||||
description:
|
||||
"Discover compatible matches made just for you. Start your journey today.",
|
||||
button: "Register Now",
|
||||
},
|
||||
];
|
||||
|
||||
const HeroSlider = () => {
|
||||
const navigate = useNavigate();
|
||||
return (
|
||||
<Swiper
|
||||
modules={[Autoplay, Pagination, Navigation]}
|
||||
autoplay={{ delay: 6000 }}
|
||||
pagination={{ clickable: true }}
|
||||
loop
|
||||
className="hero-swiper"
|
||||
>
|
||||
{slides.map((slide, index) => (
|
||||
<SwiperSlide key={index}>
|
||||
|
||||
<div className="hero-wrapper">
|
||||
<div className="hero-slide">
|
||||
{/* Left Image */}
|
||||
<div className="hero-image">
|
||||
<img src={slide.image} alt="hero" />
|
||||
</div>
|
||||
|
||||
{/* Right Content */}
|
||||
<div className="hero-content">
|
||||
<h2>{slide.title}</h2>
|
||||
<p>{slide.description}</p>
|
||||
<button onClick={()=>{
|
||||
navigate("/registration")
|
||||
}}>{slide.button}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
))}
|
||||
</Swiper>
|
||||
);
|
||||
};
|
||||
|
||||
export default HeroSlider;
|
||||
131
src/components/ui/ProfileCardDemo.jsx
Normal file
@ -0,0 +1,131 @@
|
||||
import React from "react";
|
||||
import { Heart, X, Crown, Bookmark } from "lucide-react";
|
||||
// Import your images
|
||||
import Profile1 from "../../assets/images/bride1.jpg";
|
||||
import Profile2 from "../../assets/images/bride2.jpg";
|
||||
import Profile3 from "../../assets/images/bride3.jpg";
|
||||
import Profile4 from "../../assets/images/bride4.jpg";
|
||||
|
||||
export default function ProfileCardDemo() {
|
||||
// Sample data for multiple cards with image paths
|
||||
const profiles = [
|
||||
{
|
||||
id: 1,
|
||||
name: "Jerome Bell",
|
||||
age: "22 yrs",
|
||||
height: "5'2\"",
|
||||
location: "Chennai",
|
||||
caste: "Brahmin",
|
||||
zodiac1: "Aries",
|
||||
zodiac2: "Scorpio",
|
||||
image: Profile1,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "Neha Singh",
|
||||
age: "26 yrs",
|
||||
height: "5'6\"",
|
||||
location: "Delhi",
|
||||
caste: "Brahmin",
|
||||
zodiac1: "Aries",
|
||||
zodiac2: "Scorpio",
|
||||
image: Profile2,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Priya Sharma",
|
||||
age: "24 yrs",
|
||||
height: "5'4\"",
|
||||
location: "Mumbai",
|
||||
caste: "Brahmin",
|
||||
zodiac1: "Aries",
|
||||
zodiac2: "Scorpio",
|
||||
image: Profile3,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "Kavya Iyer",
|
||||
age: "23 yrs",
|
||||
height: "5'3\"",
|
||||
location: "Bangalore",
|
||||
caste: "Brahmin",
|
||||
zodiac1: "Aries",
|
||||
zodiac2: "Scorpio",
|
||||
image: Profile4,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className=" flex justify-center items-center py-8 md:px-4">
|
||||
{/* Grid Container - max-w-[1400px] with 4 columns */}
|
||||
<div className="w-full max-w-[1200px] grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
||||
{profiles.map((profile) => (
|
||||
<div
|
||||
key={profile.id}
|
||||
className="w-full rounded-[28px] overflow-hidden bg-white shadow-2xl"
|
||||
>
|
||||
{/* IMAGE SECTION */}
|
||||
<div className="relative">
|
||||
<img
|
||||
src={profile.image}
|
||||
alt="profile"
|
||||
className="w-full h-[320px] object-cover"
|
||||
/>
|
||||
|
||||
{/* LEFT ICON (CROWN) */}
|
||||
<div className="absolute top-4 left-4 w-9 h-9 rounded-full bg-[#8b0000] flex items-center justify-center">
|
||||
<Crown size={18} color="#fff" />
|
||||
</div>
|
||||
|
||||
{/* RIGHT ICON (SHORTLIST) */}
|
||||
<div className="absolute top-4 right-4 px-3 py-1.5 rounded-[20px] bg-white flex items-center gap-1.5 text-[13px] font-medium shadow-lg">
|
||||
<Bookmark size={14} /> Shortlist
|
||||
</div>
|
||||
|
||||
{/* FADE FOR GLASS */}
|
||||
<div className="absolute bottom-0 left-0 right-0 h-0 bg-gradient-to-t from-white/90 to-transparent" />
|
||||
</div>
|
||||
|
||||
{/* GLASS CONTENT */}
|
||||
<div className="px-4 py-[12px] -mt-[60px] bg-white/65 backdrop-blur-[25px] rounded-t-[15px] shadow-[0_-10px_30px_rgba(0,0,0,0.15)] relative z-[2]">
|
||||
<h2 className="text-center text-[22px] font-semibold mb-3.5">
|
||||
{profile.name}
|
||||
</h2>
|
||||
|
||||
{/* INFO PILLS */}
|
||||
<div className="flex flex-wrap justify-center gap-2 ">
|
||||
{[
|
||||
profile.age,
|
||||
profile.height,
|
||||
profile.location,
|
||||
profile.caste,
|
||||
profile.zodiac1,
|
||||
profile.zodiac2,
|
||||
].map((v, i) => (
|
||||
<span
|
||||
key={i}
|
||||
className="px-3.5 py-1.5 rounded-[20px] bg-white/70 border border-black/8 text-[13px] "
|
||||
>
|
||||
{v}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* ACTION BUTTONS */}
|
||||
<div className="flex gap-3 mt-[15px] items-center justify-center">
|
||||
<button className="px-2 py-1 rounded-[20px] border border-red-200 bg-red-50 flex items-center justify-center gap-1.5 cursor-pointer">
|
||||
<X size={18} /> Decline
|
||||
</button>
|
||||
|
||||
<button className=" px-2 py-1 rounded-[20px] border border-green-200 bg-green-100 text-green-900 flex items-center justify-center gap-1.5 font-medium cursor-pointer">
|
||||
<Heart size={18} /> Interest
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -30,16 +30,16 @@ const LifestyleDetailsForm = ({ onSubmitStep, onSkipStep, errors }) => {
|
||||
|
||||
const fields = [
|
||||
{ name: "diet", label: "Diet", options: ["Veg", "Non-Veg", "Eggetarian"] },
|
||||
{
|
||||
name: "drinking",
|
||||
label: "Drinking Habits",
|
||||
options: ["No", "Occasionally", "Regularly"],
|
||||
},
|
||||
{
|
||||
name: "smoking",
|
||||
label: "Smoking Habits",
|
||||
options: ["No", "Occasionally", "Regularly"],
|
||||
},
|
||||
// {
|
||||
// name: "drinking",
|
||||
// label: "Drinking Habits",
|
||||
// options: ["No", "Occasionally", "Regularly"],
|
||||
// },
|
||||
// {
|
||||
// name: "smoking",
|
||||
// label: "Smoking Habits",
|
||||
// options: ["No", "Occasionally", "Regularly"],
|
||||
// },
|
||||
{
|
||||
name: "hobbies",
|
||||
label: "Hobbies & Interests",
|
||||
|
||||
@ -299,8 +299,8 @@ const ProfilePreviewPage = () => {
|
||||
|
||||
<CardContent sx={{ pt: 1 }}>
|
||||
{renderField("Diet", lifestyleDetails.diet)}
|
||||
{renderField("Drinking", lifestyleDetails.drinking)}
|
||||
{renderField("Smoking", lifestyleDetails.smoking)}
|
||||
{/* {renderField("Drinking", lifestyleDetails.drinking)}
|
||||
{renderField("Smoking", lifestyleDetails.smoking)} */}
|
||||
{renderField("Hobbies", lifestyleDetails.hobbies)}
|
||||
|
||||
</CardContent>
|
||||
|
||||
21
src/layout/HomeLayout.jsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { Outlet } from "react-router-dom";
|
||||
import ProfileHeader from "../components/common/ProfileHeader";
|
||||
import Footer from "../components/common/Footer";
|
||||
|
||||
const HomeLayout = () => {
|
||||
return (
|
||||
<>
|
||||
<ProfileHeader/>
|
||||
{/* <div className=" w-[100%] max-w-[1400px] mx-auto p-2"
|
||||
style={{
|
||||
marginBottom:'10px',
|
||||
}}> */}
|
||||
<Outlet />
|
||||
{/* </div> */}
|
||||
|
||||
<Footer/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default HomeLayout
|
||||
27
src/layout/MatchesLayout.jsx
Normal file
@ -0,0 +1,27 @@
|
||||
import React from 'react'
|
||||
import ProfileHeader from '../components/common/ProfileHeader'
|
||||
import Footer from '../components/common/Footer'
|
||||
import { Navigate, Outlet } from 'react-router-dom'
|
||||
import { isAuthenticated } from '../utills/auth'
|
||||
|
||||
const MatchesLayout = () => {
|
||||
const auth = isAuthenticated();
|
||||
return (
|
||||
<>
|
||||
<ProfileHeader/>
|
||||
<div className="page-wrapper w-[100%] max-w-[1400px] mx-auto p-2"
|
||||
style={{
|
||||
marginBottom:'10px',
|
||||
|
||||
}}>
|
||||
{/* <Outlet /> */}
|
||||
|
||||
{(auth ? <Outlet /> : <Navigate to="/login" replace />)}
|
||||
</div>
|
||||
|
||||
<Footer/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default MatchesLayout
|
||||
@ -1,21 +1,27 @@
|
||||
import { Outlet } from "react-router-dom";
|
||||
import { Navigate, Outlet } from "react-router-dom";
|
||||
import ProfileHeader from "../components/common/ProfileHeader";
|
||||
import Footer from "../components/common/Footer";
|
||||
import { isAuthenticated } from "../utills/auth";
|
||||
const ProfileLayout = () => {
|
||||
const auth = isAuthenticated();
|
||||
return (
|
||||
<>
|
||||
<ProfileHeader/>
|
||||
<div className=" w-[100%] max-w-[1400px] mx-auto p-2"
|
||||
<ProfileHeader />
|
||||
<div
|
||||
className="page-wrapper w-[100%] max-w-[1400px] mx-auto p-2"
|
||||
style={{
|
||||
marginBottom:'10px',
|
||||
// overflowX:"hidden"
|
||||
}}>
|
||||
<Outlet />
|
||||
marginBottom: "10px",
|
||||
overflowX: "hidden",
|
||||
}}
|
||||
>
|
||||
{/* <Outlet /> */}
|
||||
|
||||
{auth ? <Outlet /> : <Navigate to="/login" replace />}
|
||||
</div>
|
||||
|
||||
<Footer/>
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default ProfileLayout
|
||||
export default ProfileLayout;
|
||||
|
||||
@ -15,6 +15,7 @@ import wedding12 from "../assets/images/wedding12.avif";
|
||||
import MatrimonySwipeCards from "../components/common/MatrimonySwipeCards";
|
||||
import ThreeScrollTrigger from "../components/common/Threemarquee";
|
||||
import AppQRCode from "../components/landing/AppQRCode";
|
||||
import HeroSlider from "../components/ui/HeroSlider";
|
||||
|
||||
|
||||
|
||||
@ -67,7 +68,7 @@ const HomePage = () => {
|
||||
|
||||
return (
|
||||
<div className="">
|
||||
<div className="relative">
|
||||
{/* <div className="relative">
|
||||
<ThreeDMarquee images={images} cols = {4} />
|
||||
<div
|
||||
className="absolute bottom-0 left-0 w-full h-[100px] z-9"
|
||||
@ -77,7 +78,9 @@ const HomePage = () => {
|
||||
"linear-gradient(to bottom, rgba(255,255,255,0) 0%, rgba(255,255,255,0.6) 40%, rgba(255,255,255,1) 100%)",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
<HeroSlider/>
|
||||
<AppPromoteSection/>
|
||||
<div className="w-full bg-white py-8 pt-0">
|
||||
<AppQRCode/>
|
||||
|
||||
@ -6,7 +6,9 @@ const MatchesPage = () => {
|
||||
<>
|
||||
<SearchUI/>
|
||||
<MatchesInterface/>
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
</>
|
||||
)
|
||||
|
||||
39
src/pages/NotFound.jsx
Normal file
@ -0,0 +1,39 @@
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
const NotFound = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gray-50 px-4">
|
||||
<div className="text-center max-w-md">
|
||||
<h1 className="text-7xl font-bold text-red-900">404</h1>
|
||||
|
||||
<h2 className="mt-4 text-2xl font-semibold text-gray-800">
|
||||
Page Not Found
|
||||
</h2>
|
||||
|
||||
<p className="mt-2 text-gray-600">
|
||||
Sorry, the page you are looking for doesn’t exist or has been moved.
|
||||
</p>
|
||||
|
||||
<div className="mt-6 flex justify-center gap-4">
|
||||
<button
|
||||
onClick={() => navigate(-1)}
|
||||
className="px-5 py-2 rounded-lg bg-gray-200 hover:bg-gray-300 transition"
|
||||
>
|
||||
Go Back
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => navigate("/")}
|
||||
className="px-5 py-2 rounded-lg bg-red-900 text-white hover:bg-red-600 transition"
|
||||
>
|
||||
Go Home
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotFound;
|
||||
@ -41,8 +41,8 @@ const registrationformSlice = createSlice({
|
||||
},
|
||||
lifestyleDetails: {
|
||||
diet: "",
|
||||
drinking: "",
|
||||
smoking: "",
|
||||
// drinking: "",
|
||||
// smoking: "",
|
||||
hobbies: "",
|
||||
},
|
||||
partnerPreferences: {
|
||||
@ -160,8 +160,8 @@ preloadDummyProfile: (state) => {
|
||||
state.lifestyleDetails = {
|
||||
...state.lifestyleDetails,
|
||||
diet: "Veg",
|
||||
drinking: "No",
|
||||
smoking: "No",
|
||||
// drinking: "No",
|
||||
// smoking: "No",
|
||||
hobbies: "Reading, Music",
|
||||
};
|
||||
state.partnerPreferences = {
|
||||
|
||||
@ -5,18 +5,23 @@ import LoginPage from "../pages/auth/LoginPage";
|
||||
import ForgotPasswordPage from "../pages/auth/ForgotPasswordPage";
|
||||
import ChangePasswordPage from "../components/auth/ChangePasswordForm";
|
||||
import ProfileLayout from "../layout/ProfileLayout";
|
||||
import HomeLayout from "../layout/HomeLayout";
|
||||
import StepperForm from "../feature/StepperForm";
|
||||
import NotFound from "../pages/NotFound";
|
||||
const PublicRoutes = () => {
|
||||
return (
|
||||
<>
|
||||
<Route element={<ProfileLayout />}>
|
||||
<Route element={<HomeLayout />}>
|
||||
<Route path="/" element={<HomePage />} />
|
||||
<Route path="/registration" element={<StepperForm />} />
|
||||
|
||||
</Route>
|
||||
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
<Route path="/forgot-password" element={<ForgotPasswordPage />} />
|
||||
|
||||
|
||||
{/* 404 route MUST be last */}
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -19,14 +19,16 @@ import StepperForm from "../feature/StepperForm";
|
||||
import FilterForm from "../feature/FilterForm";
|
||||
import ProfilePreviewPage from "../feature/ProfilePreviewPage";
|
||||
import NotificationPage from "../pages/NotificationPage";
|
||||
import MatchesLayout from "../layout/MatchesLayout";
|
||||
import ProfileCardDemo from "../components/ui/ProfileCardDemo";
|
||||
|
||||
const UserRoutes = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Route element={<ProfileLayout />}>
|
||||
{/* <Route element={<ProfileLayout />}>
|
||||
<Route path="/registration" element={<StepperForm />} />
|
||||
</Route>
|
||||
</Route> */}
|
||||
|
||||
<Route element={<ProfileLayout />}>
|
||||
<Route path="/filter" element={<FilterForm />} />
|
||||
@ -38,9 +40,12 @@ const UserRoutes = () => {
|
||||
<Route element={<ProfileLayout />}>
|
||||
<Route path="/main/dashboard" element={<UserDashboardHome />} />
|
||||
</Route>
|
||||
<Route element={<ProfileLayout />}>
|
||||
<Route element={<MatchesLayout />}>
|
||||
<Route path="/matches" element={<MatchesPage />} />
|
||||
</Route>
|
||||
<Route element={<MatchesLayout />}>
|
||||
<Route path="/profile-card" element={<ProfileCardDemo />} />
|
||||
</Route>
|
||||
|
||||
<Route element={<ProfileLayout />}>
|
||||
<Route path="/interest" element={<InterestSendPage />} />
|
||||
|
||||
187
src/services/axiosInstance.js
Normal file
@ -0,0 +1,187 @@
|
||||
import axios from "axios";
|
||||
import { API_ENDPOINTS } from "./apiEndpoints";
|
||||
|
||||
|
||||
/**
|
||||
* Create an Axios instance with the base URL from environment variables
|
||||
* and default headers for JSON communication.
|
||||
*/
|
||||
const axiosInstance = axios.create({
|
||||
baseURL: import.meta.env.VITE_UNICORN_API_BASE_URL ||
|
||||
"https://www.unicorn.amrithaa.net/backend/api/" ,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Create a separate Axios instance for handling multipart file uploads
|
||||
* while sharing the same base URL and authorization mechanism.
|
||||
*/
|
||||
const apiForFiles = axios.create({
|
||||
baseURL: import.meta.env.VITE_UNICORN_API_BASE_URL,
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Set Access Token
|
||||
*/
|
||||
export const setAccessToken = (token) => {
|
||||
if (token) {
|
||||
localStorage.setItem("access_token", token); // Store token in localStorage
|
||||
axiosInstance.defaults.headers["Authorization"] = `Bearer ${token}`;
|
||||
apiForFiles.defaults.headers["Authorization"] = `Bearer ${token}`;
|
||||
} else {
|
||||
localStorage.removeItem("access_token");
|
||||
delete axiosInstance.defaults.headers["Authorization"];
|
||||
delete apiForFiles.defaults.headers["Authorization"];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a request interceptor to include the authorization token
|
||||
* in the request headers if it exists in localStorage.
|
||||
*/
|
||||
const addAuthInterceptor = (instance) => {
|
||||
instance.interceptors.request.use(
|
||||
(config) => {
|
||||
const token = localStorage.getItem("access_token");
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => Promise.reject(error)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a response interceptor to handle global error scenarios,
|
||||
* such as redirecting to the login page on a 401 Unauthorized error.
|
||||
*/
|
||||
const addErrorInterceptor = (instance) => {
|
||||
instance.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
if (error.response && error.response.status === 401) {
|
||||
console.error("Unauthorized access - logging out...");
|
||||
localStorage.removeItem("access_token");
|
||||
// window.location.href = "/";
|
||||
navigate("/");
|
||||
} else if (error.response && error.response.status === 403) {
|
||||
window.location.href = "/";
|
||||
// navigate("/");
|
||||
} else if (error.response && error.response.status === 404) {
|
||||
console.error("404 Error - Endpoint not found:", error.config?.url);
|
||||
// Don't redirect for 404, let component handle it
|
||||
} else if (error.response && error.response.status >= 500) {
|
||||
console.error("Server error:", error.response?.data);
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
// Apply interceptors to both instances
|
||||
addAuthInterceptor(axiosInstance);
|
||||
addAuthInterceptor(apiForFiles);
|
||||
|
||||
addErrorInterceptor(axiosInstance);
|
||||
addErrorInterceptor(apiForFiles);
|
||||
|
||||
/**
|
||||
* Clear all user data from the browser: localStorage, sessionStorage, and cookies.
|
||||
*/
|
||||
export const clearUserData = () => {
|
||||
try {
|
||||
// Clear localStorage
|
||||
localStorage.clear();
|
||||
|
||||
// Clear sessionStorage
|
||||
sessionStorage.clear();
|
||||
|
||||
// Clear cookies
|
||||
document.cookie.split(";").forEach((cookie) => {
|
||||
const [name] = cookie.split("=");
|
||||
document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/`;
|
||||
});
|
||||
|
||||
// Remove authorization headers from Axios instances
|
||||
delete axiosInstance.defaults.headers["Authorization"];
|
||||
delete apiForFiles.defaults.headers["Authorization"];
|
||||
|
||||
console.log("User data cleared from browser.");
|
||||
// Add redirect to ensure all tabs show login page
|
||||
// window.location.href = '/';
|
||||
} catch (error) {
|
||||
console.error("Error clearing user data:", error);
|
||||
}
|
||||
};
|
||||
|
||||
export const urlToFile = async (url, filename = "file") => {
|
||||
try {
|
||||
// Use your axios instance to request the URL as a Blob
|
||||
const response = await apiForFiles.get(url, {
|
||||
responseType: "blob",
|
||||
});
|
||||
|
||||
// Axios header keys are lowercase
|
||||
const contentType =
|
||||
response.headers["content-type"] ||
|
||||
"*"(
|
||||
// Guess MIME type based on filename extension
|
||||
filename.toLowerCase().endsWith(".png")
|
||||
? "image/png"
|
||||
: filename.toLowerCase().endsWith(".webp")
|
||||
? "image/webp"
|
||||
: filename.toLowerCase().endsWith(".jpg") ||
|
||||
filename.toLowerCase().endsWith(".jpeg")
|
||||
? "image/jpeg"
|
||||
: "application/octet-stream"
|
||||
);
|
||||
|
||||
// Create and return a File object from the blob data
|
||||
return new File([response.data], filename, { type: contentType });
|
||||
} catch (error) {
|
||||
console.error("urlToFile axios error:", error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get FCM token from localStorage
|
||||
*/
|
||||
const getFcmToken = () => {
|
||||
return localStorage.getItem("fcm_token");
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear FCM token from localStorage
|
||||
*/
|
||||
const clearFcmToken = () => {
|
||||
localStorage.removeItem("fcm_token");
|
||||
};
|
||||
|
||||
export const logoutAPI = async () => {
|
||||
console.log("Calling logout API...");
|
||||
try {
|
||||
const res = await axiosInstance.post(API_ENDPOINTS.LOGOUT);
|
||||
return res.data; // assuming API returns { message: "Logout successful" }
|
||||
} catch (error) {
|
||||
console.error("Logout API failed:", error);
|
||||
// even if API fails, still clear local storage
|
||||
} finally {
|
||||
console.log("Clearing local storage...");
|
||||
// always clear local storage
|
||||
localStorage.removeItem("access_token");
|
||||
// clearFcmToken();
|
||||
}
|
||||
};
|
||||
|
||||
// Export both instances
|
||||
export default axiosInstance;
|
||||
export { apiForFiles };
|
||||
70
src/styles/HeroSlider.css
Normal file
@ -0,0 +1,70 @@
|
||||
.hero-swiper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.hero-wrapper{
|
||||
background-image: url("../assets/images/greenbg2.jpg");
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.hero-slide {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 60px;
|
||||
/* background: #111; */
|
||||
|
||||
color: #fff;
|
||||
min-height: 480px;
|
||||
width: 100%;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.hero-image img {
|
||||
width: 100%;
|
||||
max-width: 420px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.hero-content h2 {
|
||||
font-size: 32px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.hero-content p {
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 20px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.hero-content button {
|
||||
background: #ffcc00;
|
||||
color: #000;
|
||||
border: none;
|
||||
padding: 12px 24px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.hero-slide {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.hero-image img {
|
||||
max-width: 100%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
108
src/utills/ErrorBoundary.jsx
Normal file
@ -0,0 +1,108 @@
|
||||
// ErrorBoundary.js - Error boundary component for graceful error handling
|
||||
import { Component } from "react";
|
||||
|
||||
class ErrorBoundary extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { hasError: false, error: null, errorInfo: null };
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error) {
|
||||
// Update state so the next render will show the fallback UI
|
||||
return { hasError: true };
|
||||
}
|
||||
|
||||
componentDidCatch(error, errorInfo) {
|
||||
// Log the error details
|
||||
console.error("ErrorBoundary caught an error:", error, errorInfo);
|
||||
this.setState({
|
||||
error: error,
|
||||
errorInfo: errorInfo,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gray-50">
|
||||
<div className="max-w-md w-full bg-white shadow-lg rounded-lg p-6">
|
||||
<div className="flex items-center mb-4">
|
||||
<div className="bg-red-100 rounded-full p-2">
|
||||
<svg
|
||||
className="w-6 h-6 text-red-600"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h2 className="ml-3 text-lg font-medium text-gray-900">
|
||||
Something went wrong
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<p className="text-gray-600 mb-4">
|
||||
There was an error with the application. Please try refreshing the
|
||||
page.
|
||||
</p>
|
||||
|
||||
{/* Show error details in development */}
|
||||
{process.env.NODE_ENV === "development" && this.state.error && (
|
||||
<details className="mb-4 text-sm text-gray-500">
|
||||
<summary className="cursor-pointer font-medium">
|
||||
Error Details (Development)
|
||||
</summary>
|
||||
<div className="mt-2 p-2 bg-gray-100 rounded text-xs overflow-auto">
|
||||
<strong>Error:</strong> {this.state.error.toString()}
|
||||
<br />
|
||||
<strong>Stack:</strong>
|
||||
<pre className="whitespace-pre-wrap mt-1">
|
||||
{this.state.errorInfo.componentStack}
|
||||
</pre>
|
||||
</div>
|
||||
</details>
|
||||
)}
|
||||
|
||||
<div className="flex space-x-3">
|
||||
<button
|
||||
onClick={() => window.location.reload()}
|
||||
className="flex-1 bg-blue-600 text-white py-2 px-4 rounded hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
Refresh Page
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
hasError: false,
|
||||
error: null,
|
||||
errorInfo: null,
|
||||
});
|
||||
}}
|
||||
className="flex-1 bg-gray-600 text-white py-2 px-4 rounded hover:bg-gray-700 transition-colors"
|
||||
>
|
||||
Try Again
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 text-center">
|
||||
<a href="/" className="text-sm text-blue-600 hover:text-blue-500">
|
||||
Go to Home Page
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
export default ErrorBoundary;
|
||||
3
src/utills/auth.js
Normal file
@ -0,0 +1,3 @@
|
||||
export const isAuthenticated = () => {
|
||||
return !!localStorage.getItem("token");
|
||||
};
|
||||