form done
This commit is contained in:
parent
b1d9d46e4e
commit
d35bd08f5f
264
package-lock.json
generated
264
package-lock.json
generated
@ -8,21 +8,27 @@
|
|||||||
"name": "thirukalyanam",
|
"name": "thirukalyanam",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@date-io/date-fns": "^3.2.1",
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
"@emotion/styled": "^11.14.1",
|
"@emotion/styled": "^11.14.1",
|
||||||
|
"@files-ui/react": "^1.2.5",
|
||||||
"@lottiefiles/dotlottie-react": "^0.17.8",
|
"@lottiefiles/dotlottie-react": "^0.17.8",
|
||||||
"@mui/icons-material": "^7.3.5",
|
"@mui/icons-material": "^7.3.5",
|
||||||
"@mui/lab": "^7.0.1-beta.19",
|
"@mui/lab": "^7.0.1-beta.19",
|
||||||
"@mui/material": "^7.3.5",
|
"@mui/material": "^7.3.5",
|
||||||
"@mui/styled-engine-sc": "^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",
|
"@tailwindcss/vite": "^4.1.17",
|
||||||
"axios": "^1.13.2",
|
"axios": "^1.13.2",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
"framer-motion": "^12.23.24",
|
"framer-motion": "^12.23.24",
|
||||||
"lightswind": "^3.1.18",
|
"lightswind": "^3.1.18",
|
||||||
"lucide-react": "^0.553.0",
|
"lucide-react": "^0.553.0",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
"react-lazy-load-image-component": "^1.6.3",
|
"react-lazy-load-image-component": "^1.6.3",
|
||||||
|
"react-redux": "^9.2.0",
|
||||||
"react-router-dom": "^7.9.6",
|
"react-router-dom": "^7.9.6",
|
||||||
"styled-components": "^6.1.19",
|
"styled-components": "^6.1.19",
|
||||||
"swiper": "^12.0.3",
|
"swiper": "^12.0.3",
|
||||||
@ -334,6 +340,29 @@
|
|||||||
"node": ">=6.9.0"
|
"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": {
|
"node_modules/@dimforge/rapier3d-compat": {
|
||||||
"version": "0.12.0",
|
"version": "0.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz",
|
||||||
@ -349,6 +378,13 @@
|
|||||||
"node": ">=10.0.0"
|
"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": {
|
"node_modules/@emotion/babel-plugin": {
|
||||||
"version": "11.13.5",
|
"version": "11.13.5",
|
||||||
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
|
"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",
|
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz",
|
||||||
"integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==",
|
"integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/memoize": "^0.9.0"
|
"@emotion/memoize": "^0.9.0"
|
||||||
}
|
}
|
||||||
@ -1076,6 +1113,27 @@
|
|||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"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": {
|
"node_modules/@humanfs/core": {
|
||||||
"version": "0.19.1",
|
"version": "0.19.1",
|
||||||
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
"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",
|
"resolved": "https://registry.npmjs.org/@mui/system/-/system-7.3.5.tgz",
|
||||||
"integrity": "sha512-yPaf5+gY3v80HNkJcPi6WT+r9ebeM4eJzrREXPxMt7pNTV/1eahyODO4fbH3Qvd8irNxDFYn5RQ3idHW55rA6g==",
|
"integrity": "sha512-yPaf5+gY3v80HNkJcPi6WT+r9ebeM4eJzrREXPxMt7pNTV/1eahyODO4fbH3Qvd8irNxDFYn5RQ3idHW55rA6g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.28.4",
|
"@babel/runtime": "^7.28.4",
|
||||||
"@mui/private-theming": "^7.3.5",
|
"@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": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
@ -1594,6 +1741,32 @@
|
|||||||
"integrity": "sha512-HVj7LrZ4ReHWBimBvu2SKND3cDVUPWKLqRTmWe/fNY6o1owGOX0cAHbdPDTMelgBlVbrTKrre6lFkhqGZErK/g==",
|
"integrity": "sha512-HVj7LrZ4ReHWBimBvu2SKND3cDVUPWKLqRTmWe/fNY6o1owGOX0cAHbdPDTMelgBlVbrTKrre6lFkhqGZErK/g==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/@rolldown/pluginutils": {
|
||||||
"version": "1.0.0-beta.47",
|
"version": "1.0.0-beta.47",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.47.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.47.tgz",
|
||||||
@ -1887,6 +2060,18 @@
|
|||||||
"win32"
|
"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": {
|
"node_modules/@tailwindcss/node": {
|
||||||
"version": "4.1.17",
|
"version": "4.1.17",
|
||||||
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.17.tgz",
|
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.17.tgz",
|
||||||
@ -2881,6 +3066,12 @@
|
|||||||
"integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
|
"integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/@types/webxr": {
|
||||||
"version": "0.5.24",
|
"version": "0.5.24",
|
||||||
"resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz",
|
"resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz",
|
||||||
@ -3952,9 +4143,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/date-fns": {
|
"node_modules/date-fns": {
|
||||||
"version": "3.6.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
|
||||||
"integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
|
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"funding": {
|
"funding": {
|
||||||
@ -5045,6 +5236,16 @@
|
|||||||
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
|
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/import-fresh": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
|
"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": {
|
"node_modules/lightswind/node_modules/jiti": {
|
||||||
"version": "1.21.7",
|
"version": "1.21.7",
|
||||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
|
"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"
|
"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": {
|
"node_modules/react-refresh": {
|
||||||
"version": "0.18.0",
|
"version": "0.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
|
||||||
@ -7167,6 +7403,22 @@
|
|||||||
"node": ">= 0.10"
|
"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": {
|
"node_modules/refractor": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz",
|
||||||
@ -7206,6 +7458,12 @@
|
|||||||
"node": ">=0.10.0"
|
"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": {
|
"node_modules/resolve": {
|
||||||
"version": "1.22.11",
|
"version": "1.22.11",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
|
||||||
|
|||||||
@ -10,21 +10,27 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@date-io/date-fns": "^3.2.1",
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
"@emotion/styled": "^11.14.1",
|
"@emotion/styled": "^11.14.1",
|
||||||
|
"@files-ui/react": "^1.2.5",
|
||||||
"@lottiefiles/dotlottie-react": "^0.17.8",
|
"@lottiefiles/dotlottie-react": "^0.17.8",
|
||||||
"@mui/icons-material": "^7.3.5",
|
"@mui/icons-material": "^7.3.5",
|
||||||
"@mui/lab": "^7.0.1-beta.19",
|
"@mui/lab": "^7.0.1-beta.19",
|
||||||
"@mui/material": "^7.3.5",
|
"@mui/material": "^7.3.5",
|
||||||
"@mui/styled-engine-sc": "^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",
|
"@tailwindcss/vite": "^4.1.17",
|
||||||
"axios": "^1.13.2",
|
"axios": "^1.13.2",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
"framer-motion": "^12.23.24",
|
"framer-motion": "^12.23.24",
|
||||||
"lightswind": "^3.1.18",
|
"lightswind": "^3.1.18",
|
||||||
"lucide-react": "^0.553.0",
|
"lucide-react": "^0.553.0",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
"react-lazy-load-image-component": "^1.6.3",
|
"react-lazy-load-image-component": "^1.6.3",
|
||||||
|
"react-redux": "^9.2.0",
|
||||||
"react-router-dom": "^7.9.6",
|
"react-router-dom": "^7.9.6",
|
||||||
"styled-components": "^6.1.19",
|
"styled-components": "^6.1.19",
|
||||||
"swiper": "^12.0.3",
|
"swiper": "^12.0.3",
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import "./App.css";
|
import "./App.css";
|
||||||
import { BrowserRouter as Router } from "react-router-dom";
|
import { BrowserRouter as Router } from "react-router-dom";
|
||||||
import AppRoutes from "./routes/AppRoutes";
|
import AppRoutes from "./routes/AppRoutes";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import StarIcon from "@mui/icons-material/Star";
|
|||||||
import VisibilityIcon from "@mui/icons-material/Visibility";
|
import VisibilityIcon from "@mui/icons-material/Visibility";
|
||||||
import PersonAddIcon from "@mui/icons-material/PersonAdd";
|
import PersonAddIcon from "@mui/icons-material/PersonAdd";
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
|
import FilterModal from "../../feature/FilterModal";
|
||||||
|
|
||||||
// Profile Card Component
|
// Profile Card Component
|
||||||
function ProfileCard({ profile }) {
|
function ProfileCard({ profile }) {
|
||||||
@ -98,30 +99,33 @@ function ProfileCard({ profile }) {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-3 my-2 justify-between w-full px-[20px]">
|
<div className="flex gap-3 my-2 justify-between w-full px-[0px]">
|
||||||
<button className="w-[60px] h-[60px] bg-[#A70710] hover:bg-red-600 text-white
|
<button className="gap-2 px-3 w-[fit-content] bg-[#A70710] hover:bg-red-600 text-white
|
||||||
font-semibold text-base py-2 rounded-full shadow-md
|
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">
|
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"/>
|
<path d="M18 6L6 18M6 6l12 12" strokeLinecap="round" strokeLinejoin="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
Decline
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className="w-[60px] h-[60px] bg-[#034E08] hover:bg-green-700 text-white font-semibold text-base
|
className="w-[fit-content] bg-[#034E08] hover:bg-green-700 text-white font-semibold text-base
|
||||||
rounded-full shadow-lg hover:shadow-xl transition-all duration-300
|
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"
|
transform hover:scale-105 flex items-center justify-center"
|
||||||
onClick={() => setIsLiked(!isLiked)}
|
onClick={() => setIsLiked(!isLiked)}
|
||||||
>
|
>
|
||||||
{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"/>
|
<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>
|
||||||
) : (
|
) : (
|
||||||
<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"/>
|
<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>
|
</svg>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
Interest
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -286,11 +290,14 @@ export default function MatchesInterface() {
|
|||||||
{/* Right Content Area - Scrollable */}
|
{/* Right Content Area - Scrollable */}
|
||||||
<div className=" px-2 py-8">
|
<div className=" px-2 py-8">
|
||||||
<div className="w-[100%] max-w-[1200px] mx-auto">
|
<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}
|
{tabs.find(t => t.id === selectedTab)?.title}
|
||||||
</h1>
|
</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) => (
|
{profiles.map((profile) => (
|
||||||
<ProfileCard key={profile.id} profile={profile} />
|
<ProfileCard key={profile.id} profile={profile} />
|
||||||
))}
|
))}
|
||||||
|
|||||||
114
src/feature/AdvancedDropzone.jsx
Normal file
114
src/feature/AdvancedDropzone.jsx
Normal 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;
|
||||||
144
src/feature/EducationalDetailsForm.jsx
Normal file
144
src/feature/EducationalDetailsForm.jsx
Normal 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;
|
||||||
164
src/feature/FamilyDetailsForm.jsx
Normal file
164
src/feature/FamilyDetailsForm.jsx
Normal 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
623
src/feature/FilterForm.jsx
Normal 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;
|
||||||
50
src/feature/FilterModal.jsx
Normal file
50
src/feature/FilterModal.jsx
Normal 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;
|
||||||
116
src/feature/LifestyleDetailsForm.jsx
Normal file
116
src/feature/LifestyleDetailsForm.jsx
Normal 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;
|
||||||
308
src/feature/PartnerPreferencesForm.jsx
Normal file
308
src/feature/PartnerPreferencesForm.jsx
Normal 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;
|
||||||
777
src/feature/PersonalDetailsForm.jsx
Normal file
777
src/feature/PersonalDetailsForm.jsx
Normal 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;
|
||||||
124
src/feature/PreviewScreen.jsx
Normal file
124
src/feature/PreviewScreen.jsx
Normal 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
329
src/feature/StepperForm.jsx
Normal 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;
|
||||||
@ -5,10 +5,15 @@ import "./index.css";
|
|||||||
import App from "./App.jsx";
|
import App from "./App.jsx";
|
||||||
import { ThemeProvider } from "@mui/material/styles";
|
import { ThemeProvider } from "@mui/material/styles";
|
||||||
import theme from "./theme";
|
import theme from "./theme";
|
||||||
|
|
||||||
|
import { Provider } from "react-redux";
|
||||||
|
import { store } from "./redux/store.js";
|
||||||
ReactDOM.createRoot(document.getElementById("root")).render(
|
ReactDOM.createRoot(document.getElementById("root")).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
|
<Provider store={store}>
|
||||||
<App />
|
<App />
|
||||||
|
</Provider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
|
|||||||
140
src/redux/filterSlice.jsx
Normal file
140
src/redux/filterSlice.jsx
Normal 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;
|
||||||
99
src/redux/registrationFormSlice.jsx
Normal file
99
src/redux/registrationFormSlice.jsx
Normal 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
9
src/redux/store.js
Normal 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,
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -15,11 +15,24 @@ import ChatUI from "../pages/ChatPage";
|
|||||||
import HoroscopeGenerator from "../pages/HoroscoperGeneratePage";
|
import HoroscopeGenerator from "../pages/HoroscoperGeneratePage";
|
||||||
import ContactUsPage from "../pages/ContactUsPage";
|
import ContactUsPage from "../pages/ContactUsPage";
|
||||||
import ChangePasswordPage from "../components/auth/ChangePasswordForm";
|
import ChangePasswordPage from "../components/auth/ChangePasswordForm";
|
||||||
|
import StepperForm from "../feature/StepperForm";
|
||||||
|
import FilterForm from "../feature/FilterForm";
|
||||||
|
|
||||||
const UserRoutes = () => {
|
const UserRoutes = () => {
|
||||||
|
|
||||||
return (
|
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 element={<ProfileLayout />}>
|
||||||
<Route path="/main/dashboard" element={<UserDashboardHome />} />
|
<Route path="/main/dashboard" element={<UserDashboardHome />} />
|
||||||
</Route>
|
</Route>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user