form done

This commit is contained in:
Meenadeveloper 2025-11-27 18:26:55 +05:30
parent b1d9d46e4e
commit d35bd08f5f
19 changed files with 3301 additions and 14 deletions

264
package-lock.json generated
View File

@ -8,21 +8,27 @@
"name": "thirukalyanam",
"version": "0.0.0",
"dependencies": {
"@date-io/date-fns": "^3.2.1",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@files-ui/react": "^1.2.5",
"@lottiefiles/dotlottie-react": "^0.17.8",
"@mui/icons-material": "^7.3.5",
"@mui/lab": "^7.0.1-beta.19",
"@mui/material": "^7.3.5",
"@mui/styled-engine-sc": "^7.3.5",
"@mui/x-date-pickers": "^8.19.0",
"@reduxjs/toolkit": "^2.11.0",
"@tailwindcss/vite": "^4.1.17",
"axios": "^1.13.2",
"date-fns": "^4.1.0",
"framer-motion": "^12.23.24",
"lightswind": "^3.1.18",
"lucide-react": "^0.553.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-lazy-load-image-component": "^1.6.3",
"react-redux": "^9.2.0",
"react-router-dom": "^7.9.6",
"styled-components": "^6.1.19",
"swiper": "^12.0.3",
@ -334,6 +340,29 @@
"node": ">=6.9.0"
}
},
"node_modules/@date-io/core": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/@date-io/core/-/core-3.2.0.tgz",
"integrity": "sha512-hqwXvY8/YBsT9RwQITG868ZNb1MVFFkF7W1Ecv4P472j/ZWa7EFcgSmxy8PUElNVZfvhdvfv+a8j6NWJqOX5mA==",
"license": "MIT"
},
"node_modules/@date-io/date-fns": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/@date-io/date-fns/-/date-fns-3.2.1.tgz",
"integrity": "sha512-CtXgTOAamkImI+CmbWRNdBi4ljj9xm/tdoPa+eeeiygduzubJTsXp18vYz+Vs/9yLho1zUOXlxpsfsF7PsXSWQ==",
"license": "MIT",
"dependencies": {
"@date-io/core": "^3.2.0"
},
"peerDependencies": {
"date-fns": "^3.2.0 || ^4.1.0"
},
"peerDependenciesMeta": {
"date-fns": {
"optional": true
}
}
},
"node_modules/@dimforge/rapier3d-compat": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz",
@ -349,6 +378,13 @@
"node": ">=10.0.0"
}
},
"node_modules/@dynamicss/dynamicss": {
"version": "2.2.8",
"resolved": "https://registry.npmjs.org/@dynamicss/dynamicss/-/dynamicss-2.2.8.tgz",
"integrity": "sha512-e6hrGUydr8f+c9E/9fHFSG5LoSLdq/MdZXXfbzEDWIVuzKF2hcdxZE7nHNqUNF2htw1mZ17Pyoshu3A6kFEeFA==",
"hasInstallScript": true,
"license": "MIT"
},
"node_modules/@emotion/babel-plugin": {
"version": "11.13.5",
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
@ -398,6 +434,7 @@
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz",
"integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@emotion/memoize": "^0.9.0"
}
@ -1076,6 +1113,27 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@files-ui/core": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@files-ui/core/-/core-2.0.7.tgz",
"integrity": "sha512-L/DqdMWMusULUTrOd6AEpiC+Lkfy9eMNN8LVLzPyfHrUgW9jw2b2Aa5BBJyjdiTHTmd8hM/huc0kVu7CgeZWIQ==",
"license": "MIT"
},
"node_modules/@files-ui/react": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/@files-ui/react/-/react-1.2.5.tgz",
"integrity": "sha512-in6RAdF/MsJ31lgeuSYSzo+4pe1bHrzpfEUfvIBwgv/JlUgwtdj5jE2xom/qoKovMy+Gg+WI+dmNzkrzCtkXBg==",
"license": "MIT",
"dependencies": {
"@dynamicss/dynamicss": "^2.2.8",
"@files-ui/core": "^2.0.6"
},
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react": "^17.0.2 || ^18.0.0 || ^19.0.0",
"react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/@humanfs/core": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@ -1455,6 +1513,7 @@
"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",
@ -1537,6 +1596,94 @@
}
}
},
"node_modules/@mui/x-date-pickers": {
"version": "8.19.0",
"resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-8.19.0.tgz",
"integrity": "sha512-TQ4FsGUsiGJVs+Ie4q7nHXUmFqZADXL/1hVtZpOKsdr3WQXwpX7C5YmeakZGFR2NZnuv4snFj+WTee3kgyFbyQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.28.4",
"@mui/utils": "^7.3.5",
"@mui/x-internals": "8.19.0",
"@types/react-transition-group": "^4.4.12",
"clsx": "^2.1.1",
"prop-types": "^15.8.1",
"react-transition-group": "^4.4.5"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
"@mui/material": "^5.15.14 || ^6.0.0 || ^7.0.0",
"@mui/system": "^5.15.14 || ^6.0.0 || ^7.0.0",
"date-fns": "^2.25.0 || ^3.2.0 || ^4.0.0",
"date-fns-jalali": "^2.13.0-0 || ^3.2.0-0 || ^4.0.0-0",
"dayjs": "^1.10.7",
"luxon": "^3.0.2",
"moment": "^2.29.4",
"moment-hijri": "^2.1.2 || ^3.0.0",
"moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/react": {
"optional": true
},
"@emotion/styled": {
"optional": true
},
"date-fns": {
"optional": true
},
"date-fns-jalali": {
"optional": true
},
"dayjs": {
"optional": true
},
"luxon": {
"optional": true
},
"moment": {
"optional": true
},
"moment-hijri": {
"optional": true
},
"moment-jalaali": {
"optional": true
}
}
},
"node_modules/@mui/x-internals": {
"version": "8.19.0",
"resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-8.19.0.tgz",
"integrity": "sha512-mMmiyJAN5fW27srXJjhXhXJa+w2xGO45rwcjws6OQc9rdXGdJqRXhBwJd+OT7J1xwSdFIIUhjZRTz1KAfCSGBg==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.28.4",
"@mui/utils": "^7.3.5",
"reselect": "^5.1.1",
"use-sync-external-store": "^1.6.0"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -1594,6 +1741,32 @@
"integrity": "sha512-HVj7LrZ4ReHWBimBvu2SKND3cDVUPWKLqRTmWe/fNY6o1owGOX0cAHbdPDTMelgBlVbrTKrre6lFkhqGZErK/g==",
"license": "MIT"
},
"node_modules/@reduxjs/toolkit": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.0.tgz",
"integrity": "sha512-hBjYg0aaRL1O2Z0IqWhnTLytnjDIxekmRxm1snsHjHaKVmIF1HiImWqsq+PuEbn6zdMlkIj9WofK1vR8jjx+Xw==",
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.0.0",
"@standard-schema/utils": "^0.3.0",
"immer": "^11.0.0",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"reselect": "^5.1.0"
},
"peerDependencies": {
"react": "^16.9.0 || ^17.0.0 || ^18 || ^19",
"react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-redux": {
"optional": true
}
}
},
"node_modules/@rolldown/pluginutils": {
"version": "1.0.0-beta.47",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.47.tgz",
@ -1887,6 +2060,18 @@
"win32"
]
},
"node_modules/@standard-schema/spec": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz",
"integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==",
"license": "MIT"
},
"node_modules/@standard-schema/utils": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz",
"integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==",
"license": "MIT"
},
"node_modules/@tailwindcss/node": {
"version": "4.1.17",
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.17.tgz",
@ -2881,6 +3066,12 @@
"integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
"license": "MIT"
},
"node_modules/@types/use-sync-external-store": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
"integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
"license": "MIT"
},
"node_modules/@types/webxr": {
"version": "0.5.24",
"resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz",
@ -3952,9 +4143,9 @@
}
},
"node_modules/date-fns": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
"integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
"license": "MIT",
"peer": true,
"funding": {
@ -5045,6 +5236,16 @@
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
"license": "MIT"
},
"node_modules/immer": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/immer/-/immer-11.0.0.tgz",
"integrity": "sha512-XtRG4SINt4dpqlnJvs70O2j6hH7H0X8fUzFsjMn1rwnETaxwp83HLNimXBjZ78MrKl3/d3/pkzDH0o0Lkxm37Q==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
}
},
"node_modules/import-fresh": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
@ -5940,6 +6141,17 @@
}
}
},
"node_modules/lightswind/node_modules/date-fns": {
"version": "3.6.0",
"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"
}
},
"node_modules/lightswind/node_modules/jiti": {
"version": "1.21.7",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
@ -6963,6 +7175,30 @@
"react": "^15.x.x || ^16.x.x || ^17.x.x || ^18.x.x || ^19.x.x"
}
},
"node_modules/react-redux": {
"version": "9.2.0",
"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"
},
"peerDependencies": {
"@types/react": "^18.2.25 || ^19",
"react": "^18.0 || ^19",
"redux": "^5.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"redux": {
"optional": true
}
}
},
"node_modules/react-refresh": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
@ -7167,6 +7403,22 @@
"node": ">= 0.10"
}
},
"node_modules/redux": {
"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
},
"node_modules/redux-thunk": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
"integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
"license": "MIT",
"peerDependencies": {
"redux": "^5.0.0"
}
},
"node_modules/refractor": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz",
@ -7206,6 +7458,12 @@
"node": ">=0.10.0"
}
},
"node_modules/reselect": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
"integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==",
"license": "MIT"
},
"node_modules/resolve": {
"version": "1.22.11",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",

View File

@ -10,21 +10,27 @@
"preview": "vite preview"
},
"dependencies": {
"@date-io/date-fns": "^3.2.1",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@files-ui/react": "^1.2.5",
"@lottiefiles/dotlottie-react": "^0.17.8",
"@mui/icons-material": "^7.3.5",
"@mui/lab": "^7.0.1-beta.19",
"@mui/material": "^7.3.5",
"@mui/styled-engine-sc": "^7.3.5",
"@mui/x-date-pickers": "^8.19.0",
"@reduxjs/toolkit": "^2.11.0",
"@tailwindcss/vite": "^4.1.17",
"axios": "^1.13.2",
"date-fns": "^4.1.0",
"framer-motion": "^12.23.24",
"lightswind": "^3.1.18",
"lucide-react": "^0.553.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-lazy-load-image-component": "^1.6.3",
"react-redux": "^9.2.0",
"react-router-dom": "^7.9.6",
"styled-components": "^6.1.19",
"swiper": "^12.0.3",

View File

@ -1,6 +1,7 @@
import "./App.css";
import { BrowserRouter as Router } from "react-router-dom";
import AppRoutes from "./routes/AppRoutes";
function App() {
return (
<>

View File

@ -10,6 +10,7 @@ import StarIcon from "@mui/icons-material/Star";
import VisibilityIcon from "@mui/icons-material/Visibility";
import PersonAddIcon from "@mui/icons-material/PersonAdd";
import { motion } from 'framer-motion';
import FilterModal from "../../feature/FilterModal";
// Profile Card Component
function ProfileCard({ profile }) {
@ -98,30 +99,33 @@ function ProfileCard({ profile }) {
</span>
</div>
<div className="flex gap-3 my-2 justify-between w-full px-[20px]">
<button className="w-[60px] h-[60px] bg-[#A70710] hover:bg-red-600 text-white
font-semibold text-base py-2 rounded-full shadow-md
<div className="flex gap-3 my-2 justify-between w-full px-[0px]">
<button className="gap-2 px-3 w-[fit-content] bg-[#A70710] hover:bg-red-600 text-white
font-semibold text-base py-2 rounded-[20px] shadow-md
hover:shadow-lg transition-all duration-300 flex items-center justify-center transform hover:scale-95">
<svg className="w-7 h-7" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M18 6L6 18M6 6l12 12" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
Decline
</button>
<button
className="w-[60px] h-[60px] bg-[#034E08] hover:bg-green-700 text-white font-semibold text-base
rounded-full shadow-lg hover:shadow-xl transition-all duration-300
className="w-[fit-content] bg-[#034E08] hover:bg-green-700 text-white font-semibold text-base
rounded-[20px] px-3 gap-2 py-1 shadow-lg hover:shadow-xl transition-all duration-300
transform hover:scale-105 flex items-center justify-center"
onClick={() => setIsLiked(!isLiked)}
>
{isLiked ? (
<svg className="w-7 h-7" viewBox="0 0 24 24" fill="currentColor">
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" fill="#EF4444"/>
</svg>
) : (
<svg className="w-7 h-7" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
)}
Interest
</button>
</div>
</div>
@ -286,11 +290,14 @@ export default function MatchesInterface() {
{/* Right Content Area - Scrollable */}
<div className=" px-2 py-8">
<div className="w-[100%] max-w-[1200px] mx-auto">
<h1 className="text-[24px] font-bold text-gray-900 mb-8">
<div className="flex justify-between gap-2 itemes-center mb-8">
<h1 className="text-[24px] font-bold text-gray-900 ">
{tabs.find(t => t.id === selectedTab)?.title}
</h1>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-2 gap-2">
<FilterModal/>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 gap-2">
{profiles.map((profile) => (
<ProfileCard key={profile.id} profile={profile} />
))}

View File

@ -0,0 +1,114 @@
import React from "react";
import {
Dropzone,
FileMosaic,
FullScreen,
ImagePreview,
VideoPreview,
} from "@files-ui/react";
const BASE_URL = "https://www.myserver.com";
const AdvancedDropzone = ({ value, onChange }) => {
const [extFiles, setExtFiles] = React.useState(value || []);
const [imageSrc, setImageSrc] = React.useState(undefined);
const [videoSrc, setVideoSrc] = React.useState(undefined);
const updateFiles = (incomingFiles) => {
console.log("incoming files", incomingFiles);
setExtFiles(incomingFiles);
onChange && onChange(incomingFiles); // pass back to parent (e.g. Redux)
};
const onDelete = (id) => {
const updated = extFiles.filter((x) => x.id !== id);
setExtFiles(updated);
onChange && onChange(updated);
};
const handleSee = (imageSource) => setImageSrc(imageSource);
const handleWatch = (videoSource) => setVideoSrc(videoSource);
const handleStart = (filesToUpload) => {
console.log("advanced demo start upload", filesToUpload);
};
const handleFinish = (uploadedFiles) => {
console.log("advanced demo finish upload", uploadedFiles);
};
const handleAbort = (id) => {
setExtFiles((prev) =>
prev.map((ef) =>
ef.id === id ? { ...ef, uploadStatus: "aborted" } : ef
)
);
};
const handleCancel = (id) => {
setExtFiles((prev) =>
prev.map((ef) =>
ef.id === id ? { ...ef, uploadStatus: undefined } : ef
)
);
};
return (
<>
<Dropzone
onChange={updateFiles}
minHeight="195px"
value={extFiles}
accept="image/*, video/*"
maxFiles={3}
maxFileSize={2 * 1024 * 1024}
label="Drag'n drop files here or click to browse"
uploadConfig={{
url: BASE_URL + "/file",
cleanOnUpload: true,
}}
onUploadStart={handleStart}
onUploadFinish={handleFinish}
fakeUpload
actionButtons={{
position: "after",
abortButton: {},
deleteButton: {},
uploadButton: {},
}}
>
{extFiles.map((file) => (
<FileMosaic
{...file}
key={file.id}
onDelete={onDelete}
onSee={handleSee}
onWatch={handleWatch}
onAbort={handleAbort}
onCancel={handleCancel}
resultOnTooltip
alwaysActive
preview
info
/>
))}
</Dropzone>
<FullScreen
open={imageSrc !== undefined}
onClose={() => setImageSrc(undefined)}
>
<ImagePreview src={imageSrc} />
</FullScreen>
<FullScreen
open={videoSrc !== undefined}
onClose={() => setVideoSrc(undefined)}
>
<VideoPreview src={videoSrc} autoPlay controls />
</FullScreen>
</>
);
};
export default AdvancedDropzone;

View File

@ -0,0 +1,144 @@
import React, { useEffect, useRef } from "react";
import { useDispatch, useSelector } from "react-redux";
import { updateEducationalDetails } from "../redux/registrationFormSlice";
import { TextField, Button, Grid } from "@mui/material";
const EducationalDetailsForm = ({ onSubmitStep, onSkipStep, errors }) => {
const dispatch = useDispatch();
const data = useSelector((state) => state.registerform.educationalDetails);
const inputRef = useRef(null);
useEffect(() => {
inputRef.current?.focus();
}, []);
const handleChange = (field, value) => {
dispatch(updateEducationalDetails({ [field]: value }));
};
const handleSubmit = () => {
console.log("Submitting educational details:", data);
onSubmitStep();
};
return (
<>
<div className="w-full max-w-[1200px] mx-auto bg-[#fff2f2] py-6 md:px-2 rounded-8">
<form noValidate autoComplete="off" style={{ padding: 16 }}>
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-20 gap-y-10 mb-6">
{/* Highest Qualification */}
<div className="flex flex-col gap-4">
<TextField
fullWidth
inputRef={inputRef}
name="qualification"
label="Highest Qualification"
value={data.qualification}
onChange={(e) => handleChange("qualification", e.target.value)}
error={Boolean(errors.qualification)}
helperText={errors.qualification}
placeholder="Enter Highest Qualification"
variant="outlined"
/>
</div>
{/* Field of Study */}
<div className="flex flex-col gap-4">
<TextField
fullWidth
name="fieldOfStudy"
label="Field of Study"
value={data.fieldOfStudy}
onChange={(e) => handleChange("fieldOfStudy", e.target.value)}
error={Boolean(errors.fieldOfStudy)}
helperText={errors.fieldOfStudy}
placeholder="Enter Field of Study"
variant="outlined"
/>
</div>
{/* Occupation */}
<div className="flex flex-col gap-4">
<TextField
fullWidth
name="occupation"
label="Occupation"
value={data.occupation}
onChange={(e) => handleChange("occupation", e.target.value)}
error={Boolean(errors.occupation)}
helperText={errors.occupation}
placeholder="Enter Occupation"
variant="outlined"
/>
</div>
{/* Company / Organization Name */}
<div className="flex flex-col gap-4">
<TextField
fullWidth
name="organization"
label="Company / Organization Name"
value={data.organization}
onChange={(e) => handleChange("organization", e.target.value)}
error={Boolean(errors.organization)}
helperText={errors.organization}
placeholder="Enter Company / Organization Name"
variant="outlined"
/>
</div>
{/* Annual Income */}
<div className="flex flex-col gap-4">
<TextField
fullWidth
name="income"
label="Annual Income"
value={data.income}
onChange={(e) => handleChange("income", e.target.value)}
error={Boolean(errors.income)}
helperText={errors.income}
placeholder="Enter Annual Income"
variant="outlined"
/>
</div>
{/* Work Location */}
<div className="flex flex-col gap-4">
<TextField
fullWidth
name="workLocation"
label="Work Location"
value={data.workLocation}
onChange={(e) => handleChange("workLocation", e.target.value)}
error={Boolean(errors.workLocation)}
helperText={errors.workLocation}
placeholder="Enter Work Location"
variant="outlined"
/>
</div>
</div>
<Grid
item
xs={12}
style={{
marginTop: "40px",
display: "flex",
gap: 16,
justifyContent: "center",
}}
>
<Button variant="outlined" onClick={onSkipStep}>
Skip
</Button>
<Button variant="contained" color="primary" onClick={handleSubmit}>
Next
</Button>
</Grid>
</form>
</div>
</>
);
};
export default EducationalDetailsForm;

View File

@ -0,0 +1,164 @@
import React, { useEffect, useRef } from "react";
import { useDispatch, useSelector } from "react-redux";
import { updateFamilyDetails } from "../redux/registrationFormSlice";
import {
Grid,
TextField,
FormControl,
InputLabel,
Select,
MenuItem,
Button,
} from "@mui/material";
const FamilyDetailsForm = ({ onSubmitStep, onSkipStep, errors }) => {
const dispatch = useDispatch();
const data = useSelector((state) => state.registerform.familyDetails);
const inputRef = useRef(null);
useEffect(() => {
inputRef.current?.focus();
}, []);
const handleChange = (field, value) => {
dispatch(updateFamilyDetails({ [field]: value }));
};
return (
<>
<div className="w-full max-w-[1200px] mx-auto bg-[#fff2f2] py-6 md:px-2 rounded-8">
<form noValidate autoComplete="off" style={{ padding: 16 }}>
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-20 gap-y-10 mb-6">
{/* Highest Qualification */}
<div className="flex flex-col gap-4">
<TextField
fullWidth
inputRef={inputRef}
name="fatherName"
label="Father Name"
value={data.fatherName}
onChange={(e) => handleChange("fatherName", e.target.value)}
error={Boolean(errors.fatherName)}
helperText={errors.fatherName}
placeholder="Enter Father Name"
variant="outlined"
/>
</div>
<div className="flex flex-col gap-4">
<TextField
fullWidth
name="fatherOccupation"
label="Father Occupation"
value={data.fatherOccupation}
onChange={(e) => handleChange("fatherOccupation", e.target.value)}
error={Boolean(errors.fatherOccupation)}
helperText={errors.fatherOccupation}
placeholder="Enter Father Occupation"
variant="outlined"
/>
</div>
<div className="flex flex-col gap-4">
<TextField
fullWidth
name="motherName"
label="Mother Name"
value={data.motherName}
onChange={(e) => handleChange("motherName", e.target.value)}
error={Boolean(errors.motherName)}
helperText={errors.motherName}
placeholder="Enter Mother Name"
variant="outlined"
/>
</div>
<div className="flex flex-col gap-4">
<TextField
fullWidth
name="motherOccupation"
label="Mother Occupation"
value={data.motherOccupation}
onChange={(e) => handleChange("motherOccupation", e.target.value)}
error={Boolean(errors.motherOccupation)}
helperText={errors.motherOccupation}
placeholder="Enter Mother Occupation"
variant="outlined"
/>
</div>
<div className="flex flex-col gap-4">
<TextField
fullWidth
name="siblings"
label="Number of Brothers / Sisters"
value={data.siblings}
onChange={(e) => handleChange("siblings", e.target.value)}
error={Boolean(errors.siblings)}
helperText={errors.siblings}
placeholder="Enter Number of Brothers / Sisters"
variant="outlined"
/>
</div>
<div className="flex flex-col gap-4">
<FormControl
fullWidth
variant="outlined"
error={Boolean(errors.siblingsStatus)}
>
<InputLabel id="siblingsStatus-label">
Brothers / Sisters (Married / Unmarried)
</InputLabel>
<Select
labelId="siblingsStatus-label"
label="Brothers / Sisters (Married / Unmarried)"
name="siblingsStatus"
value={data.siblingsStatus}
onChange={(e) => handleChange("siblingsStatus", e.target.value)}
>
<MenuItem value="All Married">All Married</MenuItem>
<MenuItem value="All Unmarried">All Unmarried</MenuItem>
<MenuItem value="Mixed">Mixed</MenuItem>
</Select>
{errors.siblingsStatus && (
<p
style={{
color: "#d32f2f",
margin: "3px 14px 0 14px",
fontSize: "0.75rem",
}}
>
{errors.siblingsStatus}
</p>
)}
</FormControl>
</div>
<Grid item xs={12} style={{ marginTop: 16, display: "flex", gap: 16 , justifyContent: "center"}}>
<Button variant="outlined" fullWidth onClick={onSkipStep}>
Skip
</Button>
<Button variant="contained" color="primary" fullWidth onClick={onSubmitStep}>
Submit & Next
</Button>
</Grid>
</div>
</form>
</div>
</>
);
};
export default FamilyDetailsForm;

623
src/feature/FilterForm.jsx Normal file
View File

@ -0,0 +1,623 @@
import React, { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import {
setAge,
setHeight,
setMaritalStatus,
setMotherTongue,
setReligion,
setMatchesWithHoroscope,
setCaste,
setSubCaste,
updateFilter,
} from "../redux/filterSlice";
import {
Slider,
FormControl,
InputLabel,
Select,
MenuItem,
Chip,
Box,
Button,
Typography,
FormControlLabel,
Checkbox,
Accordion,
AccordionSummary,
AccordionDetails,
} from "@mui/material";
import { ChevronDown } from "lucide-react";
const FilterForm = () => {
const dispatch = useDispatch();
const filters = useSelector((state) => state.filters);
const [expandedSections, setExpandedSections] = useState({
basic: true,
religious: false,
professional: false,
location: false,
lifestyle: false,
family: false,
});
const handleAccordionChange = (section) => (event, isExpanded) => {
setExpandedSections((prev) => ({ ...prev, [section]: isExpanded }));
};
const casteOptions = ["Agamudayar", "Pillai", "Vellalar"];
const motherTongueOptions = [
"Tamil",
"Telugu",
"Malayalam",
"Kannada",
"Hindi",
];
const handleSubmit = () => {
console.log("Filter Values:", filters);
};
const handleSelectionChange = (field, value) => {
console.log(`${field} selected:`, value);
};
return (
<div className="max-w-6xl mx-auto p-4 md:p-6 ">
<div className="bg-white rounded-lg shadow-sm">
{/* Header */}
<div className="border-b p-4">
<Typography variant="h5" className="font-semibold">
Filter Options
</Typography>
</div>
<div className="p-4 md:p-6">
{/* Basic Details Section */}
<Accordion
expanded={expandedSections.basic}
onChange={handleAccordionChange("basic")}
className="mb-4"
>
<AccordionSummary expandIcon={<ChevronDown />}>
<Typography className="font-semibold bg-gray-100 -mx-4 -my-2 px-4 py-2 w-full">
Basic Details
</Typography>
</AccordionSummary>
<AccordionDetails>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
{/* Age Slider */}
<div className="md:col-span-1 mr-2 ">
<Typography gutterBottom sx={{marginBottom:"30px"}}>Age</Typography>
<div className="px-2">
<Slider
value={filters.age}
onChange={(e, newValue) => {
dispatch(setAge(newValue));
handleSelectionChange("Age", newValue);
}}
valueLabelDisplay="on"
min={18}
max={70}
/>
<div className="flex justify-between text-sm text-gray-600">
<span>18 Age</span>
<span>70 Age</span>
</div>
</div>
</div>
{/* Height Slider */}
<div className="md:col-span-1">
<Typography gutterBottom sx={{marginBottom:"30px"}}>Height</Typography>
<div className="px-2">
<Slider
value={filters.height}
onChange={(e, newValue) => {
dispatch(setHeight(newValue));
handleSelectionChange("Height", newValue);
}}
valueLabelDisplay="on"
min={4.0}
max={7.11}
step={0.01}
valueLabelFormat={(value) => `${value.toFixed(2)}'`}
/>
<div className="flex justify-between text-sm text-gray-600">
<span>4'0"</span>
<span>7'11"</span>
</div>
</div>
</div>
{/* Marital Status */}
<FormControl fullWidth>
<InputLabel>Marital Status</InputLabel>
<Select
value={filters.maritalStatus}
label="Marital Status"
onChange={(e) => {
dispatch(setMaritalStatus(e.target.value));
handleSelectionChange("Marital Status", e.target.value);
}}
>
<MenuItem value="All Profile">All Profile</MenuItem>
<MenuItem value="Never Married">Never Married</MenuItem>
<MenuItem value="Divorced">Divorced</MenuItem>
<MenuItem value="Widowed">Widowed</MenuItem>
</Select>
</FormControl>
{/* Mother Tongue */}
<FormControl fullWidth>
<InputLabel>Mother Tongue</InputLabel>
<Select
multiple
value={filters.motherTongue}
label="Mother Tongue"
onChange={(e) => {
dispatch(setMotherTongue(e.target.value));
handleSelectionChange("Mother Tongue", e.target.value);
}}
renderValue={(selected) => (
<Box sx={{ display: "flex", flexWrap: "wrap", gap: 0.5 }}>
{selected.map((value) => (
<Chip key={value} label={value} size="small" />
))}
</Box>
)}
>
{motherTongueOptions.map((lang) => (
<MenuItem key={lang} value={lang}>
{lang}
</MenuItem>
))}
</Select>
</FormControl>
</div>
</AccordionDetails>
</Accordion>
{/* Religious Details Section */}
<Accordion
expanded={expandedSections.religious}
onChange={handleAccordionChange("religious")}
className="mb-4"
>
<AccordionSummary expandIcon={<ChevronDown />}>
<Typography className="font-semibold bg-gray-100 -mx-4 -my-2 px-4 py-2 w-full">
Religious Details
</Typography>
</AccordionSummary>
<AccordionDetails>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
{/* Religion */}
<FormControl fullWidth>
<InputLabel>Religion</InputLabel>
<Select
value={filters.religion}
label="Religion"
onChange={(e) => {
dispatch(setReligion(e.target.value));
handleSelectionChange("Religion", e.target.value);
}}
>
<MenuItem value="Hindu">Hindu</MenuItem>
<MenuItem value="Muslim">Muslim</MenuItem>
<MenuItem value="Christian">Christian</MenuItem>
<MenuItem value="Sikh">Sikh</MenuItem>
</Select>
</FormControl>
{/* Matches with Horoscope */}
<FormControlLabel
control={
<Checkbox
checked={filters.matchesWithHoroscope}
onChange={(e) => {
dispatch(setMatchesWithHoroscope(e.target.checked));
handleSelectionChange(
"Matches with Horoscope",
e.target.checked
);
}}
/>
}
label="Matches with horoscope"
/>
{/* Caste */}
<FormControl fullWidth>
<InputLabel>Caste (Multi Select)</InputLabel>
<Select
multiple
value={filters.caste}
label="Caste (Multi Select)"
onChange={(e) => {
dispatch(setCaste(e.target.value));
handleSelectionChange("Caste", e.target.value);
}}
renderValue={(selected) => (
<Box
sx={{ display: "flex", flexWrap: "wrap", gap: 0.5 }}
>
{selected.map((value) => (
<Chip key={value} label={value} size="small" />
))}
</Box>
)}
>
{["Agamudayar", "Pillai", "Vellalar"].map((caste) => (
<MenuItem key={caste} value={caste}>
{caste}
</MenuItem>
))}
</Select>
</FormControl>
{/* Sub-Caste */}
<FormControl fullWidth>
<InputLabel>Sub-Caste (Multi Select)</InputLabel>
<Select
multiple
value={filters.subCaste}
label="Sub-Caste (Multi Select)"
onChange={(e) => {
dispatch(setSubCaste(e.target.value));
handleSelectionChange("Sub-Caste", e.target.value);
}}
renderValue={(selected) => (
<Box
sx={{ display: "flex", flexWrap: "wrap", gap: 0.5 }}
>
{selected.map((value) => (
<Chip key={value} label={value} size="small" />
))}
</Box>
)}
>
{["Agamudayar", "Pillai", "Vellalar"].map((caste) => (
<MenuItem key={caste} value={caste}>
{caste}
</MenuItem>
))}
</Select>
</FormControl>
{/* Star */}
<FormControl fullWidth>
<InputLabel>Star</InputLabel>
<Select
value={filters.star}
label="Star"
onChange={(e) => {
dispatch(updateFilter({ star: e.target.value }));
handleSelectionChange("Star", e.target.value);
}}
>
<MenuItem value="Any">Any</MenuItem>
<MenuItem value="Ashwini">Ashwini</MenuItem>
<MenuItem value="Bharani">Bharani</MenuItem>
</Select>
</FormControl>
{/* Dasham */}
<FormControl fullWidth>
<InputLabel>Dasham</InputLabel>
<Select
value={filters.dasham}
label="Dasham"
onChange={(e) => {
dispatch(updateFilter({ dasham: e.target.value }));
handleSelectionChange("Dasham", e.target.value);
}}
>
<MenuItem value="Doesn't matter">Doesn't matter</MenuItem>
<MenuItem value="Yes">Yes</MenuItem>
<MenuItem value="No">No</MenuItem>
</Select>
</FormControl>
</div>
</AccordionDetails>
</Accordion>
{/* Professional Details */}
<Accordion
expanded={expandedSections.professional}
onChange={handleAccordionChange("professional")}
className="mb-4"
>
<AccordionSummary expandIcon={<ChevronDown />}>
<Typography className="font-semibold bg-gray-100 -mx-4 -my-2 px-4 py-2 w-full">
Professional Details
</Typography>
</AccordionSummary>
<AccordionDetails>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
<FormControl fullWidth>
<InputLabel>Occupation</InputLabel>
<Select
value={filters.occupation}
label="Occupation"
onChange={(e) => {
dispatch(updateFilter({ occupation: e.target.value }));
handleSelectionChange("Occupation", e.target.value);
}}
>
<MenuItem value="Any">Any</MenuItem>
<MenuItem value="Software Engineer">
Software Engineer
</MenuItem>
<MenuItem value="Doctor">Doctor</MenuItem>
</Select>
</FormControl>
<FormControl fullWidth>
<InputLabel>Annual Income</InputLabel>
<Select
value={filters.annualIncome}
label="Annual Income"
onChange={(e) => {
dispatch(updateFilter({ annualIncome: e.target.value }));
handleSelectionChange("Annual Income", e.target.value);
}}
>
<MenuItem value="Any">Any</MenuItem>
<MenuItem value="0-5 Lakhs">0-5 Lakhs</MenuItem>
<MenuItem value="5-10 Lakhs">5-10 Lakhs</MenuItem>
</Select>
</FormControl>
<FormControl fullWidth>
<InputLabel>Employee Type</InputLabel>
<Select
value={filters.employeeType}
label="Employee Type"
onChange={(e) => {
dispatch(updateFilter({ employeeType: e.target.value }));
handleSelectionChange("Employee Type", e.target.value);
}}
>
<MenuItem value="Any">Any</MenuItem>
<MenuItem value="Government">Government</MenuItem>
<MenuItem value="Private">Private</MenuItem>
</Select>
</FormControl>
<FormControl fullWidth>
<InputLabel>Education</InputLabel>
<Select
value={filters.education}
label="Education"
onChange={(e) => {
dispatch(updateFilter({ education: e.target.value }));
handleSelectionChange("Education", e.target.value);
}}
>
<MenuItem value="Any">Any</MenuItem>
<MenuItem value="Bachelor">Bachelor</MenuItem>
<MenuItem value="Master">Master</MenuItem>
</Select>
</FormControl>
</div>
</AccordionDetails>
</Accordion>
{/* Location Details */}
<Accordion
expanded={expandedSections.location}
onChange={handleAccordionChange("location")}
className="mb-4"
>
<AccordionSummary expandIcon={<ChevronDown />}>
<Typography className="font-semibold bg-gray-100 -mx-4 -my-2 px-4 py-2 w-full">
Location Details
</Typography>
</AccordionSummary>
<AccordionDetails>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
<FormControl fullWidth>
<InputLabel>State</InputLabel>
<Select
value={filters.state}
label="State"
onChange={(e) => {
dispatch(updateFilter({ state: e.target.value }));
handleSelectionChange("State", e.target.value);
}}
>
<MenuItem value="Any">Any</MenuItem>
<MenuItem value="Tamil Nadu">Tamil Nadu</MenuItem>
<MenuItem value="Karnataka">Karnataka</MenuItem>
</Select>
</FormControl>
<FormControl fullWidth>
<InputLabel>Country</InputLabel>
<Select
value={filters.country}
label="Country"
onChange={(e) => {
dispatch(updateFilter({ country: e.target.value }));
handleSelectionChange("Country", e.target.value);
}}
>
<MenuItem value="Any">Any</MenuItem>
<MenuItem value="India">India</MenuItem>
<MenuItem value="USA">USA</MenuItem>
</Select>
</FormControl>
<FormControl fullWidth>
<InputLabel>Citizenship</InputLabel>
<Select
value={filters.citizenship}
label="Citizenship"
onChange={(e) => {
dispatch(updateFilter({ citizenship: e.target.value }));
handleSelectionChange("Citizenship", e.target.value);
}}
>
<MenuItem value="Any">Any</MenuItem>
<MenuItem value="Indian">Indian</MenuItem>
<MenuItem value="US Citizen">US Citizen</MenuItem>
</Select>
</FormControl>
</div>
</AccordionDetails>
</Accordion>
{/* Lifestyle Details */}
<Accordion
expanded={expandedSections.lifestyle}
onChange={handleAccordionChange("lifestyle")}
className="mb-4"
>
<AccordionSummary expandIcon={<ChevronDown />}>
<Typography className="font-semibold bg-gray-100 -mx-4 -my-2 px-4 py-2 w-full">
Lifestyle Details
</Typography>
</AccordionSummary>
<AccordionDetails>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
<FormControl fullWidth>
<InputLabel>Eating Habits</InputLabel>
<Select
value={filters.eatingHabits}
label="Eating Habits"
onChange={(e) => {
dispatch(updateFilter({ eatingHabits: e.target.value }));
handleSelectionChange("Eating Habits", e.target.value);
}}
>
<MenuItem value="Any">Any</MenuItem>
<MenuItem value="Vegetarian">Vegetarian</MenuItem>
<MenuItem value="Non-Vegetarian">Non-Vegetarian</MenuItem>
</Select>
</FormControl>
<FormControl fullWidth>
<InputLabel>Smoking Habits</InputLabel>
<Select
value={filters.smokingHabits}
label="Smoking Habits"
onChange={(e) => {
dispatch(
updateFilter({ smokingHabits: e.target.value })
);
handleSelectionChange("Smoking Habits", e.target.value);
}}
>
<MenuItem value="Doesn't matter">Doesn't matter</MenuItem>
<MenuItem value="Non-Smoker">Non-Smoker</MenuItem>
<MenuItem value="Smoker">Smoker</MenuItem>
</Select>
</FormControl>
<FormControl fullWidth>
<InputLabel>Drinking Habits</InputLabel>
<Select
value={filters.drinkingHabits}
label="Drinking Habits"
onChange={(e) => {
dispatch(
updateFilter({ drinkingHabits: e.target.value })
);
handleSelectionChange("Drinking Habits", e.target.value);
}}
>
<MenuItem value="Doesn't matter">Doesn't matter</MenuItem>
<MenuItem value="Non-Drinker">Non-Drinker</MenuItem>
<MenuItem value="Social Drinker">Social Drinker</MenuItem>
</Select>
</FormControl>
</div>
</AccordionDetails>
</Accordion>
{/* Family Details */}
<Accordion
expanded={expandedSections.family}
onChange={handleAccordionChange("family")}
className="mb-4"
>
<AccordionSummary expandIcon={<ChevronDown />}>
<Typography className="font-semibold bg-gray-100 -mx-4 -my-2 px-4 py-2 w-full">
Family Details
</Typography>
</AccordionSummary>
<AccordionDetails>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
<FormControl fullWidth>
<InputLabel>Family Type</InputLabel>
<Select
value={filters.familyType}
label="Family Type"
onChange={(e) => {
dispatch(updateFilter({ familyType: e.target.value }));
handleSelectionChange("Family Type", e.target.value);
}}
>
<MenuItem value="Any">Any</MenuItem>
<MenuItem value="Nuclear">Nuclear</MenuItem>
<MenuItem value="Joint">Joint</MenuItem>
</Select>
</FormControl>
<FormControl fullWidth>
<InputLabel>Family Status</InputLabel>
<Select
value={filters.familyStatus}
label="Family Status"
onChange={(e) => {
dispatch(updateFilter({ familyStatus: e.target.value }));
handleSelectionChange("Family Status", e.target.value);
}}
>
<MenuItem value="Any">Any</MenuItem>
<MenuItem value="Middle Class">Middle Class</MenuItem>
<MenuItem value="Upper Middle Class">
Upper Middle Class
</MenuItem>
</Select>
</FormControl>
<FormControl fullWidth>
<InputLabel>Family Value</InputLabel>
<Select
value={filters.familyValue}
label="Family Value"
onChange={(e) => {
dispatch(updateFilter({ familyValue: e.target.value }));
handleSelectionChange("Family Value", e.target.value);
}}
>
<MenuItem value="Any">Any</MenuItem>
<MenuItem value="Traditional">Traditional</MenuItem>
<MenuItem value="Moderate">Moderate</MenuItem>
<MenuItem value="Liberal">Liberal</MenuItem>
</Select>
</FormControl>
</div>
</AccordionDetails>
</Accordion>
{/* Submit Button */}
<div className="mt-6 flex justify-center">
<Button
variant="contained"
size="large"
onClick={handleSubmit}
className="px-12"
>
Apply Filters
</Button>
</div>
</div>
</div>
</div>
);
};
export default FilterForm;

View File

@ -0,0 +1,50 @@
// FilterModal.jsx
import React, { useState } from "react";
import { IconButton, Dialog, DialogTitle, DialogContent, Box } from "@mui/material";
import { X, SlidersHorizontal } from "lucide-react";
import FilterForm from "./FilterForm";
const FilterModal = () => {
const [open, setOpen] = useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
return (
<>
{/* Filter Icon Button (place this in your page header / toolbar) */}
<IconButton
onClick={handleOpen}
aria-label="open filters"
color="primary"
size="large"
>
<SlidersHorizontal size={22} />
</IconButton>
{/* Modal */}
<Dialog
open={open}
onClose={handleClose}
fullWidth
maxWidth="lg" // adjust: "sm" | "md" | "lg"
>
<DialogTitle>
<Box display="flex" alignItems="center" justifyContent="space-between">
<span>Filter Options</span>
<IconButton onClick={handleClose} aria-label="close">
<X size={20} />
</IconButton>
</Box>
</DialogTitle>
<DialogContent dividers>
{/* Use your existing FilterForm here */}
<FilterForm />
</DialogContent>
</Dialog>
</>
);
};
export default FilterModal;

View File

@ -0,0 +1,116 @@
import React, { useEffect, useRef } from "react";
import { useDispatch, useSelector } from "react-redux";
import { updateLifestyleDetails } from "../redux/registrationFormSlice";
import {
Grid,
FormControl,
InputLabel,
Select,
MenuItem,
Button,
} from "@mui/material";
const LifestyleDetailsForm = ({ onSubmitStep, onSkipStep, errors }) => {
const dispatch = useDispatch();
const data = useSelector((state) => state.registerform.lifestyleDetails);
const inputRef = useRef(null);
useEffect(() => {
inputRef.current?.focus();
}, []);
const handleChange = (field, value) => {
dispatch(updateLifestyleDetails({ [field]: value }));
};
const handleSubmit = () => {
console.log("Submitting lifestyle details:", data);
onSubmitStep();
};
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: "hobbies",
label: "Hobbies & Interests",
options: ["Song", "Reading", "Sports", "Travel"],
},
];
return (
<div className="w-full max-w-[1200px] mx-auto bg-[#fff2f2] py-6 md:px-2 rounded-8">
<form noValidate autoComplete="off" style={{ padding: 16 }}>
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-20 gap-y-10 mb-6">
{fields.map(({ name, label, options }) => (
<div key={name} className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">{label}</label>
<FormControl
fullWidth
variant="outlined"
error={Boolean(errors[name])}
>
<InputLabel id={`${name}-label`}>Select {label}</InputLabel>
<Select
labelId={`${name}-label`}
label={`Select ${label}`}
name={name}
value={data[name]}
onChange={(e) => handleChange(name, e.target.value)}
inputRef={name === "diet" ? inputRef : null}
>
{options.map((opt) => (
<MenuItem key={opt} value={opt}>
{opt}
</MenuItem>
))}
</Select>
{errors[name] && (
<p
style={{
color: "#d32f2f",
margin: "3px 14px 0 14px",
fontSize: "0.75rem",
}}
>
{errors[name]}
</p>
)}
</FormControl>
</div>
))}
</div>
<Grid
item
xs={12}
style={{
marginTop: 24,
display: "flex",
gap: 16,
justifyContent: "center",
}}
>
<Button variant="outlined" onClick={onSkipStep}>
Skip
</Button>
<Button variant="contained" color="primary" onClick={handleSubmit}>
Next
</Button>
</Grid>
</form>
</div>
);
};
export default LifestyleDetailsForm;

View File

@ -0,0 +1,308 @@
import React, { useEffect, useRef } from "react";
import { useDispatch, useSelector } from "react-redux";
import { updatePartnerPreferences } from "../redux/registrationFormSlice";
import {
Grid,
TextField,
FormControl,
InputLabel,
Select,
MenuItem,
Button,
} from "@mui/material";
const PartnerPreferencesForm = ({ onSubmitStep, onSkipStep, errors }) => {
const dispatch = useDispatch();
const data = useSelector((state) => state.registerform.partnerPreferences);
const inputRef = useRef(null);
useEffect(() => {
inputRef.current?.focus();
}, []);
const handleChange = (field, value) => {
dispatch(updatePartnerPreferences({ [field]: value }));
};
const handleSubmit = () => {
console.log("Submitting partner preferences:", data);
onSubmitStep();
};
return (
<div className="w-full max-w-[1200px] mx-auto bg-[#fff2f2] py-6 md:px-2 rounded-8">
<form noValidate autoComplete="off" style={{ padding: 16 }}>
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-20 gap-y-10 mb-6">
{/* Preferred Age Range */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">
Preferred Age Range
</label>
<FormControl
fullWidth
variant="outlined"
error={Boolean(errors.ageRange)}
>
<InputLabel id="ageRange-label">
Select Preferred Age Range
</InputLabel>
<Select
labelId="ageRange-label"
label="Select Preferred Age Range"
name="ageRange"
value={data.ageRange}
onChange={(e) => handleChange("ageRange", e.target.value)}
displayEmpty
inputRef={inputRef}
>
<MenuItem value="">
<em>Select Preferred Age Range</em>
</MenuItem>
<MenuItem value="25 - 28">25 - 28</MenuItem>
<MenuItem value="29 - 32">29 - 32</MenuItem>
<MenuItem value="33 - 36">33 - 36</MenuItem>
</Select>
{errors.ageRange && (
<p
style={{
color: "#d32f2f",
margin: "3px 14px 0 14px",
fontSize: "0.75rem",
}}
>
{errors.ageRange}
</p>
)}
</FormControl>
</div>
{/* Religion / Caste Preference */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">
Religion / Caste Preference
</label>
<FormControl
fullWidth
variant="outlined"
error={Boolean(errors.religionCaste)}
>
<InputLabel id="religionCaste-label">
Select Religion / Caste Preference
</InputLabel>
<Select
labelId="religionCaste-label"
label="Select Religion / Caste Preference"
name="religionCaste"
value={data.religionCaste}
onChange={(e) =>
handleChange("religionCaste", e.target.value)
}
displayEmpty
>
<MenuItem value="">
<em>Select Religion / Caste Preference</em>
</MenuItem>
<MenuItem value="Hindu">Hindu</MenuItem>
<MenuItem value="Muslim">Muslim</MenuItem>
<MenuItem value="Christian">Christian</MenuItem>
<MenuItem value="Any">Any</MenuItem>
</Select>
{errors.religionCaste && (
<p
style={{
color: "#d32f2f",
margin: "3px 14px 0 14px",
fontSize: "0.75rem",
}}
>
{errors.religionCaste}
</p>
)}
</FormControl>
</div>
{/* Occupation Preference */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">
Occupation Preference
</label>
<FormControl
fullWidth
variant="outlined"
error={Boolean(errors.occupationPref)}
>
<InputLabel id="occupationPref-label">
Select Occupation Preference
</InputLabel>
<Select
labelId="occupationPref-label"
label="Select Occupation Preference"
name="occupationPref"
value={data.occupationPref}
onChange={(e) =>
handleChange("occupationPref", e.target.value)
}
displayEmpty
>
<MenuItem value="">
<em>Select Occupation Preference</em>
</MenuItem>
<MenuItem value="IT, Business">IT, Business</MenuItem>
<MenuItem value="Doctor">Doctor</MenuItem>
<MenuItem value="Engineer">Engineer</MenuItem>
<MenuItem value="Any">Any</MenuItem>
</Select>
{errors.occupationPref && (
<p
style={{
color: "#d32f2f",
margin: "3px 14px 0 14px",
fontSize: "0.75rem",
}}
>
{errors.occupationPref}
</p>
)}
</FormControl>
</div>
{/* Lifestyle & Habits Preference */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">
Lifestyle & Habits Preference
</label>
<FormControl
fullWidth
variant="outlined"
error={Boolean(errors.lifestylePref)}
>
<InputLabel id="lifestylePref-label">
Select Lifestyle & Habits Preference
</InputLabel>
<Select
labelId="lifestylePref-label"
label="Select Lifestyle & Habits Preference"
name="lifestylePref"
value={data.lifestylePref}
onChange={(e) =>
handleChange("lifestylePref", e.target.value)
}
displayEmpty
>
<MenuItem value="">
<em>Select Lifestyle & Habits Preference</em>
</MenuItem>
<MenuItem value="Non-Smoking, Non-Drinking">
Non-Smoking, Non-Drinking
</MenuItem>
<MenuItem value="Social Drinker">Social Drinker</MenuItem>
<MenuItem value="Any">Any</MenuItem>
</Select>
{errors.lifestylePref && (
<p
style={{
color: "#d32f2f",
margin: "3px 14px 0 14px",
fontSize: "0.75rem",
}}
>
{errors.lifestylePref}
</p>
)}
</FormControl>
</div>
{/* Prefer Qualification */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">
Prefer Qualification
</label>
<TextField
fullWidth
name="qualificationPref"
label="Enter prefer Qualification"
value={data.qualificationPref}
onChange={(e) =>
handleChange("qualificationPref", e.target.value)
}
error={Boolean(errors.qualificationPref)}
helperText={errors.qualificationPref}
variant="outlined"
/>
</div>
{/* Occupation (text) */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Occupation</label>
<TextField
fullWidth
name="occupationText"
label="Enter Occupation"
value={data.occupationText}
onChange={(e) =>
handleChange("occupationText", e.target.value)
}
error={Boolean(errors.occupationText)}
helperText={errors.occupationText}
variant="outlined"
/>
</div>
{/* Prefer Annual Income */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">
Prefer Annual Income
</label>
<TextField
fullWidth
name="incomePref"
label="Enter Annual Income"
value={data.incomePref}
onChange={(e) => handleChange("incomePref", e.target.value)}
error={Boolean(errors.incomePref)}
helperText={errors.incomePref}
variant="outlined"
/>
</div>
{/* Prefer Location */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">
Prefer Location
</label>
<TextField
fullWidth
name="locationPref"
label="Enter Prefer Location"
value={data.locationPref}
onChange={(e) => handleChange("locationPref", e.target.value)}
error={Boolean(errors.locationPref)}
helperText={errors.locationPref}
variant="outlined"
/>
</div>
</div>
<Grid
item
xs={12}
style={{
marginTop: 24,
display: "flex",
gap: 16,
justifyContent: "center",
}}
>
<Button variant="outlined" onClick={onSkipStep}>
Skip
</Button>
<Button variant="contained" color="primary" onClick={handleSubmit}>
Next
</Button>
</Grid>
</form>
</div>
);
};
export default PartnerPreferencesForm;

View File

@ -0,0 +1,777 @@
import React, { useCallback, useEffect, useRef, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { updatePersonalDetails } from "../redux/registrationFormSlice";
import {
Grid,
TextField,
FormControl,
InputLabel,
Select,
MenuItem,
Button,
Box,
Typography,
Link,
} from "@mui/material";
import AdvancedDropzone from "./AdvancedDropzone";
import { DatePicker } from "@mui/x-date-pickers/DatePicker";
import { LocalizationProvider } from "@mui/x-date-pickers";
import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFns";
const OTP_LENGTH = 4;
const OTP_TIMER_SEC = 120; // 2 minutes
const PersonalDetailsForm = ({ onSubmitStep, onSkipStep, errors }) => {
const dispatch = useDispatch();
const data = useSelector((state) => state.registerform.personalDetails);
const nameInputRef = useRef(null);
const [showOtp, setShowOtp] = useState(false);
const [otp, setOtp] = useState(new Array(OTP_LENGTH).fill(""));
const [otpTimer, setOtpTimer] = useState(0);
const [otpError, setOtpError] = useState("");
const [mobileOtpVerified, setMobileOtpVerified] = useState(false);
const [mobileNumberError, setMobileNumberError] = useState("");
const startOtpTimer = useCallback(() => {
setOtpTimer(OTP_TIMER_SEC);
}, []);
useEffect(() => {
if (otpTimer <= 0) return;
const timerId = setInterval(() => {
setOtpTimer((sec) => sec - 1);
}, 1000);
return () => clearInterval(timerId);
}, [otpTimer]);
useEffect(() => {
nameInputRef.current?.focus();
}, []);
const handleOtpChange = (index, value) => {
if (!/^\d*$/.test(value)) return;
const newOtp = [...otp];
newOtp[index] = value.slice(-1);
setOtp(newOtp);
if (value && index < OTP_LENGTH - 1) {
const next = document.getElementById(`otp-${index + 1}`);
if (next) next.focus();
}
};
const resetOtp = () => {
setOtp(new Array(OTP_LENGTH).fill(""));
setOtpError("");
startOtpTimer();
};
const handleMobileSubmit = () => {
if (!data.mobileNumber || data.mobileNumber.length !== 10) {
setMobileNumberError(
"Please enter a valid 10-digit mobile number before sending OTP"
);
return;
}
setMobileNumberError("");
setShowOtp(true);
resetOtp();
setMobileOtpVerified(false);
};
const handleChange = (field, value) => {
dispatch(updatePersonalDetails({ [field]: value }));
if (field === "mobileNumber") {
setMobileNumberError("");
setShowOtp(false);
setOtp(new Array(OTP_LENGTH).fill(""));
setOtpError("");
setOtpTimer(0);
setMobileOtpVerified(false);
}
};
const isOtpComplete = otp.every((digit) => digit !== "");
// Simulated API OTP verification (replace with actual)
const verifyOtpApi = async (mobile, otpValue) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (otpValue === "1234") resolve(true);
else reject("Invalid OTP");
}, 1000);
});
};
const handleOtpSubmit = async () => {
if (!isOtpComplete) {
setOtpError("Complete OTP is required");
return;
}
try {
await verifyOtpApi(data.mobileNumber, otp.join(""));
setMobileOtpVerified(true);
setMobileNumberError("");
} catch (error) {
setOtpError(error || "OTP verification failed");
}
};
// file upload
// const handleSubmit = async () => {
// if (showOtp && !isOtpComplete) {
// setOtpError("OTP is required and must be complete");
// return;
// }
// if (showOtp && !mobileOtpVerified) {
// try {
// await verifyOtpApi(data.mobileNumber, otp.join(""));
// setMobileOtpVerified(true);
// setOtpError("");
// onSubmitStep();
// console.log("OTP verified on submit");
// } catch (err) {
// setOtpError(err || "OTP verification failed");
// }
// return;
// }
// onSubmitStep();
// };
const handleSubmit = async () => {
if (showOtp && !isOtpComplete) {
setOtpError("OTP is required and must be complete");
return;
}
if (showOtp && !mobileOtpVerified) {
try {
await verifyOtpApi(data.mobileNumber, otp.join(""));
setMobileOtpVerified(true);
setOtpError("");
console.log("Submitting personal details:", data); // log here
onSubmitStep();
console.log("OTP verified on submit");
} catch (err) {
setOtpError(err || "OTP verification failed");
}
return;
}
// no OTP or already verified
console.log("Submitting personal details:", data); // log here
onSubmitStep();
};
const formatTimer = (sec) => {
const m = Math.floor(sec / 60)
.toString()
.padStart(2, "0");
const s = (sec % 60).toString().padStart(2, "0");
return `${m}:${s}`;
};
const parseDobValue = data.dob ? new Date(data.dob) : null;
return (
<>
<div className="w-full max-w-[1200px] mx-auto bg-[#fff2f2] py-6 md:px-2 rounded-8">
<form noValidate autoComplete="off" style={{ padding: 16 }}>
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-20 gap-y-10 mb-6">
{/* Name */}
<div className="flex flex-col gap-4">
<label className="text-gray-900 text-[17px]">
Enter the Name
</label>
<TextField
fullWidth
inputRef={nameInputRef}
name="name"
label="Name"
value={data.name}
onChange={(e) => handleChange("name", e.target.value)}
error={Boolean(errors.name)}
helperText={errors.name}
placeholder="Enter Name"
variant="outlined"
/>
</div>
{/* Gender */}
<div className="flex flex-col gap-6">
<label className="text-gray-900 text-[17px]">
Enter the Gender
</label>
<FormControl
fullWidth
variant="outlined"
error={Boolean(errors.gender)}
>
<InputLabel id="gender-label">Gender</InputLabel>
<Select
labelId="gender-label"
label="Gender"
name="gender"
value={data.gender}
onChange={(e) => handleChange("gender", e.target.value)}
>
<MenuItem value="Male">Male</MenuItem>
<MenuItem value="Female">Female</MenuItem>
</Select>
{errors.gender && (
<p
style={{
color: "#d32f2f",
margin: "3px 14px 0 14px",
fontSize: "0.75rem",
}}
>
{errors.gender}
</p>
)}
</FormControl>
</div>
{/* Mobile Number and Send OTP Button */}
<div className="flex flex-col gap-6 relative">
<TextField
fullWidth
name="mobileNumber"
label="Mobile Number"
type="tel"
value={data.mobileNumber}
onChange={(e) => handleChange("mobileNumber", e.target.value)}
error={
Boolean(errors.mobileNumber) || Boolean(mobileNumberError)
}
helperText={mobileNumberError || errors.mobileNumber}
placeholder="Enter Mobile Number"
inputProps={{ maxLength: 10 }}
variant="outlined"
disabled={mobileOtpVerified}
// sx={{ maxWidth: "430px" }}
/>
<Box sx={{ position: "absolute", right: 0 }}>
{!showOtp && !mobileOtpVerified && (
<Button
variant="outlined"
color="primary"
onClick={handleMobileSubmit}
sx={{
padding: "15px 10px",
background: "green",
color: "#fff",
}}
>
Send OTP
</Button>
)}
</Box>
</div>
{/* OTP Inputs */}
<div className="flex flex-col gap-6">
{showOtp && !mobileOtpVerified && (
<>
<Box display="flex" gap={1} alignItems="center">
{otp.map((digit, index) => (
<TextField
key={index}
id={`otp-${index}`}
inputProps={{
maxLength: 1,
style: {
textAlign: "center",
width: 40,
fontSize: 20,
},
}}
value={digit}
onChange={(e) => handleOtpChange(index, e.target.value)}
error={Boolean(otpError)}
autoFocus={index === 0}
variant="outlined"
/>
))}
<Button
variant="contained"
color="secondary"
onClick={handleOtpSubmit} // new handler to verify OTP
sx={{ height: 40, ml: 2 }}
disabled={!isOtpComplete}
>
Submit OTP
</Button>
</Box>
<Typography sx={{ ml: 2, minWidth: 56 }}>
{otpTimer > 0 ? (
formatTimer(otpTimer)
) : (
<Link
component="button"
variant="body2"
onClick={resetOtp}
disabled={otpTimer > 0}
>
Resend OTP
</Link>
)}
</Typography>
{otpError && (
<Typography color="error" variant="caption" sx={{ mt: 1 }}>
{otpError}
</Typography>
)}
</>
)}
{mobileOtpVerified && (
<Typography
color="primary"
variant="body2"
sx={{
background: "green",
padding: "17px 15px",
width: "fit-content",
borderRadius: "5px",
color: "#fff",
fontSize: "18px",
}}
>
Mobile number verified
</Typography>
)}
</div>
{/* Other fields like DOB, height, marital status, etc. */}
{/* Your other inputs here */}
{/* DOB */}
{/* <div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Date of Birth</label>
<TextField
fullWidth
name="dob"
type="date"
value={data.dob}
onChange={(e) => handleChange("dob", e.target.value)}
error={Boolean(errors.dob)}
helperText={errors.dob}
InputLabelProps={{ shrink: true }}
variant="outlined"
/>
</div> */}
{/* DOB with MUI DatePicker */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Date of Birth</label>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
format="dd/MM/yyyy"
value={parseDobValue}
onChange={(value) => {
let formatted = "";
if (value instanceof Date && !isNaN(value)) {
const y = value.getFullYear();
const m = String(value.getMonth() + 1).padStart(2, "0");
const d = String(value.getDate()).padStart(2, "0");
formatted = `${y}-${m}-${d}`;
}
handleChange("dob", formatted);
}}
slotProps={{
textField: {
fullWidth: true,
error: Boolean(errors.dob),
helperText: errors.dob,
},
}}
/>
</LocalizationProvider>
</div>
{/* Height */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Height</label>
<TextField
fullWidth
name="height"
label="Enter Height"
value={data.height}
onChange={(e) => handleChange("height", e.target.value)}
error={Boolean(errors.height)}
helperText={errors.height}
variant="outlined"
/>
</div>
{/* Weight */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Weight</label>
<TextField
fullWidth
name="weight"
label="Enter Weight"
value={data.weight}
onChange={(e) => handleChange("weight", e.target.value)}
error={Boolean(errors.weight)}
helperText={errors.weight}
variant="outlined"
/>
</div>
{/* Marital Status */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Marital Status</label>
<FormControl
fullWidth
variant="outlined"
error={Boolean(errors.maritalStatus)}
>
<InputLabel id="maritalStatus-label">
Select Marital Status
</InputLabel>
<Select
labelId="maritalStatus-label"
label="Select Marital Status"
name="maritalStatus"
value={data.maritalStatus}
onChange={(e) =>
handleChange("maritalStatus", e.target.value)
}
>
<MenuItem value="Single">Single</MenuItem>
<MenuItem value="Divorced">Divorced</MenuItem>
<MenuItem value="Widowed">Widowed</MenuItem>
</Select>
{errors.maritalStatus && (
<Typography
color="error"
variant="caption"
sx={{ mt: 0.5, ml: 1.8 }}
>
{errors.maritalStatus}
</Typography>
)}
</FormControl>
</div>
{/* Religion */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Religion</label>
<FormControl
fullWidth
variant="outlined"
error={Boolean(errors.religion)}
>
<InputLabel id="religion-label">Select Religion</InputLabel>
<Select
labelId="religion-label"
label="Select Religion"
name="religion"
value={data.religion}
onChange={(e) => handleChange("religion", e.target.value)}
>
<MenuItem value="Hindu">Hindu</MenuItem>
<MenuItem value="Muslim">Muslim</MenuItem>
<MenuItem value="Christian">Christian</MenuItem>
<MenuItem value="Others">Others</MenuItem>
</Select>
{errors.religion && (
<Typography
color="error"
variant="caption"
sx={{ mt: 0.5, ml: 1.8 }}
>
{errors.religion}
</Typography>
)}
</FormControl>
</div>
{/* Caste / Community */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">
Caste / Community
</label>
<FormControl
fullWidth
variant="outlined"
error={Boolean(errors.caste)}
>
<InputLabel id="caste-label">Select Caste / Community</InputLabel>
<Select
labelId="caste-label"
label="Select Caste / Community"
name="caste"
value={data.caste}
onChange={(e) => handleChange("caste", e.target.value)}
>
<MenuItem value="Agamudayar">Agamudayar</MenuItem>
<MenuItem value="Brahmin">Brahmin</MenuItem>
<MenuItem value="Others">Others</MenuItem>
</Select>
{errors.caste && (
<Typography
color="error"
variant="caption"
sx={{ mt: 0.5, ml: 1.8 }}
>
{errors.caste}
</Typography>
)}
</FormControl>
</div>
{/* Sub-Caste (optional) */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">
Sub-Caste (optional)
</label>
<FormControl fullWidth variant="outlined">
<InputLabel id="subCaste-label">
Select Sub-Caste (optional)
</InputLabel>
<Select
labelId="subCaste-label"
label="Select Sub-Caste (optional)"
name="subCaste"
value={data.subCaste}
onChange={(e) => handleChange("subCaste", e.target.value)}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value="Thuluva vellala">Thuluva vellala</MenuItem>
</Select>
</FormControl>
</div>
{/* Gothram (optional) */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">
Gothram (optional)
</label>
<FormControl fullWidth variant="outlined">
<InputLabel id="gothram-label">
Select Gothram (optional)
</InputLabel>
<Select
labelId="gothram-label"
label="Select Gothram (optional)"
name="gothram"
value={data.gothram}
onChange={(e) => handleChange("gothram", e.target.value)}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value="Siva Gotheram">Siva Gotheram</MenuItem>
</Select>
</FormControl>
</div>
{/* Blood Group (optional) */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">
Blood Group (optional)
</label>
<FormControl fullWidth variant="outlined">
<InputLabel id="blood-label">
Select Blood Group (optional)
</InputLabel>
<Select
labelId="blood-label"
label="Select Blood Group (optional)"
name="bloodGroup"
value={data.bloodGroup}
onChange={(e) => handleChange("bloodGroup", e.target.value)}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value="O+">O+</MenuItem>
<MenuItem value="A+">A+</MenuItem>
<MenuItem value="B+">B+</MenuItem>
<MenuItem value="AB+">AB+</MenuItem>
</Select>
</FormControl>
</div>
{/* Email Id */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Email Id</label>
<TextField
fullWidth
name="email"
label="Enter Email Id"
value={data.email}
onChange={(e) => handleChange("email", e.target.value)}
error={Boolean(errors.email)}
helperText={errors.email}
variant="outlined"
/>
</div>
{/* State */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">State</label>
<FormControl
fullWidth
variant="outlined"
error={Boolean(errors.state)}
>
<InputLabel id="state-label">Select State</InputLabel>
<Select
labelId="state-label"
label="Select State"
name="state"
value={data.state}
onChange={(e) => handleChange("state", e.target.value)}
>
<MenuItem value="Tamil Nadu">Tamil Nadu</MenuItem>
<MenuItem value="Kerala">Kerala</MenuItem>
<MenuItem value="Karnataka">Karnataka</MenuItem>
</Select>
{errors.state && (
<Typography
color="error"
variant="caption"
sx={{ mt: 0.5, ml: 1.8 }}
>
{errors.state}
</Typography>
)}
</FormControl>
</div>
{/* City */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">City</label>
<FormControl
fullWidth
variant="outlined"
error={Boolean(errors.city)}
>
<InputLabel id="city-label">Select City</InputLabel>
<Select
labelId="city-label"
label="Select City"
name="city"
value={data.city}
onChange={(e) => handleChange("city", e.target.value)}
>
<MenuItem value="Chennai">Chennai</MenuItem>
<MenuItem value="Coimbatore">Coimbatore</MenuItem>
<MenuItem value="Madurai">Madurai</MenuItem>
</Select>
{errors.city && (
<Typography
color="error"
variant="caption"
sx={{ mt: 0.5, ml: 1.8 }}
>
{errors.city}
</Typography>
)}
</FormControl>
</div>
{/* Pin code */}
<div className="flex flex-col gap-2">
<label className="text-gray-900 text-[15px]">Pin code</label>
<TextField
fullWidth
name="pincode"
label="Enter Pin code"
value={data.pincode}
onChange={(e) => handleChange("pincode", e.target.value)}
error={Boolean(errors.pincode)}
helperText={errors.pincode}
inputProps={{ maxLength: 6 }}
variant="outlined"
/>
</div>
<div className="flex flex-col gap-2">
{/* Upload Profile (UI only) */}
{/* <div className="flex flex-col gap-2 md:col-span-2 w-full max-w-[900px] ">
<label className="text-gray-900 text-[15px]">
Upload Profile (Multi - Select)
</label>
<div className=" border-2 border-dashed border-gray-300 rounded-lg p-6 text-center bg-white">
<div className="text-3xl mb-2"></div>
<p className="text-sm text-gray-600">Upload PDF, IMG, JPG</p>
</div>
</div> */}
<div className="flex flex-col gap-2 md:col-span-2 w-full">
<label className="text-gray-900 text-[15px]">
Upload Profile (Multi - Select)
</label>
<AdvancedDropzone
value={data.profiles || []}
onChange={(files) => {
// if you want to keep in Redux as plain metadata
dispatch(updatePersonalDetails({ profiles: files }));
}}
/>
</div>
</div>
</div>
<Grid
item
xs={12}
style={{
marginTop: "40px",
display: "flex",
gap: 16,
justifyContent: "center",
}}
>
<Button
variant="outlined"
onClick={onSkipStep}
disabled={mobileOtpVerified}
>
Skip
</Button>
<Button
variant="contained"
color="primary"
onClick={handleSubmit}
// disabled={!mobileOtpVerified}
>
Submit & Next
</Button>
</Grid>
</form>
</div>
</>
);
};
export default PersonalDetailsForm;

View File

@ -0,0 +1,124 @@
import React from 'react';
import { useSelector } from 'react-redux';
import { Edit2 } from 'lucide-react';
import {
Card,
CardContent,
CardHeader,
Typography,
Box,
Divider,
IconButton,
Button,
Grid,
} from '@mui/material';
const PreviewScreen = ({ onEdit, onSubmit }) => {
const formData = useSelector((state) => state.registerform);
const sections = [
{
title: 'Personal Details',
step: 1,
data: formData.personalDetails,
},
{
title: 'Educational & Professional Details',
step: 2,
data: formData.educationalDetails,
},
{
title: 'Family Details',
step: 3,
data: formData.familyDetails,
},
{
title: 'Lifestyle & Habits',
step: 4,
data: formData.lifestyleDetails,
},
{
title: 'Partner Preferences',
step: 5,
data: formData.partnerPreferences,
},
];
return (
<Box p={3} maxWidth={720} mx="auto" minHeight="100vh" bgcolor="#f9fafb">
<Grid container spacing={3}>
{sections.map((section) => (
<Grid item xs={12} key={section.title}>
<Card variant="outlined" sx={{ borderRadius: 2 }}>
<CardHeader
title={
<Typography variant="h6" fontWeight="bold">
{section.title}
</Typography>
}
action={
<IconButton
aria-label="edit"
color="primary"
onClick={() => onEdit(section.step)}
size="large"
>
<Edit2 size={20} />
</IconButton>
}
sx={{ pb: 0 }}
/>
<Divider />
<CardContent sx={{ pt: 1 }}>
{Object.entries(section.data).map(([key, value]) => {
if (value && key !== 'profiles') {
// Convert camelCase or camel_Snake_case to readable words
const formattedKey = key
.replace(/([A-Z])/g, ' $1')
.replace(/_/g, ' ')
.replace(/\b\w/g, (l) => l.toUpperCase())
.trim();
return (
<Box
key={key}
display="flex"
justifyContent="space-between"
py={0.7}
borderBottom="1px solid #e0e0e0"
>
<Typography color="text.secondary" sx={{ fontWeight: 500 }}>
{formattedKey}:
</Typography>
<Typography
sx={{ fontWeight: 600, ml: 2, maxWidth: '60%', wordBreak: 'break-word' }}
>
{value}
</Typography>
</Box>
);
}
return null;
})}
</CardContent>
</Card>
</Grid>
))}
<Grid item xs={12}>
<Button
variant="contained"
color="success"
fullWidth
size="large"
onClick={onSubmit}
sx={{ borderRadius: 2 }}
>
Submit
</Button>
</Grid>
</Grid>
</Box>
);
};
export default PreviewScreen;

329
src/feature/StepperForm.jsx Normal file
View File

@ -0,0 +1,329 @@
import React, { useState } from "react";
import { ChevronLeft } from "lucide-react";
import { useDispatch, useSelector } from "react-redux";
import {
updatePersonalDetails,
updateEducationalDetails,
updateFamilyDetails,
updateLifestyleDetails,
updatePartnerPreferences,
submitForm,
} from "../redux/registrationFormSlice";
import PersonalDetailsForm from "./PersonalDetailsForm";
import EducationalDetailsForm from "./EducationalDetailsForm";
import FamilyDetailsForm from "./FamilyDetailsForm";
import LifestyleDetailsForm from "./LifestyleDetailsForm";
import PartnerPreferencesForm from "./PartnerPreferencesForm";
import PreviewScreen from "./PreviewScreen";
const Stepper = ({ currentStep, onStepClick }) => {
const steps = [
{ num: 1, label: "Personal" },
{ num: 2, label: "Educational" },
{ num: 3, label: "Family" },
{ num: 4, label: "Lifestyle" },
{ num: 5, label: "Partner" },
{ num: 6, label: "Preview" },
];
return (
<div className="flex items-center justify-between px-4 py-6">
{steps.map((step, index) => (
<React.Fragment key={step.num}>
<div
className="flex flex-col items-center cursor-pointer"
onClick={() => onStepClick(step.num)}
>
<div
className={`w-8 h-8 rounded-full flex items-center justify-center text-sm font-semibold ${
currentStep >= step.num
? "bg-red-600 text-white"
: "bg-gray-300 text-gray-600"
}`}
>
{step.num}
</div>
</div>
{index < steps.length - 1 && (
<div
className={`flex-1 h-0.5 mx-1 ${
currentStep > step.num ? "bg-red-600" : "bg-gray-300"
}`}
/>
)}
</React.Fragment>
))}
</div>
);
};
const StepperForm = () => {
const dispatch = useDispatch();
const personalDetails = useSelector(
(state) => state.registerform.personalDetails
);
const educationalDetails = useSelector(
(state) => state.registerform.educationalDetails
);
const familyDetails = useSelector((state) => state.registerform.familyDetails);
const lifestyleDetails = useSelector(
(state) => state.registerform.lifestyleDetails
);
const partnerPreferences = useSelector(
(state) => state.registerform.partnerPreferences
);
const [currentStep, setCurrentStep] = useState(1);
const [errors, setErrors] = useState({});
const validateStep = (step) => {
const newErrors = {};
if (step === 1) {
const required = [
"name",
"mobileNumber",
"gender",
"dob",
"height",
"weight",
"maritalStatus",
"religion",
"caste",
"email",
"state",
"city",
"pincode",
];
required.forEach((field) => {
if (!personalDetails[field]) {
newErrors[field] = "This field is required";
}
});
if (
personalDetails.email &&
!/\S+@\S+\.\S+/.test(personalDetails.email)
) {
newErrors.email = "Invalid email format";
}
if (
personalDetails.mobileNumber &&
personalDetails.mobileNumber.length !== 10
) {
newErrors.mobileNumber = "Mobile number must be 10 digits";
}
} else if (step === 2) {
const required = [
"qualification",
"fieldOfStudy",
"occupation",
"organization",
"income",
"workLocation",
];
required.forEach((field) => {
if (!educationalDetails[field]) {
newErrors[field] = "This field is required";
}
});
} else if (step === 3) {
const required = [
"fatherName",
"fatherOccupation",
"motherName",
"motherOccupation",
"siblings",
"siblingsStatus",
"familyType",
"nativePlace",
];
required.forEach((field) => {
if (!familyDetails[field]) {
newErrors[field] = "This field is required";
}
});
} else if (step === 4) {
const required = ["diet", "drinking", "smoking", "hobbies"];
required.forEach((field) => {
if (!lifestyleDetails[field]) {
newErrors[field] = "This field is required";
}
});
} else if (step === 5) {
const required = [
"ageRange",
"religionCaste",
"occupationPref",
"lifestylePref",
"qualificationPref",
"occupationText",
"incomePref",
"locationPref",
];
required.forEach((field) => {
if (!partnerPreferences[field]) {
newErrors[field] = "This field is required";
}
});
}
setErrors(newErrors);
// Autofocus first invalid field
if (Object.keys(newErrors).length > 0) {
const firstKey = Object.keys(newErrors)[0];
const el = document.querySelector(`[name="${firstKey}"]`);
if (el) el.focus();
}
return Object.keys(newErrors).length === 0;
};
// API call simulation for each step submit
const submitStepAPI = async (step) => {
// Replace with actual API call
return new Promise((resolve) => setTimeout(resolve, 500));
};
const handleStepSubmit = async () => {
const isValid = validateStep(currentStep);
if (!isValid) return;
try {
await submitStepAPI(currentStep);
setCurrentStep((prev) => Math.min(prev + 1, 6));
window.scrollTo(0, 0);
} catch (e) {
alert("Failed to submit step. Please try again.");
}
};
const handleSkip = () => {
setErrors({});
setCurrentStep((prev) => Math.min(prev + 1, 6));
window.scrollTo(0, 0);
};
const handleStepClick = (step) => {
setCurrentStep(step);
setErrors({});
window.scrollTo(0, 0);
};
const handleEdit = (step) => {
setCurrentStep(step);
setErrors({});
window.scrollTo(0, 0);
};
const handleFinalSubmit = async () => {
for (let i = 1; i <= 5; i++) {
const ok = validateStep(i);
if (!ok) {
setCurrentStep(i);
return;
}
}
try {
// final combined API call - replace with your final API
await new Promise((resolve) => setTimeout(resolve, 500));
alert("Form submitted successfully!");
} catch (e) {
alert("Failed to submit form.");
}
};
const renderStepContent = () => {
switch (currentStep) {
case 1:
return (
<PersonalDetailsForm
onSubmitStep={handleStepSubmit}
onSkipStep={handleSkip}
errors={errors}
/>
);
case 2:
return (
<EducationalDetailsForm
onSubmitStep={handleStepSubmit}
onSkipStep={handleSkip}
errors={errors}
/>
);
case 3:
return (
<FamilyDetailsForm
onSubmitStep={handleStepSubmit}
onSkipStep={handleSkip}
errors={errors}
/>
);
case 4:
return (
<LifestyleDetailsForm
onSubmitStep={handleStepSubmit}
onSkipStep={handleSkip}
errors={errors}
/>
);
case 5:
return (
<PartnerPreferencesForm
onSubmitStep={handleStepSubmit}
onSkipStep={handleSkip}
errors={errors}
/>
);
case 6:
return <PreviewScreen onEdit={handleEdit} onSubmit={handleFinalSubmit} />;
default:
return null;
}
};
const getTitle = () => {
const titles = {
1: "Personal Details",
2: "Educational & Professional Details",
3: "Family Details",
4: "Lifestyle & Habits",
5: "Partner Preferences",
6: "Details Preview",
};
return titles[currentStep] || "";
};
return (
<div className="">
<div className="max-w-[1400px] mx-auto bg-white ">
{/* Header */}
<div className="my-4 rounded-[10px] py-4 bg-[#e3ffed] w-full max-w-[1200px] mx-auto">
<Stepper currentStep={currentStep} onStepClick={handleStepClick} />
<div className="flex items-center p-4 justify-center">
{currentStep > 1 && currentStep < 6 && (
<button
onClick={() => setCurrentStep((prev) => prev - 1)}
className="mr-3"
>
<ChevronLeft size={24} />
</button>
)}
<h1 className="text-[24px] font-semibold text-center uppercase bg-[#fff2f2] py-2 px-3 rounded-5">{getTitle()}</h1>
</div>
</div>
{/* Content */}
<div className="pb-6">{renderStepContent()}</div>
</div>
</div>
);
};
export default StepperForm;

View File

@ -5,10 +5,15 @@ import "./index.css";
import App from "./App.jsx";
import { ThemeProvider } from "@mui/material/styles";
import theme from "./theme";
import { Provider } from "react-redux";
import { store } from "./redux/store.js";
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<ThemeProvider theme={theme}>
<Provider store={store}>
<App />
</Provider>
</ThemeProvider>
</React.StrictMode>
);

140
src/redux/filterSlice.jsx Normal file
View File

@ -0,0 +1,140 @@
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
age: [18, 70],
height: [4.0, 7.11],
maritalStatus: "All Profile",
motherTongue: [],
religion: "Hindu",
matchesWithHoroscope: false,
caste: [],
subCaste: [],
star: "Any",
dasham: "Doesn't matter",
occupation: "Any",
annualIncome: "Any",
employeeType: "Any",
education: "Any",
state: "Any",
country: "Any",
citizenship: "Any",
eatingHabits: "Any",
smokingHabits: "Doesn't matter",
drinkingHabits: "Doesn't matter",
familyType: "Any",
familyStatus: "Any",
familyValue: "Any",
};
const filterSlice = createSlice({
name: "filters",
initialState,
reducers: {
setAge: (state, action) => {
state.age = action.payload;
},
setHeight: (state, action) => {
state.height = action.payload;
},
setMaritalStatus: (state, action) => {
state.maritalStatus = action.payload;
},
setMotherTongue: (state, action) => {
state.motherTongue = action.payload;
},
setReligion: (state, action) => {
state.religion = action.payload;
},
setMatchesWithHoroscope: (state, action) => {
state.matchesWithHoroscope = action.payload;
},
setCaste: (state, action) => {
state.caste = action.payload;
},
setSubCaste: (state, action) => {
state.subCaste = action.payload;
},
setStar: (state, action) => {
state.star = action.payload;
},
setDasham: (state, action) => {
state.dasham = action.payload;
},
setOccupation: (state, action) => {
state.occupation = action.payload;
},
setAnnualIncome: (state, action) => {
state.annualIncome = action.payload;
},
setEmployeeType: (state, action) => {
state.employeeType = action.payload;
},
setEducation: (state, action) => {
state.education = action.payload;
},
setState: (state, action) => {
state.state = action.payload;
},
setCountry: (state, action) => {
state.country = action.payload;
},
setCitizenship: (state, action) => {
state.citizenship = action.payload;
},
setEatingHabits: (state, action) => {
state.eatingHabits = action.payload;
},
setSmokingHabits: (state, action) => {
state.smokingHabits = action.payload;
},
setDrinkingHabits: (state, action) => {
state.drinkingHabits = action.payload;
},
setFamilyType: (state, action) => {
state.familyType = action.payload;
},
setFamilyStatus: (state, action) => {
state.familyStatus = action.payload;
},
setFamilyValue: (state, action) => {
state.familyValue = action.payload;
},
// universal update
updateFilter: (state, action) => {
return { ...state, ...action.payload };
},
resetFilters: () => initialState,
},
});
export const {
setAge,
setHeight,
setMaritalStatus,
setMotherTongue,
setReligion,
setMatchesWithHoroscope,
setCaste,
setSubCaste,
setStar,
setDasham,
setOccupation,
setAnnualIncome,
setEmployeeType,
setEducation,
setState,
setCountry,
setCitizenship,
setEatingHabits,
setSmokingHabits,
setDrinkingHabits,
setFamilyType,
setFamilyStatus,
setFamilyValue,
updateFilter,
resetFilters,
} = filterSlice.actions;
export default filterSlice.reducer;

View File

@ -0,0 +1,99 @@
import { createSlice } from "@reduxjs/toolkit";
const registrationformSlice = createSlice({
name: "registerform",
initialState: {
personalDetails: {
name: "",
mobileNumber: "",
gender: "",
dob: "",
height: "",
weight: "",
maritalStatus: "",
religion: "",
caste: "",
subCaste: "",
gothram: "",
bloodGroup: "",
email: "",
state: "",
city: "",
pincode: "",
profiles: [],
},
educationalDetails: {
qualification: "",
fieldOfStudy: "",
occupation: "",
organization: "",
income: "",
workLocation: "",
},
familyDetails: {
fatherName: "",
fatherOccupation: "",
motherName: "",
motherOccupation: "",
siblings: "",
siblingsStatus: "",
familyType: "",
nativePlace: "",
},
lifestyleDetails: {
diet: "",
drinking: "",
smoking: "",
hobbies: "",
},
partnerPreferences: {
ageRange: "",
religionCaste: "",
occupationPref: "",
lifestylePref: "",
qualificationPref: "",
occupationText: "",
incomePref: "",
locationPref: "",
},
},
reducers: {
updatePersonalDetails: (state, action) => {
state.personalDetails = { ...state.personalDetails, ...action.payload };
},
updateEducationalDetails: (state, action) => {
state.educationalDetails = { ...state.educationalDetails, ...action.payload };
},
updateFamilyDetails: (state, action) => {
state.familyDetails = { ...state.familyDetails, ...action.payload };
},
updateLifestyleDetails: (state, action) => {
state.lifestyleDetails = { ...state.lifestyleDetails, ...action.payload };
},
updatePartnerPreferences: (state, action) => {
state.partnerPreferences = { ...state.partnerPreferences, ...action.payload };
},
submitForm: (state) => {
console.log("Form Submitted:", {
personalDetails: state.personalDetails,
educationalDetails: state.educationalDetails,
familyDetails: state.familyDetails,
lifestyleDetails: state.lifestyleDetails,
partnerPreferences: state.partnerPreferences,
});
},
},
});
export const {
updatePersonalDetails,
updateEducationalDetails,
updateFamilyDetails,
updateLifestyleDetails,
updatePartnerPreferences,
submitForm,
} = registrationformSlice.actions;
export default registrationformSlice.reducer;

9
src/redux/store.js Normal file
View File

@ -0,0 +1,9 @@
import { configureStore } from "@reduxjs/toolkit";
import registerformReducer from "./registrationFormSlice";
import filtersReducer from "./filterSlice";
export const store = configureStore({
reducer: {
registerform:registerformReducer,
filters:filtersReducer,
},
});

View File

@ -15,11 +15,24 @@ import ChatUI from "../pages/ChatPage";
import HoroscopeGenerator from "../pages/HoroscoperGeneratePage";
import ContactUsPage from "../pages/ContactUsPage";
import ChangePasswordPage from "../components/auth/ChangePasswordForm";
import StepperForm from "../feature/StepperForm";
import FilterForm from "../feature/FilterForm";
const UserRoutes = () => {
return (
<>
<Route element={<ProfileLayout />}>
<Route path="/registration" element={<StepperForm />} />
</Route>
<Route element={<ProfileLayout />}>
<Route path="/filter" element={<FilterForm />} />
</Route>
<Route element={<ProfileLayout />}>
<Route path="/" element={<UserDashboardHome />} />
</Route>
<Route element={<ProfileLayout />}>
<Route path="/main/dashboard" element={<UserDashboardHome />} />
</Route>