From 57959f39ceea86a9f3b7fa888fc52bad1d17ea40 Mon Sep 17 00:00:00 2001 From: Koosha Lahouti Date: Sat, 9 Aug 2025 16:22:15 -0700 Subject: [PATCH] chore: optimization of code --- package-lock.json | 339 +++++++++++++---- package.json | 10 +- public/locales/en/country.json | 247 +++++++++++++ public/locales/en/profileSetting.json | 4 +- public/locales/fa/country.json | 182 +++++++++ public/locales/fa/profileSetting.json | 4 +- src/App.tsx | 47 +-- .../components/CountryCodeSelector.tsx | 254 +++++++++++++ .../activeDevices/ActiveDevices.tsx | 6 +- .../profile/components/layout/Header.tsx | 25 ++ .../profile/components/layout/Layout.tsx | 127 +++++++ .../profile/components/layout/Routes.tsx | 52 +++ .../profile/components/layout/navConfigs.tsx | 121 ++++++ .../components/security/PasswordDialog.tsx | 179 +++++++++ .../components/security/PasswordSecurity.tsx | 322 ++++------------ .../components/security/RecentLogins.tsx | 6 +- .../profile/components/security/Security.tsx | 12 - .../userInformation/PersonalInformation.tsx | 45 ++- .../userInformation/PhoneNumber.tsx | 274 +++----------- .../userInformation/SocialMedia.tsx | 344 +----------------- .../phoneNumber/PhoneActionButtons.tsx | 49 +++ .../phoneNumber/PhoneDisplay.tsx | 31 ++ .../phoneNumber/PhoneEditForm.tsx | 208 +++++++++++ .../socialMedia/SocialMediaDialog.tsx | 187 ++++++++++ .../socialMedia/SocialMediaList.tsx | 63 ++++ .../socialMedia/SocialMediaMenu.tsx | 117 ++++++ src/features/profile/data/countries.ts | 271 ++++++++++++++ src/features/profile/routes/SettingPage.tsx | 311 +--------------- 28 files changed, 2586 insertions(+), 1251 deletions(-) create mode 100644 public/locales/en/country.json create mode 100644 public/locales/fa/country.json create mode 100644 src/features/profile/components/CountryCodeSelector.tsx create mode 100644 src/features/profile/components/layout/Header.tsx create mode 100644 src/features/profile/components/layout/Layout.tsx create mode 100644 src/features/profile/components/layout/Routes.tsx create mode 100644 src/features/profile/components/layout/navConfigs.tsx create mode 100644 src/features/profile/components/security/PasswordDialog.tsx delete mode 100644 src/features/profile/components/security/Security.tsx create mode 100644 src/features/profile/components/userInformation/phoneNumber/PhoneActionButtons.tsx create mode 100644 src/features/profile/components/userInformation/phoneNumber/PhoneDisplay.tsx create mode 100644 src/features/profile/components/userInformation/phoneNumber/PhoneEditForm.tsx create mode 100644 src/features/profile/components/userInformation/socialMedia/SocialMediaDialog.tsx create mode 100644 src/features/profile/components/userInformation/socialMedia/SocialMediaList.tsx create mode 100644 src/features/profile/components/userInformation/socialMedia/SocialMediaMenu.tsx create mode 100644 src/features/profile/data/countries.ts 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} + + + + + + + setSearchTerm(e.target.value)} + /> + + + {/* Can improve preformance with using virtual scrolling */} + + {filteredCountries.length === 0 ? ( + + + {t('messages.noResualtFound')} + + + ) : ( + filteredCountries.map((country) => ( + handleSelect(country)} + > + + + + + + {country.phone} + + + )) + )} + + + {/* virtual scrolling */} + {/* ( + + {t('messages.noResultFound')} + + ), + }} + initialTopMostItemIndex={countries.indexOf(selectedCountry)} + itemContent={(_, country) => ( + handleSelect(country)} + > + + + + + + {country.phone} + + + )} + /> */} + + + + ); +} 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 ( + + + + + + + + {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 } }} + /> + + + + + + + ); +} 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.lastChange')} + + + ) : ( + + {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={ - - - - - - - { - handleCloseMenu(); - handleOpenDialog(); - }} - > - - - - {t('settingForm.email')} - - - - - - {t('settingForm.google')} - - - - - - {t('settingForm.apple')} - - - + 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 ( + + + + + + {t('settingForm.addEmailButton')} + + + + + {t('settingForm.newEmail')} + + {t('settingForm.dialogHeader')} + + + + + + + + + + + {t('settingForm.or')} + + + + + + + + + + + ); +} 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 ( + + + + + + + { + handleCloseMenu(); + onOpenDialog(); + }} + > + + + + {t('settingForm.email')} + + + + + + {t('settingForm.google')} + + + + + + {t('settingForm.apple')} + + + + ); +} 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 ;