diff --git a/package-lock.json b/package-lock.json
index ea48e01..25fb3a6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,13 +10,17 @@
"dependencies": {
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
- "@mui/material": "^7.2.0",
+ "@mui/icons-material": "^7.3.1",
+ "@mui/material": "^7.3.1",
"@mui/stylis-plugin-rtl": "^7.2.0",
+ "@mui/x-data-grid": "^8.10.0",
+ "@mui/x-virtualizer": "^0.1.1",
"@rkheftan/harmony-ui": "^0.1.4",
"i18next": "^25.3.0",
"i18next-browser-languagedetector": "^8.2.0",
"i18next-http-backend": "^3.0.2",
"iconsax-react": "^0.0.8",
+ "libphonenumber-js": "^1.12.10",
"react": "^19.1.0",
"react-country-flag": "^3.1.0",
"react-dom": "^19.1.0",
@@ -28,8 +32,8 @@
"devDependencies": {
"@eslint/js": "^9.29.0",
"@types/node": "^24.0.10",
- "@types/react": "^19.1.8",
- "@types/react-dom": "^19.1.6",
+ "@types/react": "^19.1.9",
+ "@types/react-dom": "^19.1.7",
"@types/stylis": "^4.2.7",
"@typescript-eslint/eslint-plugin": "^8.35.1",
"@typescript-eslint/parser": "^8.35.1",
@@ -1254,32 +1258,58 @@
}
},
"node_modules/@mui/core-downloads-tracker": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.2.0.tgz",
- "integrity": "sha512-d49s7kEgI5iX40xb2YPazANvo7Bx0BLg/MNRwv+7BVpZUzXj1DaVCKlQTDex3gy/0jsCb4w7AY2uH4t4AJvSog==",
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.1.tgz",
+ "integrity": "sha512-+mIK1Z0BhOaQ0vCgOkT1mSrIpEHLo338h4/duuL4TBLXPvUMit732mnwJY3W40Avy30HdeSfwUAAGRkKmwRaEQ==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
}
},
- "node_modules/@mui/material": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.2.0.tgz",
- "integrity": "sha512-NTuyFNen5Z2QY+I242MDZzXnFIVIR6ERxo7vntFi9K1wCgSwvIl0HcAO2OOydKqqKApE6omRiYhpny1ZhGuH7Q==",
+ "node_modules/@mui/icons-material": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.3.1.tgz",
+ "integrity": "sha512-upzCtG6awpL6noEZlJ5Z01khZ9VnLNLaj7tb6iPbN6G97eYfUTs8e9OyPKy3rEms3VQWmVBfri7jzeaRxdFIzA==",
"license": "MIT",
"dependencies": {
- "@babel/runtime": "^7.27.6",
- "@mui/core-downloads-tracker": "^7.2.0",
- "@mui/system": "^7.2.0",
- "@mui/types": "^7.4.4",
- "@mui/utils": "^7.2.0",
+ "@babel/runtime": "^7.28.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@mui/material": "^7.3.1",
+ "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@mui/material": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.1.tgz",
+ "integrity": "sha512-Xf6Shbo03YmcBedZMwSpEFOwpYDtU7tC+rhAHTrA9FHk0FpsDqiQ9jUa1j/9s3HLs7KWb5mDcGnlwdh9Q9KAag==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.28.2",
+ "@mui/core-downloads-tracker": "^7.3.1",
+ "@mui/system": "^7.3.1",
+ "@mui/types": "^7.4.5",
+ "@mui/utils": "^7.3.1",
"@popperjs/core": "^2.11.8",
"@types/react-transition-group": "^4.4.12",
"clsx": "^2.1.1",
"csstype": "^3.1.3",
"prop-types": "^15.8.1",
- "react-is": "^19.1.0",
+ "react-is": "^19.1.1",
"react-transition-group": "^4.4.5"
},
"engines": {
@@ -1292,7 +1322,7 @@
"peerDependencies": {
"@emotion/react": "^11.5.0",
"@emotion/styled": "^11.3.0",
- "@mui/material-pigment-css": "^7.2.0",
+ "@mui/material-pigment-css": "^7.3.1",
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
@@ -1313,13 +1343,13 @@
}
},
"node_modules/@mui/private-theming": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.2.0.tgz",
- "integrity": "sha512-y6N1Yt3T5RMxVFnCh6+zeSWBuQdNDm5/UlM0EAYZzZR/1u+XKJWYQmbpx4e+F+1EpkYi3Nk8KhPiQDi83M3zIw==",
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.3.1.tgz",
+ "integrity": "sha512-WU3YLkKXii/x8ZEKnrLKsPwplCVE11yZxUvlaaZSIzCcI3x2OdFC8eMlNy74hVeUsYQvzzX1Es/k4ARPlFvpPQ==",
"license": "MIT",
"dependencies": {
- "@babel/runtime": "^7.27.6",
- "@mui/utils": "^7.2.0",
+ "@babel/runtime": "^7.28.2",
+ "@mui/utils": "^7.3.1",
"prop-types": "^15.8.1"
},
"engines": {
@@ -1340,12 +1370,12 @@
}
},
"node_modules/@mui/styled-engine": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.2.0.tgz",
- "integrity": "sha512-yq08xynbrNYcB1nBcW9Fn8/h/iniM3ewRguGJXPIAbHvxEF7Pz95kbEEOAAhwzxMX4okhzvHmk0DFuC5ayvgIQ==",
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.3.1.tgz",
+ "integrity": "sha512-Nqo6OHjvJpXJ1+9TekTE//+8RybgPQUKwns2Lh0sq+8rJOUSUKS3KALv4InSOdHhIM9Mdi8/L7LTF1/Ky6D6TQ==",
"license": "MIT",
"dependencies": {
- "@babel/runtime": "^7.27.6",
+ "@babel/runtime": "^7.28.2",
"@emotion/cache": "^11.14.0",
"@emotion/serialize": "^1.3.3",
"@emotion/sheet": "^1.4.0",
@@ -1394,16 +1424,16 @@
}
},
"node_modules/@mui/system": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.2.0.tgz",
- "integrity": "sha512-PG7cm/WluU6RAs+gNND2R9vDwNh+ERWxPkqTaiXQJGIFAyJ+VxhyKfzpdZNk0z0XdmBxxi9KhFOpgxjehf/O0A==",
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.3.1.tgz",
+ "integrity": "sha512-mIidecvcNVpNJMdPDmCeoSL5zshKBbYPcphjuh6ZMjhybhqhZ4mX6k9zmIWh6XOXcqRQMg5KrcjnO0QstrNj3w==",
"license": "MIT",
"dependencies": {
- "@babel/runtime": "^7.27.6",
- "@mui/private-theming": "^7.2.0",
- "@mui/styled-engine": "^7.2.0",
- "@mui/types": "^7.4.4",
- "@mui/utils": "^7.2.0",
+ "@babel/runtime": "^7.28.2",
+ "@mui/private-theming": "^7.3.1",
+ "@mui/styled-engine": "^7.3.1",
+ "@mui/types": "^7.4.5",
+ "@mui/utils": "^7.3.1",
"clsx": "^2.1.1",
"csstype": "^3.1.3",
"prop-types": "^15.8.1"
@@ -1434,12 +1464,12 @@
}
},
"node_modules/@mui/types": {
- "version": "7.4.4",
- "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.4.tgz",
- "integrity": "sha512-p63yhbX52MO/ajXC7hDHJA5yjzJekvWD3q4YDLl1rSg+OXLczMYPvTuSuviPRCgRX8+E42RXz1D/dz9SxPSlWg==",
+ "version": "7.4.5",
+ "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.5.tgz",
+ "integrity": "sha512-ZPwlAOE3e8C0piCKbaabwrqZbW4QvWz0uapVPWya7fYj6PeDkl5sSJmomT7wjOcZGPB48G/a6Ubidqreptxz4g==",
"license": "MIT",
"dependencies": {
- "@babel/runtime": "^7.27.6"
+ "@babel/runtime": "^7.28.2"
},
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
@@ -1451,17 +1481,17 @@
}
},
"node_modules/@mui/utils": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.2.0.tgz",
- "integrity": "sha512-O0i1GQL6MDzhKdy9iAu5Yr0Sz1wZjROH1o3aoztuivdCXqEeQYnEjTDiRLGuFxI9zrUbTHBwobMyQH5sNtyacw==",
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.1.tgz",
+ "integrity": "sha512-/31y4wZqVWa0jzMnzo6JPjxwP6xXy4P3+iLbosFg/mJQowL1KIou0LC+lquWW60FKVbKz5ZUWBg2H3jausa0pw==",
"license": "MIT",
"dependencies": {
- "@babel/runtime": "^7.27.6",
- "@mui/types": "^7.4.4",
+ "@babel/runtime": "^7.28.2",
+ "@mui/types": "^7.4.5",
"@types/prop-types": "^15.7.15",
"clsx": "^2.1.1",
"prop-types": "^15.8.1",
- "react-is": "^19.1.0"
+ "react-is": "^19.1.1"
},
"engines": {
"node": ">=14.0.0"
@@ -1481,16 +1511,15 @@
}
},
"node_modules/@mui/x-data-grid": {
- "version": "8.9.2",
- "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-8.9.2.tgz",
- "integrity": "sha512-LXyRiF0U60enI/FMFPd0Ct9rP5PIIvUUWnTDqJ5IDSj7cDuD6/cG4E3RcV76iN9j+tALWMZKjeTkBGxQjZ078A==",
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-8.10.0.tgz",
+ "integrity": "sha512-NMOZyDcE9vqn0qEv0z6DqkXwzIOj4ZFy4QC0RcUjEvBmjwdRc3KCh9XSWAuqmpc23B4M9cydVVkt0CBfOJKwsQ==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/runtime": "^7.28.2",
"@mui/utils": "^7.2.0",
- "@mui/x-internals": "8.9.2",
- "@mui/x-virtualizer": "0.1.0",
+ "@mui/x-internals": "8.10.0",
+ "@mui/x-virtualizer": "0.1.1",
"clsx": "^2.1.1",
"prop-types": "^15.8.1",
"use-sync-external-store": "^1.5.0"
@@ -1557,6 +1586,67 @@
}
}
},
+ "node_modules/@mui/x-data-grid-premium/node_modules/@mui/x-data-grid": {
+ "version": "8.9.2",
+ "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-8.9.2.tgz",
+ "integrity": "sha512-LXyRiF0U60enI/FMFPd0Ct9rP5PIIvUUWnTDqJ5IDSj7cDuD6/cG4E3RcV76iN9j+tALWMZKjeTkBGxQjZ078A==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.28.2",
+ "@mui/utils": "^7.2.0",
+ "@mui/x-internals": "8.9.2",
+ "@mui/x-virtualizer": "0.1.0",
+ "clsx": "^2.1.1",
+ "prop-types": "^15.8.1",
+ "use-sync-external-store": "^1.5.0"
+ },
+ "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",
+ "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
+ }
+ }
+ },
+ "node_modules/@mui/x-data-grid-premium/node_modules/@mui/x-virtualizer": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@mui/x-virtualizer/-/x-virtualizer-0.1.0.tgz",
+ "integrity": "sha512-fugd4tRJTTZpP2NFAxrngQQQhy1SDQq5wzMb6PJC1Y3b6THDw3P4vFuKxHmmfbPuCXLrxDifG0a8Q4LncBt1zA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.27.4",
+ "@mui/x-internals": "8.9.2"
+ },
+ "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",
+ "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/@mui/x-data-grid-pro": {
"version": "8.9.2",
"resolved": "https://registry.npmjs.org/@mui/x-data-grid-pro/-/x-data-grid-pro-8.9.2.tgz",
@@ -1593,6 +1683,89 @@
}
}
},
+ "node_modules/@mui/x-data-grid-pro/node_modules/@mui/x-data-grid": {
+ "version": "8.9.2",
+ "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-8.9.2.tgz",
+ "integrity": "sha512-LXyRiF0U60enI/FMFPd0Ct9rP5PIIvUUWnTDqJ5IDSj7cDuD6/cG4E3RcV76iN9j+tALWMZKjeTkBGxQjZ078A==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.28.2",
+ "@mui/utils": "^7.2.0",
+ "@mui/x-internals": "8.9.2",
+ "@mui/x-virtualizer": "0.1.0",
+ "clsx": "^2.1.1",
+ "prop-types": "^15.8.1",
+ "use-sync-external-store": "^1.5.0"
+ },
+ "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",
+ "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
+ }
+ }
+ },
+ "node_modules/@mui/x-data-grid-pro/node_modules/@mui/x-virtualizer": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@mui/x-virtualizer/-/x-virtualizer-0.1.0.tgz",
+ "integrity": "sha512-fugd4tRJTTZpP2NFAxrngQQQhy1SDQq5wzMb6PJC1Y3b6THDw3P4vFuKxHmmfbPuCXLrxDifG0a8Q4LncBt1zA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/runtime": "^7.27.4",
+ "@mui/x-internals": "8.9.2"
+ },
+ "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",
+ "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@mui/x-data-grid/node_modules/@mui/x-internals": {
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-8.10.0.tgz",
+ "integrity": "sha512-stYhWBeCKfV2/ltAWShZ3ZJ51otbqpMpC+krWWoIsxM8TuvGzwXw5YMU9L2fTb8hRstsiOCQfEzIn12Ii7+N0Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.28.2",
+ "@mui/utils": "^7.2.0",
+ "reselect": "^5.1.1",
+ "use-sync-external-store": "^1.5.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/@mui/x-internals": {
"version": "8.9.2",
"resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-8.9.2.tgz",
@@ -1656,14 +1829,14 @@
}
},
"node_modules/@mui/x-virtualizer": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/@mui/x-virtualizer/-/x-virtualizer-0.1.0.tgz",
- "integrity": "sha512-fugd4tRJTTZpP2NFAxrngQQQhy1SDQq5wzMb6PJC1Y3b6THDw3P4vFuKxHmmfbPuCXLrxDifG0a8Q4LncBt1zA==",
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/@mui/x-virtualizer/-/x-virtualizer-0.1.1.tgz",
+ "integrity": "sha512-pZ84wPu/97Z6g2HF7D4t8X5GSgc+Gr3EoJJpGv1SP3mAX2OcZtYhXiUyQzvHPm2jvDQuxIIzwXT3hMIEgdDPPQ==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/runtime": "^7.27.4",
- "@mui/x-internals": "8.9.2"
+ "@mui/utils": "^7.2.0",
+ "@mui/x-internals": "8.10.0"
},
"engines": {
"node": ">=14.0.0"
@@ -1677,6 +1850,28 @@
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
+ "node_modules/@mui/x-virtualizer/node_modules/@mui/x-internals": {
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-8.10.0.tgz",
+ "integrity": "sha512-stYhWBeCKfV2/ltAWShZ3ZJ51otbqpMpC+krWWoIsxM8TuvGzwXw5YMU9L2fTb8hRstsiOCQfEzIn12Ii7+N0Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.28.2",
+ "@mui/utils": "^7.2.0",
+ "reselect": "^5.1.1",
+ "use-sync-external-store": "^1.5.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -2129,18 +2324,18 @@
"license": "MIT"
},
"node_modules/@types/react": {
- "version": "19.1.8",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz",
- "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==",
+ "version": "19.1.9",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.9.tgz",
+ "integrity": "sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA==",
"license": "MIT",
"dependencies": {
"csstype": "^3.0.2"
}
},
"node_modules/@types/react-dom": {
- "version": "19.1.6",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz",
- "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==",
+ "version": "19.1.7",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.7.tgz",
+ "integrity": "sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw==",
"dev": true,
"license": "MIT",
"peerDependencies": {
@@ -4374,6 +4569,12 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/libphonenumber-js": {
+ "version": "1.12.10",
+ "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.10.tgz",
+ "integrity": "sha512-E91vHJD61jekHHR/RF/E83T/CMoaLXT7cwYA75T4gim4FZjnM6hbJjVIGg7chqlSqRsSvQ3izGmOjHy1SQzcGQ==",
+ "license": "MIT"
+ },
"node_modules/lie": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
@@ -5035,9 +5236,9 @@
}
},
"node_modules/react-is": {
- "version": "19.1.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz",
- "integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==",
+ "version": "19.1.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.1.tgz",
+ "integrity": "sha512-tr41fA15Vn8p4X9ntI+yCyeGSf1TlYaY5vlTZfQmeLBrFo3psOPX6HhTDnFNL9uj3EhP0KAQ80cugCl4b4BERA==",
"license": "MIT"
},
"node_modules/react-refresh": {
@@ -5178,8 +5379,7 @@
"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",
- "peer": true
+ "license": "MIT"
},
"node_modules/resolve": {
"version": "1.22.10",
@@ -5556,9 +5756,9 @@
}
},
"node_modules/tmp": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz",
- "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==",
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
+ "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
"license": "MIT",
"peer": true,
"engines": {
@@ -5782,7 +5982,6 @@
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
"integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
"license": "MIT",
- "peer": true,
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
diff --git a/package.json b/package.json
index 54fc38f..661e5b5 100644
--- a/package.json
+++ b/package.json
@@ -13,13 +13,17 @@
"dependencies": {
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
- "@mui/material": "^7.2.0",
+ "@mui/icons-material": "^7.3.1",
+ "@mui/material": "^7.3.1",
"@mui/stylis-plugin-rtl": "^7.2.0",
+ "@mui/x-data-grid": "^8.10.0",
+ "@mui/x-virtualizer": "^0.1.1",
"@rkheftan/harmony-ui": "^0.1.4",
"i18next": "^25.3.0",
"i18next-browser-languagedetector": "^8.2.0",
"i18next-http-backend": "^3.0.2",
"iconsax-react": "^0.0.8",
+ "libphonenumber-js": "^1.12.10",
"react": "^19.1.0",
"react-country-flag": "^3.1.0",
"react-dom": "^19.1.0",
@@ -31,8 +35,8 @@
"devDependencies": {
"@eslint/js": "^9.29.0",
"@types/node": "^24.0.10",
- "@types/react": "^19.1.8",
- "@types/react-dom": "^19.1.6",
+ "@types/react": "^19.1.9",
+ "@types/react-dom": "^19.1.7",
"@types/stylis": "^4.2.7",
"@typescript-eslint/eslint-plugin": "^8.35.1",
"@typescript-eslint/parser": "^8.35.1",
diff --git a/public/locales/en/country.json b/public/locales/en/country.json
new file mode 100644
index 0000000..b874af3
--- /dev/null
+++ b/public/locales/en/country.json
@@ -0,0 +1,247 @@
+{
+ "country.afghanistan": "Afghanistan",
+ "country.aland_islands": "Aland islands",
+ "country.albania": "Albania",
+ "country.algeria": "Algeria",
+ "country.american_samoa": "American samoa",
+ "country.andorra": "Andorra",
+ "country.angola": "Angola",
+ "country.anguilla": "Anguilla",
+ "country.antarctica": "Antarctica",
+ "country.antigua_and_barbuda": "Antigua and barbuda",
+ "country.argentina": "Argentina",
+ "country.armenia": "Armenia",
+ "country.aruba": "Aruba",
+ "country.australia": "Australia",
+ "country.austria": "Austria",
+ "country.azerbaijan": "Azerbaijan",
+ "country.bahamas": "Bahamas",
+ "country.bahrain": "Bahrain",
+ "country.bangladesh": "Bangladesh",
+ "country.barbados": "Barbados",
+ "country.belarus": "Belarus",
+ "country.belgium": "Belgium",
+ "country.belize": "Belize",
+ "country.benin": "Benin",
+ "country.bermuda": "Bermuda",
+ "country.bhutan": "Bhutan",
+ "country.bolivia": "Bolivia",
+ "country.bosnia_and_herzegovina": "Bosnia and herzegovina",
+ "country.botswana": "Botswana",
+ "country.brazil": "Brazil",
+ "country.british_indian_ocean_territory": "British indian ocean territory",
+ "country.british_virgin_islands": "British virgin islands",
+ "country.brunei": "Brunei",
+ "country.bulgaria": "Bulgaria",
+ "country.burkina_faso": "Burkina faso",
+ "country.burundi": "Burundi",
+ "country.cambodia": "Cambodia",
+ "country.cameroon": "Cameroon",
+ "country.canada": "Canada",
+ "country.cape_verde": "Cape verde",
+ "country.cayman_islands": "Cayman islands",
+ "country.central_african_republic": "Central african republic",
+ "country.chad": "Chad",
+ "country.chile": "Chile",
+ "country.china": "China",
+ "country.christmas_island": "Christmas island",
+ "country.cocos_keeling_islands": "Cocos keeling islands",
+ "country.colombia": "Colombia",
+ "country.comoros": "Comoros",
+ "country.congo_brazzaville": "Congo brazzaville",
+ "country.congo_kinshasa": "Congo kinshasa",
+ "country.cook_islands": "Cook islands",
+ "country.costa_rica": "Costa rica",
+ "country.cote_divoire": "Cote divoire",
+ "country.croatia": "Croatia",
+ "country.cuba": "Cuba",
+ "country.curacao": "Curacao",
+ "country.cyprus": "Cyprus",
+ "country.czech_republic": "Czech republic",
+ "country.denmark": "Denmark",
+ "country.djibouti": "Djibouti",
+ "country.dominica": "Dominica",
+ "country.dominican_republic": "Dominican republic",
+ "country.ecuador": "Ecuador",
+ "country.egypt": "Egypt",
+ "country.el_salvador": "El salvador",
+ "country.equatorial_guinea": "Equatorial guinea",
+ "country.eritrea": "Eritrea",
+ "country.estonia": "Estonia",
+ "country.eswatini": "Eswatini",
+ "country.ethiopia": "Ethiopia",
+ "country.falkland_islands": "Falkland islands",
+ "country.faroe_islands": "Faroe islands",
+ "country.fiji": "Fiji",
+ "country.finland": "Finland",
+ "country.france": "France",
+ "country.french_guiana": "French guiana",
+ "country.french_polynesia": "French polynesia",
+ "country.gabon": "Gabon",
+ "country.gambia": "Gambia",
+ "country.georgia": "Georgia",
+ "country.germany": "Germany",
+ "country.ghana": "Ghana",
+ "country.gibraltar": "Gibraltar",
+ "country.greece": "Greece",
+ "country.greenland": "Greenland",
+ "country.grenada": "Grenada",
+ "country.guadeloupe": "Guadeloupe",
+ "country.guam": "Guam",
+ "country.guatemala": "Guatemala",
+ "country.guernsey": "Guernsey",
+ "country.guinea": "Guinea",
+ "country.guinea_bissau": "Guinea bissau",
+ "country.guyana": "Guyana",
+ "country.haiti": "Haiti",
+ "country.honduras": "Honduras",
+ "country.hong_kong": "Hong kong",
+ "country.hungary": "Hungary",
+ "country.iceland": "Iceland",
+ "country.india": "India",
+ "country.indonesia": "Indonesia",
+ "country.iran": "Iran",
+ "country.iraq": "Iraq",
+ "country.ireland": "Ireland",
+ "country.isle_of_man": "Isle of man",
+ "country.israel": "Israel",
+ "country.italy": "Italy",
+ "country.jamaica": "Jamaica",
+ "country.japan": "Japan",
+ "country.jersey": "Jersey",
+ "country.jordan": "Jordan",
+ "country.kazakhstan": "Kazakhstan",
+ "country.kenya": "Kenya",
+ "country.kiribati": "Kiribati",
+ "country.kosovo": "Kosovo",
+ "country.kuwait": "Kuwait",
+ "country.kyrgyzstan": "Kyrgyzstan",
+ "country.laos": "Laos",
+ "country.latvia": "Latvia",
+ "country.lebanon": "Lebanon",
+ "country.lesotho": "Lesotho",
+ "country.liberia": "Liberia",
+ "country.libya": "Libya",
+ "country.liechtenstein": "Liechtenstein",
+ "country.lithuania": "Lithuania",
+ "country.luxembourg": "Luxembourg",
+ "country.macau": "Macau",
+ "country.madagascar": "Madagascar",
+ "country.malawi": "Malawi",
+ "country.malaysia": "Malaysia",
+ "country.maldives": "Maldives",
+ "country.mali": "Mali",
+ "country.malta": "Malta",
+ "country.marshall_islands": "Marshall islands",
+ "country.martinique": "Martinique",
+ "country.mauritania": "Mauritania",
+ "country.mauritius": "Mauritius",
+ "country.mayotte": "Mayotte",
+ "country.mexico": "Mexico",
+ "country.micronesia": "Micronesia",
+ "country.moldova": "Moldova",
+ "country.monaco": "Monaco",
+ "country.mongolia": "Mongolia",
+ "country.montenegro": "Montenegro",
+ "country.montserrat": "Montserrat",
+ "country.morocco": "Morocco",
+ "country.mozambique": "Mozambique",
+ "country.myanmar": "Myanmar",
+ "country.namibia": "Namibia",
+ "country.nauru": "Nauru",
+ "country.nepal": "Nepal",
+ "country.netherlands": "Netherlands",
+ "country.new_caledonia": "New caledonia",
+ "country.new_zealand": "New zealand",
+ "country.nicaragua": "Nicaragua",
+ "country.niger": "Niger",
+ "country.nigeria": "Nigeria",
+ "country.niue": "Niue",
+ "country.norfolk_island": "Norfolk island",
+ "country.north_korea": "North korea",
+ "country.north_macedonia": "North macedonia",
+ "country.northern_mariana_islands": "Northern mariana islands",
+ "country.norway": "Norway",
+ "country.oman": "Oman",
+ "country.pakistan": "Pakistan",
+ "country.palau": "Palau",
+ "country.palestine": "Palestine",
+ "country.panama": "Panama",
+ "country.papua_new_guinea": "Papua new guinea",
+ "country.paraguay": "Paraguay",
+ "country.peru": "Peru",
+ "country.philippines": "Philippines",
+ "country.pitcairn_islands": "Pitcairn islands",
+ "country.poland": "Poland",
+ "country.portugal": "Portugal",
+ "country.puerto_rico": "Puerto rico",
+ "country.qatar": "Qatar",
+ "country.reunion": "Reunion",
+ "country.romania": "Romania",
+ "country.russia": "Russia",
+ "country.rwanda": "Rwanda",
+ "country.saint_barthelemy": "Saint barthelemy",
+ "country.saint_helena": "Saint helena",
+ "country.saint_kitts_and_nevis": "Saint kitts and nevis",
+ "country.saint_lucia": "Saint lucia",
+ "country.saint_martin": "Saint martin",
+ "country.saint_pierre_and_miquelon": "Saint pierre and miquelon",
+ "country.saint_vincent_and_the_grenadines": "Saint vincent and the grenadines",
+ "country.samoa": "Samoa",
+ "country.san_marino": "San marino",
+ "country.sao_tome_and_principe": "Sao tome and principe",
+ "country.saudi_arabia": "Saudi arabia",
+ "country.senegal": "Senegal",
+ "country.serbia": "Serbia",
+ "country.seychelles": "Seychelles",
+ "country.sierra_leone": "Sierra leone",
+ "country.singapore": "Singapore",
+ "country.sint_maarten": "Sint maarten",
+ "country.slovakia": "Slovakia",
+ "country.slovenia": "Slovenia",
+ "country.solomon_islands": "Solomon islands",
+ "country.somalia": "Somalia",
+ "country.south_africa": "South africa",
+ "country.south_georgia_and_south_sandwich_islands": "South georgia and south sandwich islands",
+ "country.south_korea": "South korea",
+ "country.south_sudan": "South sudan",
+ "country.spain": "Spain",
+ "country.sri_lanka": "Sri lanka",
+ "country.sudan": "Sudan",
+ "country.suriname": "Suriname",
+ "country.svalbard_and_jan_mayen": "Svalbard and jan mayen",
+ "country.sweden": "Sweden",
+ "country.switzerland": "Switzerland",
+ "country.syria": "Syria",
+ "country.taiwan": "Taiwan",
+ "country.tajikistan": "Tajikistan",
+ "country.tanzania": "Tanzania",
+ "country.thailand": "Thailand",
+ "country.timor_leste": "Timor leste",
+ "country.togo": "Togo",
+ "country.tokelau": "Tokelau",
+ "country.tonga": "Tonga",
+ "country.trinidad_and_tobago": "Trinidad and tobago",
+ "country.tunisia": "Tunisia",
+ "country.turkey": "Turkey",
+ "country.turkmenistan": "Turkmenistan",
+ "country.turks_and_caicos_islands": "Turks and caicos islands",
+ "country.tuvalu": "Tuvalu",
+ "country.us_virgin_islands": "Us virgin islands",
+ "country.uganda": "Uganda",
+ "country.ukraine": "Ukraine",
+ "country.united_arab_emirates": "United arab emirates",
+ "country.united_kingdom": "United kingdom",
+ "country.united_states": "United states",
+ "country.uruguay": "Uruguay",
+ "country.uzbekistan": "Uzbekistan",
+ "country.vanuatu": "Vanuatu",
+ "country.vatican_city": "Vatican city",
+ "country.venezuela": "Venezuela",
+ "country.vietnam": "Vietnam",
+ "country.wallis_and_futuna": "Wallis and futuna",
+ "country.western_sahara": "Western sahara",
+ "country.yemen": "Yemen",
+ "country.zambia": "Zambia",
+ "country.zimbabwe": "Zimbabwe"
+}
diff --git a/public/locales/en/profileSetting.json b/public/locales/en/profileSetting.json
index b53e9ff..bb4b664 100644
--- a/public/locales/en/profileSetting.json
+++ b/public/locales/en/profileSetting.json
@@ -38,6 +38,8 @@
"phoneNumberText": "Your new contact number will replace your previous contact number.",
"verb": ".",
"notDetermined": "Not determined",
- "successfulChangePhone": "Phone number changed successfully"
+ "successfulChangePhone": "Phone number changed successfully",
+ "phoneNumberIsInvalid": "Phone number is invalid",
+ "thisFieldIsRequired": "This field is requried"
}
}
diff --git a/public/locales/fa/country.json b/public/locales/fa/country.json
new file mode 100644
index 0000000..0da70cf
--- /dev/null
+++ b/public/locales/fa/country.json
@@ -0,0 +1,182 @@
+{
+ "country.afghanistan": "افغانستان",
+ "country.aland_islands": "جزایر آلند",
+ "country.albania": "آلبانی",
+ "country.algeria": "الجزایر",
+ "country.american_samoa": "ساموای آمریکایی",
+ "country.andorra": "آندورا",
+ "country.angola": "آنگولا",
+ "country.anguilla": "آنگویلا",
+ "country.antarctica": "جنوبگان",
+ "country.antigua_and_barbuda": "آنتیگوا و باربودا",
+ "country.argentina": "آرژانتین",
+ "country.armenia": "ارمنستان",
+ "country.aruba": "آروبا",
+ "country.australia": "استرالیا",
+ "country.austria": "اتریش",
+ "country.azerbaijan": "آذربایجان",
+ "country.bahamas": "باهاما",
+ "country.bahrain": "بحرین",
+ "country.bangladesh": "بنگلادش",
+ "country.barbados": "باربادوس",
+ "country.belarus": "بلاروس",
+ "country.belgium": "بلژیک",
+ "country.belize": "بلیز",
+ "country.benin": "بنین",
+ "country.bermuda": "برمودا",
+ "country.bhutan": "بوتان",
+ "country.bolivia": "بولیوی",
+ "country.bosnia_and_herzegovina": "بوسنی و هرزگوین",
+ "country.botswana": "بوتسوانا",
+ "country.brazil": "برزیل",
+ "country.british_virgin_islands": "جزایر ویرجین بریتانیا",
+ "country.brunei": "برونئی",
+ "country.bulgaria": "بلغارستان",
+ "country.burkina_faso": "بورکینافاسو",
+ "country.burundi": "بوروندی",
+ "country.cambodia": "کامبوج",
+ "country.cameroon": "کامرون",
+ "country.canada": "کانادا",
+ "country.cape_verde": "کیپ ورد",
+ "country.cayman_islands": "جزایر کیمن",
+ "country.central_african_republic": "جمهوری آفریقای مرکزی",
+ "country.chad": "چاد",
+ "country.chile": "شیلی",
+ "country.china": "چین",
+ "country.colombia": "کلمبیا",
+ "country.comoros": "کومور",
+ "country.costa_rica": "کاستاریکا",
+ "country.cote_divoire": "ساحل عاج",
+ "country.croatia": "کرواسی",
+ "country.cuba": "کوبا",
+ "country.cyprus": "قبرس",
+ "country.czech_republic": "جمهوری چک",
+ "country.denmark": "دانمارک",
+ "country.djibouti": "جیبوتی",
+ "country.dominica": "دومینیکا",
+ "country.dominican_republic": "جمهوری دومینیکن",
+ "country.ecuador": "اکوادور",
+ "country.egypt": "مصر",
+ "country.el_salvador": "السالوادور",
+ "country.equatorial_guinea": "گینه استوایی",
+ "country.eritrea": "اریتره",
+ "country.estonia": "استونی",
+ "country.eswatini": "سوازیلند",
+ "country.ethiopia": "اتیوپی",
+ "country.fiji": "فیجی",
+ "country.finland": "فنلاند",
+ "country.france": "فرانسه",
+ "country.gabon": "گابن",
+ "country.gambia": "گامبیا",
+ "country.georgia": "گرجستان",
+ "country.germany": "آلمان",
+ "country.ghana": "غنا",
+ "country.greece": "یونان",
+ "country.guatemala": "گواتمالا",
+ "country.guinea": "گینه",
+ "country.guinea_bissau": "گینه بیسائو",
+ "country.guyana": "گویان",
+ "country.haiti": "هائیتی",
+ "country.honduras": "هندوراس",
+ "country.hungary": "مجارستان",
+ "country.iceland": "ایسلند",
+ "country.india": "هندوستان",
+ "country.indonesia": "اندونزی",
+ "country.iran": "ایران",
+ "country.iraq": "عراق",
+ "country.ireland": "ایرلند",
+ "country.israel": "اسرائیل",
+ "country.italy": "ایتالیا",
+ "country.jamaica": "جامائیکا",
+ "country.japan": "ژاپن",
+ "country.jordan": "اردن",
+ "country.kazakhstan": "قزاقستان",
+ "country.kenya": "کنیا",
+ "country.kuwait": "کویت",
+ "country.kyrgyzstan": "قرقیزستان",
+ "country.laos": "لائوس",
+ "country.latvia": "لتونی",
+ "country.lebanon": "لبنان",
+ "country.lesotho": "لسوتو",
+ "country.liberia": "لیبریا",
+ "country.libya": "لیبی",
+ "country.luxembourg": "لوکزامبورگ",
+ "country.malaysia": "مالزی",
+ "country.maldives": "مالدیو",
+ "country.mali": "مالی",
+ "country.malta": "مالت",
+ "country.mauritania": "موریتانی",
+ "country.mauritius": "موریس",
+ "country.mexico": "مکزیک",
+ "country.moldova": "مولداوی",
+ "country.monaco": "موناکو",
+ "country.mongolia": "مغولستان",
+ "country.morocco": "مراکش",
+ "country.mozambique": "موزامبیک",
+ "country.myanmar": "میانمار",
+ "country.namibia": "نامیبیا",
+ "country.nepal": "نپال",
+ "country.netherlands": "هلند",
+ "country.new_zealand": "نیوزیلند",
+ "country.nicaragua": "نیکاراگوئه",
+ "country.niger": "نیجر",
+ "country.nigeria": "نیجریه",
+ "country.north_korea": "کره شمالی",
+ "country.north_macedonia": "مقدونیه",
+ "country.norway": "نروژ",
+ "country.oman": "عمان",
+ "country.pakistan": "پاکستان",
+ "country.palau": "پالائو",
+ "country.panama": "پاناما",
+ "country.papua_new_guinea": "پاپوآ گینه نو",
+ "country.paraguay": "پاراگوئه",
+ "country.peru": "پرو",
+ "country.philippines": "فیلیپین",
+ "country.poland": "لهستان",
+ "country.portugal": "پرتغال",
+ "country.qatar": "قطر",
+ "country.romania": "رومانی",
+ "country.russia": "روسیه",
+ "country.rwanda": "رواندا",
+ "country.saudi_arabia": "عربستان سعودی",
+ "country.senegal": "سنگال",
+ "country.serbia": "صربستان",
+ "country.seychelles": "سیشل",
+ "country.sierra_leone": "سیرالئون",
+ "country.singapore": "سنگاپور",
+ "country.south_africa": "آفریقای جنوبی",
+ "country.south_korea": "کره جنوبی",
+ "country.south_sudan": "سودان جنوبی",
+ "country.spain": "اسپانیا",
+ "country.sri_lanka": "سریلانکا",
+ "country.sudan": "سودان",
+ "country.suriname": "سورینام",
+ "country.sweden": "سوئد",
+ "country.switzerland": "سوئیس",
+ "country.syria": "سوریه",
+ "country.taiwan": "تایوان",
+ "country.tajikistan": "تاجیکستان",
+ "country.tanzania": "تانزانیا",
+ "country.thailand": "تایلند",
+ "country.timor_leste": "تیمور شرقی",
+ "country.togo": "توگو",
+ "country.tonga": "تونگا",
+ "country.trinidad_and_tobago": "ترینیداد و توباگو",
+ "country.tunisia": "تونس",
+ "country.turkey": "ترکیه",
+ "country.turkmenistan": "ترکمنستان",
+ "country.tuvalu": "تووالو",
+ "country.uganda": "اوگاندا",
+ "country.ukraine": "اوکراین",
+ "country.united_arab_emirates": "امارات متحده عربی",
+ "country.united_kingdom": "انگلستان",
+ "country.united_states": "ایالات متحده آمریکا",
+ "country.uruguay": "اروگوئه",
+ "country.uzbekistan": "ازبکستان",
+ "country.vanuatu": "وانواتو",
+ "country.venezuela": "ونزوئلا",
+ "country.vietnam": "ویتنام",
+ "country.yemen": "یمن",
+ "country.zambia": "زامبیا",
+ "country.zimbabwe": "زیمبابوه"
+}
diff --git a/public/locales/fa/profileSetting.json b/public/locales/fa/profileSetting.json
index d1942d0..c48cd73 100644
--- a/public/locales/fa/profileSetting.json
+++ b/public/locales/fa/profileSetting.json
@@ -38,6 +38,8 @@
"phoneNumberText": "شماره تماس جدید شما جایگزین شماره تماس قبلی",
"verb": "خواهد شد",
"notDetermined": "تعیین نشده",
- "successfulChangePhone": "شماره تماس با موفقیت تغییر کرد"
+ "successfulChangePhone": "شماره تماس با موفقیت تغییر کرد",
+ "phoneNumberIsInvalid": "شماره وارد شده نامعتبر میباشد",
+ "thisFieldIsRequired": "این فیلد الزامی است"
}
}
diff --git a/src/App.tsx b/src/App.tsx
index a89dfd2..4fd2f1b 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,4 +1,4 @@
-import { CssBaseline, useColorScheme } from '@mui/material';
+import { CssBaseline } from '@mui/material';
import './App.css';
import { LanguageManager } from './components/LanguageManager';
import { Settings } from './features/profile/routes/SettingPage';
@@ -9,53 +9,8 @@ function App() {
- {/*
-
{t('helloWorld')}
-
-
-
-
- success
-
-
- warning
-
-
- info
-
-
- error
-
-
-
*/}
>
);
}
export default App;
-
-import { Button } from '@mui/material';
-import { PersonalInformation } from './features/profile/components/userInformation/PersonalInformation';
-import { PhoneNumber } from './features/profile/components/userInformation/PhoneNumber';
-import { SocialMedia } from './features/profile/components/userInformation/SocialMedia';
-import { PasswordSecurity } from './features/profile/components/security/PasswordSecurity';
-import { RecentLogins } from './features/profile/components/security/RecentLogins';
-import { ActiveDevices } from './features/profile/components/activeDevices/ActiveDevices';
-import { Setting } from './features/profile/components/setting/Setting';
-
-export const ThemeToggleButton = () => {
- const { mode, setMode } = useColorScheme();
-
- return (
-
- );
-};
diff --git a/src/features/profile/components/CountryCodeSelector.tsx b/src/features/profile/components/CountryCodeSelector.tsx
new file mode 100644
index 0000000..8925014
--- /dev/null
+++ b/src/features/profile/components/CountryCodeSelector.tsx
@@ -0,0 +1,254 @@
+import {
+ Box,
+ InputAdornment,
+ ListItem,
+ ListItemIcon,
+ ListItemText,
+ Menu,
+ MenuItem,
+ TextField,
+ Typography,
+} from '@mui/material';
+import { useMemo, useRef, useState, type RefObject } from 'react';
+import { ArrowDown2 } from 'iconsax-react';
+import ReactCountryFlag from 'react-country-flag';
+import { useTranslation } from 'react-i18next';
+import { countries, type Country } from '../data/countries';
+
+interface CountryCodeSelectorProps {
+ show: boolean;
+ value: string;
+ onChange: (newValue: string) => void;
+ menuAnchor: HTMLElement | null;
+ onCloseFocusRef: RefObject;
+}
+
+/**
+ * An animated country code adornment that fades and slides into view.
+ * Its visibility is controlled by the `show` prop.
+ */
+export function CountryCodeSelector({
+ show,
+ value,
+ onChange,
+ menuAnchor,
+ onCloseFocusRef,
+}: CountryCodeSelectorProps) {
+ const [anchorEl, setAnchorEl] = useState(null);
+ const [searchTerm, setSearchTerm] = useState('');
+ const open = Boolean(anchorEl);
+ const searchInputRef = useRef(null);
+ const menuWidth = menuAnchor ? menuAnchor.clientWidth : 'auto';
+ const { t, i18n } = useTranslation();
+
+ const selectedCountry =
+ countries.find((c) => c.phone === value) || countries[0];
+
+ const handleClick = () => {
+ setAnchorEl(menuAnchor);
+ };
+
+ const handleClose = () => {
+ setTimeout(() => {
+ setAnchorEl(null);
+ }, 0);
+ setTimeout(() => {
+ onCloseFocusRef.current?.focus();
+ }, 100);
+ setSearchTerm(''); // Reset search on close
+ };
+
+ const handleSelect = (country: Country) => {
+ onChange(country.phone);
+ handleClose();
+ };
+
+ const handleMenuEntered = () => {
+ // Focus the input field after the menu has finished opening
+ searchInputRef.current?.focus();
+ };
+
+ const filteredCountries = useMemo(
+ () =>
+ countries.filter(
+ (country) =>
+ t(country.label).toLowerCase().includes(searchTerm.toLowerCase()) ||
+ country.label.toLowerCase().includes(searchTerm.toLowerCase()) ||
+ country.phone.includes(searchTerm),
+ ),
+ [searchTerm],
+ );
+
+ return (
+
+
+ theme.transitions.create(['width', 'opacity'], {
+ duration: theme.transitions.duration.standard,
+ }),
+
+ // Prevent content from wrapping or spilling out during animation
+ overflow: 'hidden',
+ whiteSpace: 'nowrap',
+
+ // layout styles
+ height: '100%',
+ display: 'flex',
+ alignItems: 'center',
+ gap: 0.25,
+ pl: show ? 0.25 : 0,
+
+ '&:hover': {
+ cursor: 'pointer',
+ },
+ }}
+ >
+ {/* This inner Box prevents the content from being squeezed during the transition */}
+
+
+
+ {value}
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/features/profile/components/activeDevices/ActiveDevices.tsx b/src/features/profile/components/activeDevices/ActiveDevices.tsx
index ef95546..b96aa77 100644
--- a/src/features/profile/components/activeDevices/ActiveDevices.tsx
+++ b/src/features/profile/components/activeDevices/ActiveDevices.tsx
@@ -10,6 +10,7 @@ import { DeviceMessage, Logout } from 'iconsax-react';
import Logo from '@/components/Logo';
import { CardContainer } from '@/components/CardContainer';
import { PageWrapper } from '../PageWrapper';
+import React from 'react';
export function ActiveDevices() {
const { t } = useTranslation('activeDevices');
@@ -83,9 +84,8 @@ export function ActiveDevices() {
}}
>
{devices.map((device) => (
- <>
+
)}
- >
+
))}
diff --git a/src/features/profile/components/layout/Header.tsx b/src/features/profile/components/layout/Header.tsx
new file mode 100644
index 0000000..cffd51b
--- /dev/null
+++ b/src/features/profile/components/layout/Header.tsx
@@ -0,0 +1,25 @@
+import { Box, Typography } from '@mui/material';
+
+export function Header() {
+ const headers = [{ name: 'محمدحسین برزه گر', phone: '09123456789' }];
+ return (
+
+ {headers.map((h) => (
+
+ {h.name}
+
+ {h.phone}
+
+
+ ))}
+
+ );
+}
diff --git a/src/features/profile/components/layout/Layout.tsx b/src/features/profile/components/layout/Layout.tsx
new file mode 100644
index 0000000..5584405
--- /dev/null
+++ b/src/features/profile/components/layout/Layout.tsx
@@ -0,0 +1,127 @@
+import { useState } from 'react';
+import { Outlet, useLocation } from 'react-router-dom';
+import {
+ Box,
+ useTheme,
+ useMediaQuery,
+ Drawer,
+ AppBar,
+ Toolbar,
+ IconButton,
+} from '@mui/material';
+import { SideNav } from '@rkheftan/harmony-ui';
+import { useTranslation } from 'react-i18next';
+import { HambergerMenu } from 'iconsax-react';
+
+import { Header } from './Header';
+import { getNavConfig } from './navConfigs';
+
+export function Layout() {
+ const theme = useTheme();
+ const isMdUp = useMediaQuery(theme.breakpoints.up('md'));
+ const isLgUp = useMediaQuery(theme.breakpoints.up('lg'));
+ const location = useLocation();
+ const { t } = useTranslation('sideMap');
+ const [drawerOpen, setDrawerOpen] = useState(false);
+
+ const navWidthMd = isLgUp ? 274 : 'min(15vw, 180px)';
+ // const sidebarWidth = isLgUp ? 274 : isMdUp ? 'min(15vw, 180px)' : 0;
+
+ const navConfig = getNavConfig(t);
+
+ return (
+
+
+ {!isMdUp && (
+
+
+ setDrawerOpen(true)}
+ size="medium"
+ sx={{ backgroundColor: '#2979FF1F' }}
+ >
+
+
+
+
+ )}
+
+
+ {isMdUp && (
+
+ }
+ activePath={location.pathname + location.hash}
+ sideNavVariant="full"
+ positioning="absolute"
+ />
+
+ )}
+
+
+
+
+
+
+ {!isMdUp && (
+ setDrawerOpen(false)}
+ >
+
+ }
+ activePath={location.pathname + location.hash}
+ sideNavVariant="full"
+ />
+
+
+ )}
+
+
+ );
+}
diff --git a/src/features/profile/components/layout/Routes.tsx b/src/features/profile/components/layout/Routes.tsx
new file mode 100644
index 0000000..b533c97
--- /dev/null
+++ b/src/features/profile/components/layout/Routes.tsx
@@ -0,0 +1,52 @@
+import { createBrowserRouter, Navigate } from 'react-router-dom';
+import { Layout } from './Layout';
+import { PersonalInformation } from '../userInformation/PersonalInformation';
+import { PhoneNumber } from '../userInformation/PhoneNumber';
+import { SocialMedia } from '../userInformation/SocialMedia';
+import { PasswordSecurity } from '../security/PasswordSecurity';
+import { RecentLogins } from '../security/RecentLogins';
+import { ActiveDevices } from '../activeDevices/ActiveDevices';
+import { Setting } from '../setting/Setting';
+import { Box } from '@mui/material';
+
+export const router = createBrowserRouter([
+ {
+ path: '/',
+ element: ,
+ children: [
+ { path: '/', element: },
+ {
+ path: '/profile',
+ element: (
+
+
+
+
+
+
+
+ ),
+ },
+ {
+ path: '/security',
+ element: (
+
+
+
+
+
+
+
+ ),
+ },
+ { path: '/devices', element: },
+ { path: '/setting', element: },
+ ],
+ },
+]);
diff --git a/src/features/profile/components/layout/navConfigs.tsx b/src/features/profile/components/layout/navConfigs.tsx
new file mode 100644
index 0000000..47e364d
--- /dev/null
+++ b/src/features/profile/components/layout/navConfigs.tsx
@@ -0,0 +1,121 @@
+import {
+ Devices,
+ LocationTick,
+ Mobile,
+ PasswordCheck,
+ Personalcard,
+ ProfileCircle,
+ Setting as SettingIcon,
+ Shield,
+ Sms,
+} from 'iconsax-react';
+import { type TFunction } from 'i18next';
+
+export function getNavConfig(t: TFunction) {
+ return [
+ {
+ text: t('side.account'),
+ getIcon: (selected: boolean) =>
+ selected ? (
+
+ ) : (
+
+ ),
+ path: '/profile',
+ children: [
+ {
+ text: t('side.personalInfo'),
+ getIcon: (sel: boolean) =>
+ sel ? (
+
+ ) : (
+
+ ),
+ path: '/profile#info',
+ },
+ {
+ text: t('side.phoneNumber'),
+ getIcon: (sel: boolean) =>
+ sel ? (
+
+ ) : (
+
+ ),
+ path: '/profile#contact-info',
+ },
+ {
+ text: t('side.email'),
+ getIcon: (sel: boolean) =>
+ sel ? (
+
+ ) : (
+
+ ),
+ path: '/profile#email',
+ },
+ ],
+ },
+ {
+ text: t('side.security'),
+ getIcon: (sel: boolean) =>
+ sel ? (
+
+ ) : (
+
+ ),
+ path: '/security',
+ children: [
+ {
+ text: t('side.password'),
+ getIcon: (sel: boolean) =>
+ sel ? (
+
+ ) : (
+
+ ),
+ path: '/security#password',
+ },
+ {
+ text: t('side.verifiedAddress'),
+ getIcon: (sel: boolean) =>
+ sel ? (
+
+ ) : (
+
+ ),
+ path: '/security#locations',
+ },
+ {
+ text: t('side.recentLogins'),
+ getIcon: (sel: boolean) =>
+ sel ? (
+
+ ) : (
+
+ ),
+ path: '/security#sessions',
+ },
+ ],
+ },
+ {
+ text: t('side.activeDevices'),
+ getIcon: (sel: boolean) =>
+ sel ? (
+
+ ) : (
+
+ ),
+ path: '/devices',
+ },
+ {
+ text: t('side.settings'),
+ getIcon: (sel: boolean) =>
+ sel ? (
+
+ ) : (
+
+ ),
+ path: '/setting',
+ },
+ ];
+}
diff --git a/src/features/profile/components/security/PasswordDialog.tsx b/src/features/profile/components/security/PasswordDialog.tsx
new file mode 100644
index 0000000..13c217f
--- /dev/null
+++ b/src/features/profile/components/security/PasswordDialog.tsx
@@ -0,0 +1,179 @@
+import {
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+ TextField,
+ IconButton,
+ Box,
+ Button,
+ Link,
+} from '@mui/material';
+import { CloseCircle, Refresh } from 'iconsax-react';
+import { PasswordValidationItem } from './PasswordValidation';
+
+interface PasswordDialogProps {
+ open: boolean;
+ fullScreen: boolean;
+ handleClose: () => void;
+ password: string;
+ setPassword: (val: string) => void;
+ confirmPassword: string;
+ setConfirmPassword: (val: string) => void;
+ currentPassword: string;
+ setCurrentPassword: (val: string) => void;
+ showValidation: boolean;
+ hasNumber: boolean;
+ hasMinLength: boolean;
+ hasUpperAndLower: boolean;
+ hasSpecialChar: boolean;
+ matchPassword: boolean;
+ loading: boolean;
+ handleShowAlert: () => void;
+ changePassword: boolean;
+ t: (key: string) => string;
+}
+
+export function PasswordDialog({
+ open,
+ fullScreen,
+ handleClose,
+ password,
+ setPassword,
+ confirmPassword,
+ setConfirmPassword,
+ currentPassword,
+ setCurrentPassword,
+ showValidation,
+ hasNumber,
+ hasMinLength,
+ hasUpperAndLower,
+ hasSpecialChar,
+ matchPassword,
+ loading,
+ handleShowAlert,
+ changePassword,
+ t,
+}: PasswordDialogProps) {
+ return (
+
+ );
+}
diff --git a/src/features/profile/components/security/PasswordSecurity.tsx b/src/features/profile/components/security/PasswordSecurity.tsx
index 4c65dd5..cf908f6 100644
--- a/src/features/profile/components/security/PasswordSecurity.tsx
+++ b/src/features/profile/components/security/PasswordSecurity.tsx
@@ -1,30 +1,23 @@
+import { useState, useEffect } from 'react';
import {
Box,
Typography,
Button,
- Dialog,
- DialogTitle,
- DialogContent,
- TextField,
- DialogActions,
- IconButton,
+ useTheme,
useMediaQuery,
- Link,
} from '@mui/material';
-import { useTheme } from '@mui/material/styles';
import { useTranslation } from 'react-i18next';
-import { useState, useEffect } from 'react';
-import { CloseCircle, Refresh } from 'iconsax-react';
-import { PasswordValidationItem } from './PasswordValidation';
-import { Toast } from '@/components/Toast';
-import Logo from '@/components/Logo';
import { CardContainer } from '@/components/CardContainer';
import { PageWrapper } from '../PageWrapper';
+import Logo from '@/components/Logo';
+import { PasswordDialog } from './PasswordDialog';
+import { Toast } from '@/components/Toast';
export function PasswordSecurity() {
const theme = useTheme();
const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
const { t } = useTranslation('security');
+
const [open, setOpen] = useState(false);
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
@@ -42,21 +35,6 @@ export function PasswordSecurity() {
hasNumber && hasMinLength && hasUpperAndLower && hasSpecialChar;
const matchPassword = password === confirmPassword;
- const handleOpen = () => setOpen(true);
- const handleClose = () => setOpen(false);
- const handleShowAlert = () => {
- setLoading(true);
- setTimeout(() => {
- setLoading(false);
- setShowPasswordAlert(true);
- setChangePassword(true);
- handleClose();
- setPassword('');
- setConfirmPassword('');
- setCurrentPassword('');
- }, 1500);
- };
-
useEffect(() => {
if (password) {
if (!validPassword) {
@@ -70,6 +48,22 @@ export function PasswordSecurity() {
}
}, [password, validPassword]);
+ const handleOpen = () => setOpen(true);
+ const handleClose = () => setOpen(false);
+
+ const handleShowAlert = () => {
+ setLoading(true);
+ setTimeout(() => {
+ setLoading(false);
+ setShowPasswordAlert(true);
+ setChangePassword(true);
+ handleClose();
+ setPassword('');
+ setConfirmPassword('');
+ setCurrentPassword('');
+ }, 1500);
+ };
+
return (
@@ -80,35 +74,21 @@ export function PasswordSecurity() {
title={t('securityForm.password')}
subtitle={t('securityForm.determinePassword')}
action={
- changePassword ? (
-
- ) : (
-
- )
+
}
>
-
- {changePassword ? (
-
-
- {t('securityForm.activePassword')}
-
-
- {t('securityForm.lastChange')}
-
-
- ) : (
-
- {t('securityForm.notDeterminedPassword')}
+ {changePassword ? (
+
+
+ {t('securityForm.activePassword')}
- )}
-
-
+
+ ) : (
+
+ {t('securityForm.notDeterminedPassword')}
+
+ )}
+
+ {/* New PasswordDialog component usage */}
+
-
-
-
-
-
-
- {t('securityForm.addPassword')}
-
-
-
- {changePassword ? (
-
- setCurrentPassword(e.target.value)}
- sx={{
- '& .MuiOutlinedInput-root': { borderRadius: 2 },
- mt: 2,
- }}
- />
-
- {t('securityForm.forgetPassword')}
-
- setPassword(e.target.value)}
- sx={{ '& .MuiOutlinedInput-root': { borderRadius: 2 } }}
- />
- {password && showValidation && (
-
-
-
-
-
-
-
-
- )}
- setConfirmPassword(e.target.value)}
- error={confirmPassword.length > 0 && !matchPassword}
- helperText={
- confirmPassword.length > 0 && !matchPassword
- ? t('securityForm.notCompatibility')
- : ' '
- }
- sx={{ '& .MuiOutlinedInput-root': { borderRadius: 2 } }}
- />
-
- ) : (
-
- setPassword(e.target.value)}
- sx={{
- '& .MuiOutlinedInput-root': { borderRadius: 2 },
- mt: 2,
- }}
- />
- {password && showValidation && (
-
-
-
-
-
-
-
-
- )}
- setConfirmPassword(e.target.value)}
- error={confirmPassword.length > 0 && !matchPassword}
- helperText={
- confirmPassword.length > 0 && !matchPassword
- ? t('securityForm.notCompatibility')
- : ' '
- }
- sx={{ '& .MuiOutlinedInput-root': { borderRadius: 2 } }}
- />
-
- )}
-
-
-
-
+ handleClose={handleClose}
+ password={password}
+ setPassword={setPassword}
+ confirmPassword={confirmPassword}
+ setConfirmPassword={setConfirmPassword}
+ currentPassword={currentPassword}
+ setCurrentPassword={setCurrentPassword}
+ showValidation={showValidation}
+ hasNumber={hasNumber}
+ hasMinLength={hasMinLength}
+ hasUpperAndLower={hasUpperAndLower}
+ hasSpecialChar={hasSpecialChar}
+ matchPassword={matchPassword}
+ loading={loading}
+ handleShowAlert={handleShowAlert}
+ changePassword={changePassword}
+ t={t}
+ />
+
{data.map((d) => (
- <>
+
- >
+
))}
diff --git a/src/features/profile/components/security/Security.tsx b/src/features/profile/components/security/Security.tsx
deleted file mode 100644
index 5ef2186..0000000
--- a/src/features/profile/components/security/Security.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import { PasswordSecurity } from './PasswordSecurity';
-import { RecentLogins } from './RecentLogins';
-import { Box } from '@mui/material';
-
-export function Security() {
- return (
-
-
-
-
- );
-}
diff --git a/src/features/profile/components/userInformation/PersonalInformation.tsx b/src/features/profile/components/userInformation/PersonalInformation.tsx
index 80a531d..829f620 100644
--- a/src/features/profile/components/userInformation/PersonalInformation.tsx
+++ b/src/features/profile/components/userInformation/PersonalInformation.tsx
@@ -1,5 +1,5 @@
import { useState } from 'react';
-import { Box, Button } from '@mui/material';
+import { Box, Button, useTheme, useMediaQuery } from '@mui/material';
import { useTranslation } from 'react-i18next';
import { CardContainer } from '@/components/CardContainer';
import { ProfileImage } from './personlInformation/ProfileImage';
@@ -13,6 +13,8 @@ export function PersonalInformation() {
const [isEditing, setIsEditing] = useState(false);
const [gender, setGender] = useState('');
const [uploadedImageUrl, setUploadedImageUrl] = useState(null);
+ const theme = useTheme();
+ const isMdUp = useMediaQuery(theme.breakpoints.up('lg'));
const initialData = {
firstName: 'محمد حسین',
@@ -30,28 +32,43 @@ export function PersonalInformation() {
if (isEditing) {
setData((prev) => ({
...prev,
- gender: gender === 'male' ? 'مرد' : gender === 'female' ? 'زن' : '',
+ gender:
+ gender === 'male'
+ ? t('settingForm.man')
+ : gender === 'female'
+ ? t('settingForm.woman')
+ : '',
}));
} else {
setGender(
- data.gender === 'مرد' ? 'male' : data.gender === 'زن' ? 'female' : '',
+ data.gender === t('settingForm.man')
+ ? 'male'
+ : data.gender === t('settingForm.woman')
+ ? 'female'
+ : '',
);
}
};
return (
-
-
-
-
+ {isMdUp && (
+ <>
+
+
+
+
+ >
+ )}
(null);
+ const inputRef = useRef(null);
+ const [error, setError] = useState();
+ const [touched, setTouched] = useState(false);
+ const inputError: boolean = touched && !!error;
+
+ const isPhoneValid = (code: string, phone: string) => {
+ const phoneNum = parsePhoneNumberFromString(code + phone);
+ return phoneNum && phoneNum.isValid();
+ };
+
+ const handleBlur = () => {
+ setTouched(true);
+ if (!phoneNumber) {
+ setError(t('settingForm.thisFieldIsRequired'));
+ }
+ if (!isPhoneValid(countryCode, phoneNumber)) {
+ setError(t('settingForm.phoneNumberIsInvalid'));
+ } else {
+ setError(undefined);
+ }
+ };
const toggleEdit = () => {
setIsEditing((prev) => {
@@ -37,16 +59,6 @@ export function PhoneNumber() {
});
};
- const handlePhoneNumberChange = (e: ChangeEvent) => {
- const value = e.target.value.replace(/\D/g, '');
- setPhoneNumber(value);
- };
-
- const handleVerificationCodeChange = (e: ChangeEvent) => {
- const value = e.target.value.replace(/\D/g, '');
- setVerificationCode(value);
- };
-
const handleSendCode = () => {
if (!phoneNumber) return;
setButtonState('counting');
@@ -79,209 +91,39 @@ export function PhoneNumber() {
subtitle={t('settingForm.descriptionPhoneNumber')}
highlighted={isEditing}
action={
-
- {isEditing && (
-
- )}
-
-
+
}
>
{isEditing ? (
-
-
-
- {t('settingForm.editPhoneNumber')}
-
-
- {t('settingForm.phoneNumberText')}(
- {phones.map((p) => p.withCode)}){t('settingForm.verb')}
-
-
-
-
- {
- setButtonState('default');
- setPhoneNumber('');
- }}
- sx={{ mr: 1 }}
- >
-
-
- ) : null,
- }}
- />
-
- {isVerified ? (
-
-
-
- {t('settingForm.successButton')}
-
-
- ) : (
-
- )}
-
-
- {buttonState === 'counting' && !isVerified && (
-
-
-
-
-
- )}
-
- setShowToast(false)}
- >
- {t('settingForm.successfulChangePhone')}
-
-
+ }
+ inputRef={inputRef as React.RefObject}
+ phones={phones}
+ showToast={showToast}
+ setShowToast={setShowToast}
+ t={t}
+ />
) : (
-
- {phones.map((item, index) => (
-
-
- {item.phone}
-
-
- {item.time}
-
-
- ))}
-
+
)}
diff --git a/src/features/profile/components/userInformation/SocialMedia.tsx b/src/features/profile/components/userInformation/SocialMedia.tsx
index 7882a6f..a393023 100644
--- a/src/features/profile/components/userInformation/SocialMedia.tsx
+++ b/src/features/profile/components/userInformation/SocialMedia.tsx
@@ -1,42 +1,12 @@
-import React, { useState } from 'react';
-import {
- Box,
- Button,
- Typography,
- Dialog,
- DialogTitle,
- DialogContent,
- IconButton,
- TextField,
- Menu,
- MenuItem,
- ListItemIcon,
- ListItemText,
- useMediaQuery,
-} from '@mui/material';
-import Slide from '@mui/material/Slide';
-import type { TransitionProps } from '@mui/material/transitions';
-import type { Theme } from '@mui/material/styles';
+import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { CardContainer } from '@/components/CardContainer';
-
-import {
- Google,
- Apple,
- Sms,
- Trash,
- CloseSquare,
- Message,
- ArrowDown3,
-} from 'iconsax-react';
import { PageWrapper } from '../PageWrapper';
-
-const MobileSlide = React.forwardRef(function MobileSlide(
- props: TransitionProps & { children: React.ReactElement },
- ref: React.Ref,
-) {
- return ;
-});
+import SocialMediaList from './socialMedia/SocialMediaList';
+import SocialMediaMenu from './socialMedia/SocialMediaMenu';
+import SocialMediaDialog from './socialMedia/SocialMediaDialog';
+import useMediaQuery from '@mui/material/useMediaQuery';
+import type { Theme } from '@mui/material/styles';
export function SocialMedia() {
const { t } = useTranslation('profileSetting');
@@ -44,9 +14,7 @@ export function SocialMedia() {
const [openDialog, setOpenDialog] = useState(false);
const [emailInput, setEmailInput] = useState('');
const [emailError, setEmailError] = useState(false);
- const [anchor, setAnchor] = useState(null);
- const openMenu = Boolean(anchor);
const fullScreen = useMediaQuery((theme: Theme) =>
theme.breakpoints.down('sm'),
);
@@ -58,19 +26,6 @@ export function SocialMedia() {
| 'lg'
| 'xl';
- const handleOpenDialog = () => setOpenDialog(true);
- const handleCloseDialog = () => setOpenDialog(false);
-
- const handleClickMenu = (e: React.MouseEvent) =>
- setAnchor(e.currentTarget);
- const handleCloseMenu = () => setAnchor(null);
-
- const handleEmailChange = (e: React.ChangeEvent) => {
- const value = e.target.value;
- setEmailInput(value);
- setEmailError(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value));
- };
-
const emailList = [
{ email: 'emailtemp@email.com', provider: 'email', time: '1 ماه پیش' },
{ email: 'emailtemp@gmail.com', provider: 'google', time: '1 ماه پیش' },
@@ -83,286 +38,21 @@ export function SocialMedia() {
title={t('settingForm.titleSocial')}
subtitle={t('settingForm.descriptionSocial')}
action={
-
-
-
-
-
-
-
+ setOpenDialog(true)} />
}
>
-
- {emailList.map((item, index) => (
-
-
- {item.provider === 'google' && (
-
- )}
- {item.provider === 'apple' && (
-
- )}
- {item.provider === 'email' && (
-
- )}
-
-
-
- {item.email}
-
-
- {item.time}
-
-
-
-
-
-
-
-
- ))}
-
-
-
+ setOpenDialog(false)}
+ t={t}
+ emailInput={emailInput}
+ setEmailInput={setEmailInput}
+ emailError={emailError}
+ setEmailError={setEmailError}
fullScreen={fullScreen}
- maxWidth={computedMaxWidth}
- scroll="paper"
- keepMounted
- TransitionComponent={fullScreen ? MobileSlide : undefined}
- PaperProps={{
- sx: {
- m: { xs: 0, sm: 2 },
- borderRadius: { xs: 0, sm: 2 },
- width: { xs: '100%', sm: 'auto' },
- height: { xs: '100%', sm: 'auto' },
- },
- }}
- >
-
-
-
-
- {t('settingForm.addEmailButton')}
-
-
-
-
-
- {t('settingForm.newEmail')}
-
-
- {t('settingForm.dialogHeader')}
-
-
-
-
-
-
-
-
-
-
- {t('settingForm.or')}
-
-
-
-
-
-
-
-
-
-
+ computedMaxWidth={computedMaxWidth}
+ />
);
diff --git a/src/features/profile/components/userInformation/phoneNumber/PhoneActionButtons.tsx b/src/features/profile/components/userInformation/phoneNumber/PhoneActionButtons.tsx
new file mode 100644
index 0000000..8329ebb
--- /dev/null
+++ b/src/features/profile/components/userInformation/phoneNumber/PhoneActionButtons.tsx
@@ -0,0 +1,49 @@
+import { Box, Button } from '@mui/material';
+
+export default function PhoneActionButtons({
+ isEditing,
+ toggleEdit,
+ t,
+}: {
+ isEditing: boolean;
+ toggleEdit: () => void;
+ t: (key: string) => string;
+}) {
+ return (
+
+ {isEditing && (
+
+ )}
+
+
+ );
+}
diff --git a/src/features/profile/components/userInformation/phoneNumber/PhoneDisplay.tsx b/src/features/profile/components/userInformation/phoneNumber/PhoneDisplay.tsx
new file mode 100644
index 0000000..a922ae4
--- /dev/null
+++ b/src/features/profile/components/userInformation/phoneNumber/PhoneDisplay.tsx
@@ -0,0 +1,31 @@
+import { Box, Typography } from '@mui/material';
+
+export default function PhoneDisplay({
+ phones,
+}: {
+ phones: { phone: string; time: string }[];
+}) {
+ return (
+
+ {phones.map((item, index) => (
+
+
+ {item.phone}
+
+
+ {item.time}
+
+
+ ))}
+
+ );
+}
diff --git a/src/features/profile/components/userInformation/phoneNumber/PhoneEditForm.tsx b/src/features/profile/components/userInformation/phoneNumber/PhoneEditForm.tsx
new file mode 100644
index 0000000..2dd7695
--- /dev/null
+++ b/src/features/profile/components/userInformation/phoneNumber/PhoneEditForm.tsx
@@ -0,0 +1,208 @@
+import { Box, Typography, TextField, IconButton, Button } from '@mui/material';
+import { Edit, Refresh, TickCircle } from 'iconsax-react';
+import { CountDownTimer } from '@/components/CountDownTimer';
+import { Toast } from '@/components/Toast';
+import { CountryCodeSelector } from '../../CountryCodeSelector';
+
+export default function PhoneEditForm({
+ phoneNumber,
+ setPhoneNumber,
+ countryCode,
+ setCountryCode,
+ verificationCode,
+ setVerificationCode,
+ isVerified,
+ isVerifying,
+ buttonState,
+ setButtonState,
+ handleSendCode,
+ handleVerifyClick,
+ error,
+ inputError,
+ handleBlur,
+ textFieldRef,
+ inputRef,
+ phones,
+ showToast,
+ setShowToast,
+ t,
+}: {
+ phoneNumber: string;
+ setPhoneNumber: (v: string) => void;
+ countryCode: string;
+ setCountryCode: (v: string) => void;
+ verificationCode: string;
+ setVerificationCode: (v: string) => void;
+ isVerified: boolean;
+ isVerifying: boolean;
+ buttonState: 'default' | 'counting';
+ setButtonState: (v: 'default' | 'counting') => void;
+ handleSendCode: () => void;
+ handleVerifyClick: () => void;
+ error?: string;
+ inputError: boolean;
+ handleBlur: () => void;
+ textFieldRef: React.RefObject;
+ inputRef: React.RefObject;
+ phones: { phone: string; time: string; withCode: string }[];
+ showToast: boolean;
+ setShowToast: (v: boolean) => void;
+ t: (key: string) => string;
+}) {
+ return (
+
+
+ {t('settingForm.editPhoneNumber')}
+
+ {t('settingForm.phoneNumberText')}({phones.map((p) => p.withCode)})
+ {t('settingForm.verb')}
+
+
+
+
+ setPhoneNumber(e.target.value)}
+ sx={{ flex: '1 1 240px', minWidth: 0 }}
+ placeholder="09123456789"
+ inputProps={{ dir: 'rtl', style: { textAlign: 'right' } }}
+ InputProps={{
+ endAdornment:
+ buttonState === 'counting' ? (
+ {
+ setButtonState('default');
+ setPhoneNumber('');
+ }}
+ sx={{ mr: 1 }}
+ >
+
+
+ ) : null,
+ }}
+ slotProps={{
+ htmlInput: {
+ dir: 'auto',
+ sx: { lineHeight: 1.5, paddingX: 0 },
+ },
+ input: {
+ endAdornment: (
+
+ ),
+ },
+ }}
+ />
+
+ {isVerified ? (
+
+
+
+ {t('settingForm.successButton')}
+
+
+ ) : (
+
+ )}
+
+
+ {buttonState === 'counting' && !isVerified && (
+
+
+ setVerificationCode(e.target.value.replace(/\D/g, ''))
+ }
+ sx={{ flex: '1 1 240px', minWidth: 0 }}
+ placeholder={t('settingForm.verificationCode')}
+ inputProps={{ dir: 'rtl', style: { textAlign: 'right' } }}
+ />
+
+
+
+ )}
+
+ setShowToast(false)}
+ >
+ {t('settingForm.successfulChangePhone')}
+
+
+ );
+}
diff --git a/src/features/profile/components/userInformation/socialMedia/SocialMediaDialog.tsx b/src/features/profile/components/userInformation/socialMedia/SocialMediaDialog.tsx
new file mode 100644
index 0000000..0f6054e
--- /dev/null
+++ b/src/features/profile/components/userInformation/socialMedia/SocialMediaDialog.tsx
@@ -0,0 +1,187 @@
+import React from 'react';
+import {
+ Box,
+ Button,
+ Dialog,
+ DialogContent,
+ DialogTitle,
+ IconButton,
+ TextField,
+ Typography,
+} from '@mui/material';
+import Slide from '@mui/material/Slide';
+import type { TransitionProps } from '@mui/material/transitions';
+import { CloseSquare, Google, Apple } from 'iconsax-react';
+
+const MobileSlide = React.forwardRef(function MobileSlide(
+ props: TransitionProps & { children: React.ReactElement },
+ ref: React.Ref,
+) {
+ return ;
+});
+
+export default function SocialMediaDialog({
+ open,
+ onClose,
+ t,
+ emailInput,
+ setEmailInput,
+ emailError,
+ setEmailError,
+ fullScreen,
+ computedMaxWidth,
+}: {
+ open: boolean;
+ onClose: () => void;
+ t: (key: string) => string;
+ emailInput: string;
+ setEmailInput: (val: string) => void;
+ emailError: boolean;
+ setEmailError: (val: boolean) => void;
+ fullScreen: boolean;
+ computedMaxWidth: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
+}) {
+ const handleEmailChange = (e: React.ChangeEvent) => {
+ const value = e.target.value;
+ setEmailInput(value);
+ setEmailError(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value));
+ };
+
+ return (
+
+ );
+}
diff --git a/src/features/profile/components/userInformation/socialMedia/SocialMediaList.tsx b/src/features/profile/components/userInformation/socialMedia/SocialMediaList.tsx
new file mode 100644
index 0000000..75c83de
--- /dev/null
+++ b/src/features/profile/components/userInformation/socialMedia/SocialMediaList.tsx
@@ -0,0 +1,63 @@
+import { Box, Typography, IconButton } from '@mui/material';
+import { Google, Apple, Sms, Trash } from 'iconsax-react';
+
+export default function SocialMediaList({
+ emailList,
+}: {
+ t: (key: string) => string;
+ emailList: readonly {
+ email: string;
+ provider: 'email' | 'google' | 'apple';
+ time: string;
+ }[];
+}) {
+ return (
+
+ {emailList.map((item, index) => (
+
+
+ {item.provider === 'google' && (
+
+ )}
+ {item.provider === 'apple' && (
+
+ )}
+ {item.provider === 'email' && (
+
+ )}
+
+
+
+ {item.email}
+
+
+ {item.time}
+
+
+
+
+
+
+
+
+ ))}
+
+ );
+}
diff --git a/src/features/profile/components/userInformation/socialMedia/SocialMediaMenu.tsx b/src/features/profile/components/userInformation/socialMedia/SocialMediaMenu.tsx
new file mode 100644
index 0000000..006550b
--- /dev/null
+++ b/src/features/profile/components/userInformation/socialMedia/SocialMediaMenu.tsx
@@ -0,0 +1,117 @@
+import React, { useState } from 'react';
+import {
+ Box,
+ Button,
+ Menu,
+ MenuItem,
+ ListItemIcon,
+ ListItemText,
+} from '@mui/material';
+import { Message, Google, Apple, ArrowDown3 } from 'iconsax-react';
+
+export default function SocialMediaMenu({
+ t,
+ onOpenDialog,
+}: {
+ t: (key: string) => string;
+ onOpenDialog: () => void;
+}) {
+ const [anchor, setAnchor] = useState(null);
+ const openMenu = Boolean(anchor);
+
+ const handleClickMenu = (e: React.MouseEvent) =>
+ setAnchor(e.currentTarget);
+ const handleCloseMenu = () => setAnchor(null);
+
+ return (
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/features/profile/data/countries.ts b/src/features/profile/data/countries.ts
new file mode 100644
index 0000000..2b05f9e
--- /dev/null
+++ b/src/features/profile/data/countries.ts
@@ -0,0 +1,271 @@
+export interface Country {
+ code: string;
+ label: string;
+ phone: string;
+}
+
+export interface Country {
+ code: string;
+ label: string;
+ phone: string;
+}
+
+export const countries: readonly Country[] = [
+ { code: 'AF', label: 'country.afghanistan', phone: '+93' },
+ { code: 'AX', label: 'country.aland_islands', phone: '+358' },
+ { code: 'AL', label: 'country.albania', phone: '+355' },
+ { code: 'DZ', label: 'country.algeria', phone: '+213' },
+ { code: 'AS', label: 'country.american_samoa', phone: '+1684' },
+ { code: 'AD', label: 'country.andorra', phone: '+376' },
+ { code: 'AO', label: 'country.angola', phone: '+244' },
+ { code: 'AI', label: 'country.anguilla', phone: '+1264' },
+ { code: 'AQ', label: 'country.antarctica', phone: '+672' },
+ { code: 'AG', label: 'country.antigua_and_barbuda', phone: '+1268' },
+ { code: 'AR', label: 'country.argentina', phone: '+54' },
+ { code: 'AM', label: 'country.armenia', phone: '+374' },
+ { code: 'AW', label: 'country.aruba', phone: '+297' },
+ { code: 'AU', label: 'country.australia', phone: '+61' },
+ { code: 'AT', label: 'country.austria', phone: '+43' },
+ { code: 'AZ', label: 'country.azerbaijan', phone: '+994' },
+ { code: 'BS', label: 'country.bahamas', phone: '+1242' },
+ { code: 'BH', label: 'country.bahrain', phone: '+973' },
+ { code: 'BD', label: 'country.bangladesh', phone: '+880' },
+ { code: 'BB', label: 'country.barbados', phone: '+1246' },
+ { code: 'BY', label: 'country.belarus', phone: '+375' },
+ { code: 'BE', label: 'country.belgium', phone: '+32' },
+ { code: 'BZ', label: 'country.belize', phone: '+501' },
+ { code: 'BJ', label: 'country.benin', phone: '+229' },
+ { code: 'BM', label: 'country.bermuda', phone: '+1441' },
+ { code: 'BT', label: 'country.bhutan', phone: '+975' },
+ { code: 'BO', label: 'country.bolivia', phone: '+591' },
+ { code: 'BA', label: 'country.bosnia_and_herzegovina', phone: '+387' },
+ { code: 'BW', label: 'country.botswana', phone: '+267' },
+ { code: 'BR', label: 'country.brazil', phone: '+55' },
+ {
+ code: 'IO',
+ label: 'country.british_indian_ocean_territory',
+ phone: '+246',
+ },
+ { code: 'VG', label: 'country.british_virgin_islands', phone: '+1284' },
+ { code: 'BN', label: 'country.brunei', phone: '+673' },
+ { code: 'BG', label: 'country.bulgaria', phone: '+359' },
+ { code: 'BF', label: 'country.burkina_faso', phone: '+226' },
+ { code: 'BI', label: 'country.burundi', phone: '+257' },
+ { code: 'KH', label: 'country.cambodia', phone: '+855' },
+ { code: 'CM', label: 'country.cameroon', phone: '+237' },
+ { code: 'CA', label: 'country.canada', phone: '+1' },
+ { code: 'CV', label: 'country.cape_verde', phone: '+238' },
+ { code: 'KY', label: 'country.cayman_islands', phone: '+1345' },
+ { code: 'CF', label: 'country.central_african_republic', phone: '+236' },
+ { code: 'TD', label: 'country.chad', phone: '+235' },
+ { code: 'CL', label: 'country.chile', phone: '+56' },
+ { code: 'CN', label: 'country.china', phone: '+86' },
+ { code: 'CX', label: 'country.christmas_island', phone: '+61' },
+ { code: 'CC', label: 'country.cocos_keeling_islands', phone: '+61' },
+ { code: 'CO', label: 'country.colombia', phone: '+57' },
+ { code: 'KM', label: 'country.comoros', phone: '+269' },
+ { code: 'CG', label: 'country.congo_brazzaville', phone: '+242' },
+ { code: 'CD', label: 'country.congo_kinshasa', phone: '+243' },
+ { code: 'CK', label: 'country.cook_islands', phone: '+682' },
+ { code: 'CR', label: 'country.costa_rica', phone: '+506' },
+ { code: 'CI', label: 'country.cote_divoire', phone: '+225' },
+ { code: 'HR', label: 'country.croatia', phone: '+385' },
+ { code: 'CU', label: 'country.cuba', phone: '+53' },
+ { code: 'CW', label: 'country.curacao', phone: '+599' },
+ { code: 'CY', label: 'country.cyprus', phone: '+357' },
+ { code: 'CZ', label: 'country.czech_republic', phone: '+420' },
+ { code: 'DK', label: 'country.denmark', phone: '+45' },
+ { code: 'DJ', label: 'country.djibouti', phone: '+253' },
+ { code: 'DM', label: 'country.dominica', phone: '+1767' },
+ { code: 'DO', label: 'country.dominican_republic', phone: '+1' },
+ { code: 'EC', label: 'country.ecuador', phone: '+593' },
+ { code: 'EG', label: 'country.egypt', phone: '+20' },
+ { code: 'SV', label: 'country.el_salvador', phone: '+503' },
+ { code: 'GQ', label: 'country.equatorial_guinea', phone: '+240' },
+ { code: 'ER', label: 'country.eritrea', phone: '+291' },
+ { code: 'EE', label: 'country.estonia', phone: '+372' },
+ { code: 'SZ', label: 'country.eswatini', phone: '+268' },
+ { code: 'ET', label: 'country.ethiopia', phone: '+251' },
+ { code: 'FK', label: 'country.falkland_islands', phone: '+500' },
+ { code: 'FO', label: 'country.faroe_islands', phone: '+298' },
+ { code: 'FJ', label: 'country.fiji', phone: '+679' },
+ { code: 'FI', label: 'country.finland', phone: '+358' },
+ { code: 'FR', label: 'country.france', phone: '+33' },
+ { code: 'GF', label: 'country.french_guiana', phone: '+594' },
+ { code: 'PF', label: 'country.french_polynesia', phone: '+689' },
+ { code: 'GA', label: 'country.gabon', phone: '+241' },
+ { code: 'GM', label: 'country.gambia', phone: '+220' },
+ { code: 'GE', label: 'country.georgia', phone: '+995' },
+ { code: 'DE', label: 'country.germany', phone: '+49' },
+ { code: 'GH', label: 'country.ghana', phone: '+233' },
+ { code: 'GI', label: 'country.gibraltar', phone: '+350' },
+ { code: 'GR', label: 'country.greece', phone: '+30' },
+ { code: 'GL', label: 'country.greenland', phone: '+299' },
+ { code: 'GD', label: 'country.grenada', phone: '+1473' },
+ { code: 'GP', label: 'country.guadeloupe', phone: '+590' },
+ { code: 'GU', label: 'country.guam', phone: '+1671' },
+ { code: 'GT', label: 'country.guatemala', phone: '+502' },
+ { code: 'GG', label: 'country.guernsey', phone: '+44' },
+ { code: 'GN', label: 'country.guinea', phone: '+224' },
+ { code: 'GW', label: 'country.guinea_bissau', phone: '+245' },
+ { code: 'GY', label: 'country.guyana', phone: '+592' },
+ { code: 'HT', label: 'country.haiti', phone: '+509' },
+ { code: 'HN', label: 'country.honduras', phone: '+504' },
+ { code: 'HK', label: 'country.hong_kong', phone: '+852' },
+ { code: 'HU', label: 'country.hungary', phone: '+36' },
+ { code: 'IS', label: 'country.iceland', phone: '+354' },
+ { code: 'IN', label: 'country.india', phone: '+91' },
+ { code: 'ID', label: 'country.indonesia', phone: '+62' },
+ { code: 'IR', label: 'country.iran', phone: '+98' },
+ { code: 'IQ', label: 'country.iraq', phone: '+964' },
+ { code: 'IE', label: 'country.ireland', phone: '+353' },
+ { code: 'IM', label: 'country.isle_of_man', phone: '+44' },
+ { code: 'IL', label: 'country.israel', phone: '+972' },
+ { code: 'IT', label: 'country.italy', phone: '+39' },
+ { code: 'JM', label: 'country.jamaica', phone: '+1876' },
+ { code: 'JP', label: 'country.japan', phone: '+81' },
+ { code: 'JE', label: 'country.jersey', phone: '+44' },
+ { code: 'JO', label: 'country.jordan', phone: '+962' },
+ { code: 'KZ', label: 'country.kazakhstan', phone: '+7' },
+ { code: 'KE', label: 'country.kenya', phone: '+254' },
+ { code: 'KI', label: 'country.kiribati', phone: '+686' },
+ { code: 'XK', label: 'country.kosovo', phone: '+383' },
+ { code: 'KW', label: 'country.kuwait', phone: '+965' },
+ { code: 'KG', label: 'country.kyrgyzstan', phone: '+996' },
+ { code: 'LA', label: 'country.laos', phone: '+856' },
+ { code: 'LV', label: 'country.latvia', phone: '+371' },
+ { code: 'LB', label: 'country.lebanon', phone: '+961' },
+ { code: 'LS', label: 'country.lesotho', phone: '+266' },
+ { code: 'LR', label: 'country.liberia', phone: '+231' },
+ { code: 'LY', label: 'country.libya', phone: '+218' },
+ { code: 'LI', label: 'country.liechtenstein', phone: '+423' },
+ { code: 'LT', label: 'country.lithuania', phone: '+370' },
+ { code: 'LU', label: 'country.luxembourg', phone: '+352' },
+ { code: 'MO', label: 'country.macau', phone: '+853' },
+ { code: 'MG', label: 'country.madagascar', phone: '+261' },
+ { code: 'MW', label: 'country.malawi', phone: '+265' },
+ { code: 'MY', label: 'country.malaysia', phone: '+60' },
+ { code: 'MV', label: 'country.maldives', phone: '+960' },
+ { code: 'ML', label: 'country.mali', phone: '+223' },
+ { code: 'MT', label: 'country.malta', phone: '+356' },
+ { code: 'MH', label: 'country.marshall_islands', phone: '+692' },
+ { code: 'MQ', label: 'country.martinique', phone: '+596' },
+ { code: 'MR', label: 'country.mauritania', phone: '+222' },
+ { code: 'MU', label: 'country.mauritius', phone: '+230' },
+ { code: 'YT', label: 'country.mayotte', phone: '+262' },
+ { code: 'MX', label: 'country.mexico', phone: '+52' },
+ { code: 'FM', label: 'country.micronesia', phone: '+691' },
+ { code: 'MD', label: 'country.moldova', phone: '+373' },
+ { code: 'MC', label: 'country.monaco', phone: '+377' },
+ { code: 'MN', label: 'country.mongolia', phone: '+976' },
+ { code: 'ME', label: 'country.montenegro', phone: '+382' },
+ { code: 'MS', label: 'country.montserrat', phone: '+1664' },
+ { code: 'MA', label: 'country.morocco', phone: '+212' },
+ { code: 'MZ', label: 'country.mozambique', phone: '+258' },
+ { code: 'MM', label: 'country.myanmar', phone: '+95' },
+ { code: 'NA', label: 'country.namibia', phone: '+264' },
+ { code: 'NR', label: 'country.nauru', phone: '+674' },
+ { code: 'NP', label: 'country.nepal', phone: '+977' },
+ { code: 'NL', label: 'country.netherlands', phone: '+31' },
+ { code: 'NC', label: 'country.new_caledonia', phone: '+687' },
+ { code: 'NZ', label: 'country.new_zealand', phone: '+64' },
+ { code: 'NI', label: 'country.nicaragua', phone: '+505' },
+ { code: 'NE', label: 'country.niger', phone: '+227' },
+ { code: 'NG', label: 'country.nigeria', phone: '+234' },
+ { code: 'NU', label: 'country.niue', phone: '+683' },
+ { code: 'NF', label: 'country.norfolk_island', phone: '+672' },
+ { code: 'KP', label: 'country.north_korea', phone: '+850' },
+ { code: 'MK', label: 'country.north_macedonia', phone: '+389' },
+ { code: 'MP', label: 'country.northern_mariana_islands', phone: '+1670' },
+ { code: 'NO', label: 'country.norway', phone: '+47' },
+ { code: 'OM', label: 'country.oman', phone: '+968' },
+ { code: 'PK', label: 'country.pakistan', phone: '+92' },
+ { code: 'PW', label: 'country.palau', phone: '+680' },
+ { code: 'PS', label: 'country.palestine', phone: '+970' },
+ { code: 'PA', label: 'country.panama', phone: '+507' },
+ { code: 'PG', label: 'country.papua_new_guinea', phone: '+675' },
+ { code: 'PY', label: 'country.paraguay', phone: '+595' },
+ { code: 'PE', label: 'country.peru', phone: '+51' },
+ { code: 'PH', label: 'country.philippines', phone: '+63' },
+ { code: 'PN', label: 'country.pitcairn_islands', phone: '+64' },
+ { code: 'PL', label: 'country.poland', phone: '+48' },
+ { code: 'PT', label: 'country.portugal', phone: '+351' },
+ { code: 'PR', label: 'country.puerto_rico', phone: '+1' },
+ { code: 'QA', label: 'country.qatar', phone: '+974' },
+ { code: 'RE', label: 'country.reunion', phone: '+262' },
+ { code: 'RO', label: 'country.romania', phone: '+40' },
+ { code: 'RU', label: 'country.russia', phone: '+7' },
+ { code: 'RW', label: 'country.rwanda', phone: '+250' },
+ { code: 'BL', label: 'country.saint_barthelemy', phone: '+590' },
+ { code: 'SH', label: 'country.saint_helena', phone: '+290' },
+ { code: 'KN', label: 'country.saint_kitts_and_nevis', phone: '+1869' },
+ { code: 'LC', label: 'country.saint_lucia', phone: '+1758' },
+ { code: 'MF', label: 'country.saint_martin', phone: '+590' },
+ { code: 'PM', label: 'country.saint_pierre_and_miquelon', phone: '+508' },
+ {
+ code: 'VC',
+ label: 'country.saint_vincent_and_the_grenadines',
+ phone: '+1784',
+ },
+ { code: 'WS', label: 'country.samoa', phone: '+685' },
+ { code: 'SM', label: 'country.san_marino', phone: '+378' },
+ { code: 'ST', label: 'country.sao_tome_and_principe', phone: '+239' },
+ { code: 'SA', label: 'country.saudi_arabia', phone: '+966' },
+ { code: 'SN', label: 'country.senegal', phone: '+221' },
+ { code: 'RS', label: 'country.serbia', phone: '+381' },
+ { code: 'SC', label: 'country.seychelles', phone: '+248' },
+ { code: 'SL', label: 'country.sierra_leone', phone: '+232' },
+ { code: 'SG', label: 'country.singapore', phone: '+65' },
+ { code: 'SX', label: 'country.sint_maarten', phone: '+1721' },
+ { code: 'SK', label: 'country.slovakia', phone: '+421' },
+ { code: 'SI', label: 'country.slovenia', phone: '+386' },
+ { code: 'SB', label: 'country.solomon_islands', phone: '+677' },
+ { code: 'SO', label: 'country.somalia', phone: '+252' },
+ { code: 'ZA', label: 'country.south_africa', phone: '+27' },
+ {
+ code: 'GS',
+ label: 'country.south_georgia_and_south_sandwich_islands',
+ phone: '+500',
+ },
+ { code: 'KR', label: 'country.south_korea', phone: '+82' },
+ { code: 'SS', label: 'country.south_sudan', phone: '+211' },
+ { code: 'ES', label: 'country.spain', phone: '+34' },
+ { code: 'LK', label: 'country.sri_lanka', phone: '+94' },
+ { code: 'SD', label: 'country.sudan', phone: '+249' },
+ { code: 'SR', label: 'country.suriname', phone: '+597' },
+ { code: 'SJ', label: 'country.svalbard_and_jan_mayen', phone: '+47' },
+ { code: 'SE', label: 'country.sweden', phone: '+46' },
+ { code: 'CH', label: 'country.switzerland', phone: '+41' },
+ { code: 'SY', label: 'country.syria', phone: '+963' },
+ { code: 'TW', label: 'country.taiwan', phone: '+886' },
+ { code: 'TJ', label: 'country.tajikistan', phone: '+992' },
+ { code: 'TZ', label: 'country.tanzania', phone: '+255' },
+ { code: 'TH', label: 'country.thailand', phone: '+66' },
+ { code: 'TL', label: 'country.timor_leste', phone: '+670' },
+ { code: 'TG', label: 'country.togo', phone: '+228' },
+ { code: 'TK', label: 'country.tokelau', phone: '+690' },
+ { code: 'TO', label: 'country.tonga', phone: '+676' },
+ { code: 'TT', label: 'country.trinidad_and_tobago', phone: '+1868' },
+ { code: 'TN', label: 'country.tunisia', phone: '+216' },
+ { code: 'TR', label: 'country.turkey', phone: '+90' },
+ { code: 'TM', label: 'country.turkmenistan', phone: '+993' },
+ { code: 'TC', label: 'country.turks_and_caicos_islands', phone: '+1649' },
+ { code: 'TV', label: 'country.tuvalu', phone: '+688' },
+ { code: 'VI', label: 'country.us_virgin_islands', phone: '+1340' },
+ { code: 'UG', label: 'country.uganda', phone: '+256' },
+ { code: 'UA', label: 'country.ukraine', phone: '+380' },
+ { code: 'AE', label: 'country.united_arab_emirates', phone: '+971' },
+ { code: 'GB', label: 'country.united_kingdom', phone: '+44' },
+ { code: 'US', label: 'country.united_states', phone: '+1' },
+ { code: 'UY', label: 'country.uruguay', phone: '+598' },
+ { code: 'UZ', label: 'country.uzbekistan', phone: '+998' },
+ { code: 'VU', label: 'country.vanuatu', phone: '+678' },
+ { code: 'VA', label: 'country.vatican_city', phone: '+39' },
+ { code: 'VE', label: 'country.venezuela', phone: '+58' },
+ { code: 'VN', label: 'country.vietnam', phone: '+84' },
+ { code: 'WF', label: 'country.wallis_and_futuna', phone: '+681' },
+ { code: 'EH', label: 'country.western_sahara', phone: '+212' },
+ { code: 'YE', label: 'country.yemen', phone: '+967' },
+ { code: 'ZM', label: 'country.zambia', phone: '+260' },
+ { code: 'ZW', label: 'country.zimbabwe', phone: '+263' },
+];
diff --git a/src/features/profile/routes/SettingPage.tsx b/src/features/profile/routes/SettingPage.tsx
index 3c8069f..631c267 100644
--- a/src/features/profile/routes/SettingPage.tsx
+++ b/src/features/profile/routes/SettingPage.tsx
@@ -1,312 +1,5 @@
-import { useState } from 'react';
-import {
- createBrowserRouter,
- RouterProvider,
- Navigate,
- Outlet,
- useLocation,
-} from 'react-router-dom';
-import { SideNav, type NavItemConfig } from '@rkheftan/harmony-ui';
-import {
- Devices,
- LocationTick,
- Mobile,
- PasswordCheck,
- Personalcard,
- ProfileCircle,
- Setting as SettingIcon,
- Shield,
- Sms,
- Menu,
-} from 'iconsax-react';
-import {
- Box,
- Typography,
- useTheme,
- useMediaQuery,
- Drawer,
- AppBar,
- Toolbar,
- IconButton,
-} from '@mui/material';
-import { useTranslation } from 'react-i18next';
-
-import { ActiveDevices } from '../components/activeDevices/ActiveDevices';
-import { Setting } from '../components/setting/Setting';
-import { RecentLogins } from '../components/security/RecentLogins';
-import { PasswordSecurity } from '../components/security/PasswordSecurity';
-import { PersonalInformation } from '../components/userInformation/PersonalInformation';
-import { PhoneNumber } from '../components/userInformation/PhoneNumber';
-import { SocialMedia } from '../components/userInformation/SocialMedia';
-
-function Header() {
- const headers = [{ name: 'محمدحسین برزه گر', phone: '09123456789' }];
- return (
-
- {headers.map((h) => (
-
- {h.name}
-
- {h.phone}
-
-
- ))}
-
- );
-}
-
-export function Layout() {
- const theme = useTheme();
- const isMdUp = useMediaQuery(theme.breakpoints.up('md'));
- const location = useLocation();
- const { t } = useTranslation('sideMap');
- const [drawerOpen, setDrawerOpen] = useState(false);
- const navWidthMd = '274px';
-
- const navConfig: NavItemConfig[] = [
- {
- text: t('side.account'),
- getIcon: (sel) =>
- sel ? (
-
- ) : (
-
- ),
- path: '/profile',
- children: [
- {
- text: t('side.personalInfo'),
- getIcon: (sel) =>
- sel ? (
-
- ) : (
-
- ),
- path: '/profile#info',
- },
- {
- text: t('side.phoneNumber'),
- getIcon: (sel) =>
- sel ? (
-
- ) : (
-
- ),
- path: '/profile#contact-info',
- },
- {
- text: t('side.email'),
- getIcon: (sel) =>
- sel ? (
-
- ) : (
-
- ),
- path: '/profile#email',
- },
- ],
- },
- {
- text: t('side.security'),
- getIcon: (sel) =>
- sel ? (
-
- ) : (
-
- ),
- path: '/security',
- children: [
- {
- text: t('side.password'),
- getIcon: (sel) =>
- sel ? (
-
- ) : (
-
- ),
- path: '/security#password',
- },
- {
- text: t('side.verifiedAddress'),
- getIcon: (sel) =>
- sel ? (
-
- ) : (
-
- ),
- path: '/security#locations',
- },
- {
- text: t('side.recentLogins'),
- getIcon: (sel) =>
- sel ? (
-
- ) : (
-
- ),
- path: '/security#sessions',
- },
- ],
- },
- {
- text: t('side.activeDevices'),
- getIcon: (sel) =>
- sel ? (
-
- ) : (
-
- ),
- path: '/devices',
- },
- {
- text: t('side.settings'),
- getIcon: (sel) =>
- sel ? (
-
- ) : (
-
- ),
- path: '/setting',
- },
- ];
-
- return (
-
-
- {!isMdUp && (
-
-
- setDrawerOpen(true)}>
-
-
-
-
- )}
-
-
- {isMdUp && (
-
- }
- activePath={location.pathname + location.hash}
- sideNavVariant="full"
- positioning="absolute"
- />
-
- )}
-
-
-
-
-
-
- {!isMdUp && (
- setDrawerOpen(false)}
- >
-
- }
- activePath={location.pathname + location.hash}
- sideNavVariant="full"
- />
-
-
- )}
-
-
- );
-}
-
-const router = createBrowserRouter([
- {
- path: '/',
- element: ,
- children: [
- { path: '/', element: },
- {
- path: '/profile',
- element: (
-
-
-
-
-
-
-
- ),
- },
- {
- path: '/security',
- element: (
-
-
-
-
-
-
-
- ),
- },
- { path: '/devices', element: },
- { path: '/setting', element: },
- ],
- },
-]);
+import { RouterProvider } from 'react-router-dom';
+import { router } from '../components/layout/Routes';
export function Settings() {
return ;