Merge pull request #27 from rkheftan/fix/completion-page
Fix/completion page
This commit is contained in:
@@ -1,255 +1,11 @@
|
|||||||
{
|
{
|
||||||
"helloWorld": "hello world",
|
"labels": {
|
||||||
"country": {
|
"search": "search"
|
||||||
"afghanistan": "Afghanistan",
|
|
||||||
"aland_islands": "Aland islands",
|
|
||||||
"albania": "Albania",
|
|
||||||
"algeria": "Algeria",
|
|
||||||
"american_samoa": "American samoa",
|
|
||||||
"andorra": "Andorra",
|
|
||||||
"angola": "Angola",
|
|
||||||
"anguilla": "Anguilla",
|
|
||||||
"antarctica": "Antarctica",
|
|
||||||
"antigua_and_barbuda": "Antigua and barbuda",
|
|
||||||
"argentina": "Argentina",
|
|
||||||
"armenia": "Armenia",
|
|
||||||
"aruba": "Aruba",
|
|
||||||
"australia": "Australia",
|
|
||||||
"austria": "Austria",
|
|
||||||
"azerbaijan": "Azerbaijan",
|
|
||||||
"bahamas": "Bahamas",
|
|
||||||
"bahrain": "Bahrain",
|
|
||||||
"bangladesh": "Bangladesh",
|
|
||||||
"barbados": "Barbados",
|
|
||||||
"belarus": "Belarus",
|
|
||||||
"belgium": "Belgium",
|
|
||||||
"belize": "Belize",
|
|
||||||
"benin": "Benin",
|
|
||||||
"bermuda": "Bermuda",
|
|
||||||
"bhutan": "Bhutan",
|
|
||||||
"bolivia": "Bolivia",
|
|
||||||
"bosnia_and_herzegovina": "Bosnia and herzegovina",
|
|
||||||
"botswana": "Botswana",
|
|
||||||
"brazil": "Brazil",
|
|
||||||
"british_indian_ocean_territory": "British indian ocean territory",
|
|
||||||
"british_virgin_islands": "British virgin islands",
|
|
||||||
"brunei": "Brunei",
|
|
||||||
"bulgaria": "Bulgaria",
|
|
||||||
"burkina_faso": "Burkina faso",
|
|
||||||
"burundi": "Burundi",
|
|
||||||
"cambodia": "Cambodia",
|
|
||||||
"cameroon": "Cameroon",
|
|
||||||
"canada": "Canada",
|
|
||||||
"cape_verde": "Cape verde",
|
|
||||||
"cayman_islands": "Cayman islands",
|
|
||||||
"central_african_republic": "Central african republic",
|
|
||||||
"chad": "Chad",
|
|
||||||
"chile": "Chile",
|
|
||||||
"china": "China",
|
|
||||||
"christmas_island": "Christmas island",
|
|
||||||
"cocos_keeling_islands": "Cocos keeling islands",
|
|
||||||
"colombia": "Colombia",
|
|
||||||
"comoros": "Comoros",
|
|
||||||
"congo_brazzaville": "Congo brazzaville",
|
|
||||||
"congo_kinshasa": "Congo kinshasa",
|
|
||||||
"cook_islands": "Cook islands",
|
|
||||||
"costa_rica": "Costa rica",
|
|
||||||
"cote_divoire": "Cote divoire",
|
|
||||||
"croatia": "Croatia",
|
|
||||||
"cuba": "Cuba",
|
|
||||||
"curacao": "Curacao",
|
|
||||||
"cyprus": "Cyprus",
|
|
||||||
"czech_republic": "Czech republic",
|
|
||||||
"denmark": "Denmark",
|
|
||||||
"djibouti": "Djibouti",
|
|
||||||
"dominica": "Dominica",
|
|
||||||
"dominican_republic": "Dominican republic",
|
|
||||||
"ecuador": "Ecuador",
|
|
||||||
"egypt": "Egypt",
|
|
||||||
"el_salvador": "El salvador",
|
|
||||||
"equatorial_guinea": "Equatorial guinea",
|
|
||||||
"eritrea": "Eritrea",
|
|
||||||
"estonia": "Estonia",
|
|
||||||
"eswatini": "Eswatini",
|
|
||||||
"ethiopia": "Ethiopia",
|
|
||||||
"falkland_islands": "Falkland islands",
|
|
||||||
"faroe_islands": "Faroe islands",
|
|
||||||
"fiji": "Fiji",
|
|
||||||
"finland": "Finland",
|
|
||||||
"france": "France",
|
|
||||||
"french_guiana": "French guiana",
|
|
||||||
"french_polynesia": "French polynesia",
|
|
||||||
"gabon": "Gabon",
|
|
||||||
"gambia": "Gambia",
|
|
||||||
"georgia": "Georgia",
|
|
||||||
"germany": "Germany",
|
|
||||||
"ghana": "Ghana",
|
|
||||||
"gibraltar": "Gibraltar",
|
|
||||||
"greece": "Greece",
|
|
||||||
"greenland": "Greenland",
|
|
||||||
"grenada": "Grenada",
|
|
||||||
"guadeloupe": "Guadeloupe",
|
|
||||||
"guam": "Guam",
|
|
||||||
"guatemala": "Guatemala",
|
|
||||||
"guernsey": "Guernsey",
|
|
||||||
"guinea": "Guinea",
|
|
||||||
"guinea_bissau": "Guinea bissau",
|
|
||||||
"guyana": "Guyana",
|
|
||||||
"haiti": "Haiti",
|
|
||||||
"honduras": "Honduras",
|
|
||||||
"hong_kong": "Hong kong",
|
|
||||||
"hungary": "Hungary",
|
|
||||||
"iceland": "Iceland",
|
|
||||||
"india": "India",
|
|
||||||
"indonesia": "Indonesia",
|
|
||||||
"iran": "Iran",
|
|
||||||
"iraq": "Iraq",
|
|
||||||
"ireland": "Ireland",
|
|
||||||
"isle_of_man": "Isle of man",
|
|
||||||
"israel": "Israel",
|
|
||||||
"italy": "Italy",
|
|
||||||
"jamaica": "Jamaica",
|
|
||||||
"japan": "Japan",
|
|
||||||
"jersey": "Jersey",
|
|
||||||
"jordan": "Jordan",
|
|
||||||
"kazakhstan": "Kazakhstan",
|
|
||||||
"kenya": "Kenya",
|
|
||||||
"kiribati": "Kiribati",
|
|
||||||
"kosovo": "Kosovo",
|
|
||||||
"kuwait": "Kuwait",
|
|
||||||
"kyrgyzstan": "Kyrgyzstan",
|
|
||||||
"laos": "Laos",
|
|
||||||
"latvia": "Latvia",
|
|
||||||
"lebanon": "Lebanon",
|
|
||||||
"lesotho": "Lesotho",
|
|
||||||
"liberia": "Liberia",
|
|
||||||
"libya": "Libya",
|
|
||||||
"liechtenstein": "Liechtenstein",
|
|
||||||
"lithuania": "Lithuania",
|
|
||||||
"luxembourg": "Luxembourg",
|
|
||||||
"macau": "Macau",
|
|
||||||
"madagascar": "Madagascar",
|
|
||||||
"malawi": "Malawi",
|
|
||||||
"malaysia": "Malaysia",
|
|
||||||
"maldives": "Maldives",
|
|
||||||
"mali": "Mali",
|
|
||||||
"malta": "Malta",
|
|
||||||
"marshall_islands": "Marshall islands",
|
|
||||||
"martinique": "Martinique",
|
|
||||||
"mauritania": "Mauritania",
|
|
||||||
"mauritius": "Mauritius",
|
|
||||||
"mayotte": "Mayotte",
|
|
||||||
"mexico": "Mexico",
|
|
||||||
"micronesia": "Micronesia",
|
|
||||||
"moldova": "Moldova",
|
|
||||||
"monaco": "Monaco",
|
|
||||||
"mongolia": "Mongolia",
|
|
||||||
"montenegro": "Montenegro",
|
|
||||||
"montserrat": "Montserrat",
|
|
||||||
"morocco": "Morocco",
|
|
||||||
"mozambique": "Mozambique",
|
|
||||||
"myanmar": "Myanmar",
|
|
||||||
"namibia": "Namibia",
|
|
||||||
"nauru": "Nauru",
|
|
||||||
"nepal": "Nepal",
|
|
||||||
"netherlands": "Netherlands",
|
|
||||||
"new_caledonia": "New caledonia",
|
|
||||||
"new_zealand": "New zealand",
|
|
||||||
"nicaragua": "Nicaragua",
|
|
||||||
"niger": "Niger",
|
|
||||||
"nigeria": "Nigeria",
|
|
||||||
"niue": "Niue",
|
|
||||||
"norfolk_island": "Norfolk island",
|
|
||||||
"north_korea": "North korea",
|
|
||||||
"north_macedonia": "North macedonia",
|
|
||||||
"northern_mariana_islands": "Northern mariana islands",
|
|
||||||
"norway": "Norway",
|
|
||||||
"oman": "Oman",
|
|
||||||
"pakistan": "Pakistan",
|
|
||||||
"palau": "Palau",
|
|
||||||
"palestine": "Palestine",
|
|
||||||
"panama": "Panama",
|
|
||||||
"papua_new_guinea": "Papua new guinea",
|
|
||||||
"paraguay": "Paraguay",
|
|
||||||
"peru": "Peru",
|
|
||||||
"philippines": "Philippines",
|
|
||||||
"pitcairn_islands": "Pitcairn islands",
|
|
||||||
"poland": "Poland",
|
|
||||||
"portugal": "Portugal",
|
|
||||||
"puerto_rico": "Puerto rico",
|
|
||||||
"qatar": "Qatar",
|
|
||||||
"reunion": "Reunion",
|
|
||||||
"romania": "Romania",
|
|
||||||
"russia": "Russia",
|
|
||||||
"rwanda": "Rwanda",
|
|
||||||
"saint_barthelemy": "Saint barthelemy",
|
|
||||||
"saint_helena": "Saint helena",
|
|
||||||
"saint_kitts_and_nevis": "Saint kitts and nevis",
|
|
||||||
"saint_lucia": "Saint lucia",
|
|
||||||
"saint_martin": "Saint martin",
|
|
||||||
"saint_pierre_and_miquelon": "Saint pierre and miquelon",
|
|
||||||
"saint_vincent_and_the_grenadines": "Saint vincent and the grenadines",
|
|
||||||
"samoa": "Samoa",
|
|
||||||
"san_marino": "San marino",
|
|
||||||
"sao_tome_and_principe": "Sao tome and principe",
|
|
||||||
"saudi_arabia": "Saudi arabia",
|
|
||||||
"senegal": "Senegal",
|
|
||||||
"serbia": "Serbia",
|
|
||||||
"seychelles": "Seychelles",
|
|
||||||
"sierra_leone": "Sierra leone",
|
|
||||||
"singapore": "Singapore",
|
|
||||||
"sint_maarten": "Sint maarten",
|
|
||||||
"slovakia": "Slovakia",
|
|
||||||
"slovenia": "Slovenia",
|
|
||||||
"solomon_islands": "Solomon islands",
|
|
||||||
"somalia": "Somalia",
|
|
||||||
"south_africa": "South africa",
|
|
||||||
"south_georgia_and_south_sandwich_islands": "South georgia and south sandwich islands",
|
|
||||||
"south_korea": "South korea",
|
|
||||||
"south_sudan": "South sudan",
|
|
||||||
"spain": "Spain",
|
|
||||||
"sri_lanka": "Sri lanka",
|
|
||||||
"sudan": "Sudan",
|
|
||||||
"suriname": "Suriname",
|
|
||||||
"svalbard_and_jan_mayen": "Svalbard and jan mayen",
|
|
||||||
"sweden": "Sweden",
|
|
||||||
"switzerland": "Switzerland",
|
|
||||||
"syria": "Syria",
|
|
||||||
"taiwan": "Taiwan",
|
|
||||||
"tajikistan": "Tajikistan",
|
|
||||||
"tanzania": "Tanzania",
|
|
||||||
"thailand": "Thailand",
|
|
||||||
"timor_leste": "Timor leste",
|
|
||||||
"togo": "Togo",
|
|
||||||
"tokelau": "Tokelau",
|
|
||||||
"tonga": "Tonga",
|
|
||||||
"trinidad_and_tobago": "Trinidad and tobago",
|
|
||||||
"tunisia": "Tunisia",
|
|
||||||
"turkey": "Turkey",
|
|
||||||
"turkmenistan": "Turkmenistan",
|
|
||||||
"turks_and_caicos_islands": "Turks and caicos islands",
|
|
||||||
"tuvalu": "Tuvalu",
|
|
||||||
"us_virgin_islands": "Us virgin islands",
|
|
||||||
"uganda": "Uganda",
|
|
||||||
"ukraine": "Ukraine",
|
|
||||||
"united_arab_emirates": "United arab emirates",
|
|
||||||
"united_kingdom": "United kingdom",
|
|
||||||
"united_states": "United states",
|
|
||||||
"uruguay": "Uruguay",
|
|
||||||
"uzbekistan": "Uzbekistan",
|
|
||||||
"vanuatu": "Vanuatu",
|
|
||||||
"vatican_city": "Vatican city",
|
|
||||||
"venezuela": "Venezuela",
|
|
||||||
"vietnam": "Vietnam",
|
|
||||||
"wallis_and_futuna": "Wallis and futuna",
|
|
||||||
"western_sahara": "Western sahara",
|
|
||||||
"yemen": "Yemen",
|
|
||||||
"zambia": "Zambia",
|
|
||||||
"zimbabwe": "Zimbabwe"
|
|
||||||
},
|
},
|
||||||
"messages": {
|
"messages": {
|
||||||
"pageNotFound": "Page not found",
|
"pageNotFound": "Page not found",
|
||||||
"noResualtFound": "No result found.",
|
"noResualtFound": "No result found.",
|
||||||
"serverError": "Internal server error"
|
"serverError": "Internal server error",
|
||||||
|
"networkError": "Network error. Please check your internet connection and try again."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,20 +19,36 @@
|
|||||||
"woman": "female",
|
"woman": "female",
|
||||||
"hasNumber": "includes number",
|
"hasNumber": "includes number",
|
||||||
"hasMinLength": "at least 8 characters",
|
"hasMinLength": "at least 8 characters",
|
||||||
"hasUpperAndLower": "includes a lowercase and uppercase letter",
|
"hasUpperAndLower": "includes a lowercase and uppercase English letter",
|
||||||
"hasSpecialChar": "includes sign (!@#$%^&*)",
|
"hasSpecialChar": "includes sign (!@#$%^&*)",
|
||||||
"notCompatibility": "does not match",
|
"notCompatibility": "does not match",
|
||||||
"emailCorrectForm": "Enter the correct email form.",
|
"emailCorrectForm": "Enter the correct email form.",
|
||||||
"agreementPart1": "By continuing the registration process, you agree to the",
|
"agreementPart1": "By continuing the registration process, you agree to the ",
|
||||||
"agreementLinkText": " Harmony Terms and Conditions",
|
"agreementLinkText": "Harmony Terms and Conditions",
|
||||||
"agreementPart2": ".",
|
"agreementPart2": ".",
|
||||||
"sent": "sent",
|
"sent": "sent",
|
||||||
"country": "country",
|
"country": "country",
|
||||||
|
"noOption": "No option",
|
||||||
"dateOfBirth": "Date of birth(optional)",
|
"dateOfBirth": "Date of birth(optional)",
|
||||||
"submitSuccess": "Information successfully registered",
|
"submitSuccess": "Information successfully registered",
|
||||||
"submitError": "Error in registering information",
|
"submitError": "Error in registering information",
|
||||||
"submitting": "Submitting...",
|
"submitting": "Submitting...",
|
||||||
"success": "Success",
|
"success": "Success",
|
||||||
"agreement": "1. Confidentiality of Information: Harmony commits under no circumstances to disclose users’ identity information, such as phone numbers, email addresses, passwords, user IDs, or any related data, to third parties.\n\n2. User information is used solely for providing authentication services and remains confidential even after account deactivation or termination.\n\n3. Harmony is obliged to implement necessary security measures to prevent unauthorized access.\n\n4. Responsibility for Account Security: Users must protect their accounts and choose strong, non-guessable passwords. Periodic password changes and immediate action in case of suspected unauthorized access are required. Any misuse of the account due to user negligence is the responsibility of the user.\n\n5. Security Breaches and Cyber Attacks: Harmony is not responsible for security breaches caused by cyber attacks beyond the system's control. However, Harmony employs up-to-date security standards and encryption to prevent such incidents.\n\n6. User Negligence in Protecting Information: If account information is disclosed due to user negligence or error, Harmony bears no responsibility. Determining such cases, based on system security logs, is the responsibility of Harmony's technical manager.\n\n7. Accurate Logging of Activities: All events related to registering, editing, and deleting information in the system are accurately and immutably logged. Claims regarding deletion or modification of data without logs are invalid unless supported by documentation provided by the user.\n\n8. Service Updates: Harmony services may be updated or changed over time. Continued use of the system after changes implies acceptance of the new terms. If users disagree, they may request account deletion.\n\n9. User Support: Support is provided only via email and phone, free of charge. Harmony is not obligated to provide in-person support or training beyond basic services.\n\n10. Official Communication Channels: Harmony communicates with users only via the phone number and email registered in the user account. Official announcements are sent through these channels.\n\n11. Official Domains for Communication: All emails from Harmony are sent exclusively from the domain harmony.id. Users must verify this to prevent phishing or similar attacks.\n\n12. Compliance with Iranian Laws: Users must comply with all applicable laws of the Islamic Republic of Iran, including the “Electronic Commerce Law,” “Computer Crimes Law,” and related legislation. Responsibility for violations rests with the user.\n\n13. Temporary Data Retention After Account Termination: Upon account termination or deletion, user information is stored securely for 30 days and permanently deleted thereafter.\n\n14. Ownership of User Data: All data submitted by users belongs to them. Harmony has no ownership over this information. Users are responsible for the accuracy, quality, and legality of their data.\n\n15. Purposeful Use of Identity Information: Collected identity information during registration is used only for authentication and basic services. It will not be shared with any third party without explicit user consent, except under a court order or legal authority.\n\n16. Permanent Data Confidentiality: Harmony commits to maintaining confidentiality of collected information even after the end of the user relationship or account closure.\n\n17. Limitation of Liability: Harmony is not liable for direct or indirect damages resulting from use or inability to use the authentication services.\n\n18. Disruptions in Communication Infrastructure: Harmony is not responsible for disruptions caused by the internet, infrastructure services, or other issues beyond its control.\n\n19. Force Majeure and Unforeseen Events: Harmony bears no responsibility for natural disasters, strikes, power outages, cyber attacks, or other events beyond its control that prevent service delivery.\n\n20. Services Dependent on Third Parties: If parts of the authentication services are provided by third parties, the usage terms of those services are the responsibility of those companies, and Harmony bears no liability.\n\n21. Guarantee of Data Access in Case of Service Termination: If Harmony ceases operations permanently, it commits to keeping servers active for two years and allowing users access to their data.\n\n22. Notification of Service Interruptions: If service interruption is necessary, Harmony must notify users at least 12 hours in advance via email or SMS."
|
"agreement": "1. Confidentiality of Information: Harmony commits under no circumstances to disclose users’ identity information, such as phone numbers, email addresses, passwords, user IDs, or any related data, to third parties.\n\n2. User information is used solely for providing authentication services and remains confidential even after account deactivation or termination.\n\n3. Harmony is obliged to implement necessary security measures to prevent unauthorized access.\n\n4. Responsibility for Account Security: Users must protect their accounts and choose strong, non-guessable passwords. Periodic password changes and immediate action in case of suspected unauthorized access are required. Any misuse of the account due to user negligence is the responsibility of the user.\n\n5. Security Breaches and Cyber Attacks: Harmony is not responsible for security breaches caused by cyber attacks beyond the system's control. However, Harmony employs up-to-date security standards and encryption to prevent such incidents.\n\n6. User Negligence in Protecting Information: If account information is disclosed due to user negligence or error, Harmony bears no responsibility. Determining such cases, based on system security logs, is the responsibility of Harmony's technical manager.\n\n7. Accurate Logging of Activities: All events related to registering, editing, and deleting information in the system are accurately and immutably logged. Claims regarding deletion or modification of data without logs are invalid unless supported by documentation provided by the user.\n\n8. Service Updates: Harmony services may be updated or changed over time. Continued use of the system after changes implies acceptance of the new terms. If users disagree, they may request account deletion.\n\n9. User Support: Support is provided only via email and phone, free of charge. Harmony is not obligated to provide in-person support or training beyond basic services.\n\n10. Official Communication Channels: Harmony communicates with users only via the phone number and email registered in the user account. Official announcements are sent through these channels.\n\n11. Official Domains for Communication: All emails from Harmony are sent exclusively from the domain harmony.id. Users must verify this to prevent phishing or similar attacks.\n\n12. Compliance with Iranian Laws: Users must comply with all applicable laws of the Islamic Republic of Iran, including the “Electronic Commerce Law,” “Computer Crimes Law,” and related legislation. Responsibility for violations rests with the user.\n\n13. Temporary Data Retention After Account Termination: Upon account termination or deletion, user information is stored securely for 30 days and permanently deleted thereafter.\n\n14. Ownership of User Data: All data submitted by users belongs to them. Harmony has no ownership over this information. Users are responsible for the accuracy, quality, and legality of their data.\n\n15. Purposeful Use of Identity Information: Collected identity information during registration is used only for authentication and basic services. It will not be shared with any third party without explicit user consent, except under a court order or legal authority.\n\n16. Permanent Data Confidentiality: Harmony commits to maintaining confidentiality of collected information even after the end of the user relationship or account closure.\n\n17. Limitation of Liability: Harmony is not liable for direct or indirect damages resulting from use or inability to use the authentication services.\n\n18. Disruptions in Communication Infrastructure: Harmony is not responsible for disruptions caused by the internet, infrastructure services, or other issues beyond its control.\n\n19. Force Majeure and Unforeseen Events: Harmony bears no responsibility for natural disasters, strikes, power outages, cyber attacks, or other events beyond its control that prevent service delivery.\n\n20. Services Dependent on Third Parties: If parts of the authentication services are provided by third parties, the usage terms of those services are the responsibility of those companies, and Harmony bears no liability.\n\n21. Guarantee of Data Access in Case of Service Termination: If Harmony ceases operations permanently, it commits to keeping servers active for two years and allowing users access to their data.\n\n22. Notification of Service Interruptions: If service interruption is necessary, Harmony must notify users at least 12 hours in advance via email or SMS."
|
||||||
|
},
|
||||||
|
"validation": {
|
||||||
|
"firstNameRequired": "First name is required.",
|
||||||
|
"lastNameRequired": "Last name is required.",
|
||||||
|
"countryRequired": "Country is required.",
|
||||||
|
"emailInvalid": "A valid email is required.",
|
||||||
|
"nationalIdInvalid": "National ID must be 10 digits.",
|
||||||
|
"passwordRequired": "Password is required.",
|
||||||
|
"passwordInvalid": "Password does not meet all the requirements.",
|
||||||
|
"confirmPasswordRequired": "Please confirm your password.",
|
||||||
|
"passwordsDoNotMatch": "Passwords do not match.",
|
||||||
|
"verificationCodeRequired": "Verification code is required.",
|
||||||
|
"mustVerifyCode": "Please click the verify button to confirm the code.",
|
||||||
|
"genderRequired": "Selecting a gender is required.",
|
||||||
|
"verificationCodeInvalid": "Verification code must be 4 digits."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,247 +1,249 @@
|
|||||||
{
|
{
|
||||||
"country.afghanistan": "Afghanistan",
|
"country": {
|
||||||
"country.aland_islands": "Aland islands",
|
"afghanistan": "Afghanistan",
|
||||||
"country.albania": "Albania",
|
"aland_islands": "Aland islands",
|
||||||
"country.algeria": "Algeria",
|
"albania": "Albania",
|
||||||
"country.american_samoa": "American samoa",
|
"algeria": "Algeria",
|
||||||
"country.andorra": "Andorra",
|
"american_samoa": "American samoa",
|
||||||
"country.angola": "Angola",
|
"andorra": "Andorra",
|
||||||
"country.anguilla": "Anguilla",
|
"angola": "Angola",
|
||||||
"country.antarctica": "Antarctica",
|
"anguilla": "Anguilla",
|
||||||
"country.antigua_and_barbuda": "Antigua and barbuda",
|
"antarctica": "Antarctica",
|
||||||
"country.argentina": "Argentina",
|
"antigua_and_barbuda": "Antigua and barbuda",
|
||||||
"country.armenia": "Armenia",
|
"argentina": "Argentina",
|
||||||
"country.aruba": "Aruba",
|
"armenia": "Armenia",
|
||||||
"country.australia": "Australia",
|
"aruba": "Aruba",
|
||||||
"country.austria": "Austria",
|
"australia": "Australia",
|
||||||
"country.azerbaijan": "Azerbaijan",
|
"austria": "Austria",
|
||||||
"country.bahamas": "Bahamas",
|
"azerbaijan": "Azerbaijan",
|
||||||
"country.bahrain": "Bahrain",
|
"bahamas": "Bahamas",
|
||||||
"country.bangladesh": "Bangladesh",
|
"bahrain": "Bahrain",
|
||||||
"country.barbados": "Barbados",
|
"bangladesh": "Bangladesh",
|
||||||
"country.belarus": "Belarus",
|
"barbados": "Barbados",
|
||||||
"country.belgium": "Belgium",
|
"belarus": "Belarus",
|
||||||
"country.belize": "Belize",
|
"belgium": "Belgium",
|
||||||
"country.benin": "Benin",
|
"belize": "Belize",
|
||||||
"country.bermuda": "Bermuda",
|
"benin": "Benin",
|
||||||
"country.bhutan": "Bhutan",
|
"bermuda": "Bermuda",
|
||||||
"country.bolivia": "Bolivia",
|
"bhutan": "Bhutan",
|
||||||
"country.bosnia_and_herzegovina": "Bosnia and herzegovina",
|
"bolivia": "Bolivia",
|
||||||
"country.botswana": "Botswana",
|
"bosnia_and_herzegovina": "Bosnia and herzegovina",
|
||||||
"country.brazil": "Brazil",
|
"botswana": "Botswana",
|
||||||
"country.british_indian_ocean_territory": "British indian ocean territory",
|
"brazil": "Brazil",
|
||||||
"country.british_virgin_islands": "British virgin islands",
|
"british_indian_ocean_territory": "British indian ocean territory",
|
||||||
"country.brunei": "Brunei",
|
"british_virgin_islands": "British virgin islands",
|
||||||
"country.bulgaria": "Bulgaria",
|
"brunei": "Brunei",
|
||||||
"country.burkina_faso": "Burkina faso",
|
"bulgaria": "Bulgaria",
|
||||||
"country.burundi": "Burundi",
|
"burkina_faso": "Burkina faso",
|
||||||
"country.cambodia": "Cambodia",
|
"burundi": "Burundi",
|
||||||
"country.cameroon": "Cameroon",
|
"cambodia": "Cambodia",
|
||||||
"country.canada": "Canada",
|
"cameroon": "Cameroon",
|
||||||
"country.cape_verde": "Cape verde",
|
"canada": "Canada",
|
||||||
"country.cayman_islands": "Cayman islands",
|
"cape_verde": "Cape verde",
|
||||||
"country.central_african_republic": "Central african republic",
|
"cayman_islands": "Cayman islands",
|
||||||
"country.chad": "Chad",
|
"central_african_republic": "Central african republic",
|
||||||
"country.chile": "Chile",
|
"chad": "Chad",
|
||||||
"country.china": "China",
|
"chile": "Chile",
|
||||||
"country.christmas_island": "Christmas island",
|
"china": "China",
|
||||||
"country.cocos_keeling_islands": "Cocos keeling islands",
|
"christmas_island": "Christmas island",
|
||||||
"country.colombia": "Colombia",
|
"cocos_keeling_islands": "Cocos keeling islands",
|
||||||
"country.comoros": "Comoros",
|
"colombia": "Colombia",
|
||||||
"country.congo_brazzaville": "Congo brazzaville",
|
"comoros": "Comoros",
|
||||||
"country.congo_kinshasa": "Congo kinshasa",
|
"congo_brazzaville": "Congo brazzaville",
|
||||||
"country.cook_islands": "Cook islands",
|
"congo_kinshasa": "Congo kinshasa",
|
||||||
"country.costa_rica": "Costa rica",
|
"cook_islands": "Cook islands",
|
||||||
"country.cote_divoire": "Cote divoire",
|
"costa_rica": "Costa rica",
|
||||||
"country.croatia": "Croatia",
|
"cote_divoire": "Cote divoire",
|
||||||
"country.cuba": "Cuba",
|
"croatia": "Croatia",
|
||||||
"country.curacao": "Curacao",
|
"cuba": "Cuba",
|
||||||
"country.cyprus": "Cyprus",
|
"curacao": "Curacao",
|
||||||
"country.czech_republic": "Czech republic",
|
"cyprus": "Cyprus",
|
||||||
"country.denmark": "Denmark",
|
"czech_republic": "Czech republic",
|
||||||
"country.djibouti": "Djibouti",
|
"denmark": "Denmark",
|
||||||
"country.dominica": "Dominica",
|
"djibouti": "Djibouti",
|
||||||
"country.dominican_republic": "Dominican republic",
|
"dominica": "Dominica",
|
||||||
"country.ecuador": "Ecuador",
|
"dominican_republic": "Dominican republic",
|
||||||
"country.egypt": "Egypt",
|
"ecuador": "Ecuador",
|
||||||
"country.el_salvador": "El salvador",
|
"egypt": "Egypt",
|
||||||
"country.equatorial_guinea": "Equatorial guinea",
|
"el_salvador": "El salvador",
|
||||||
"country.eritrea": "Eritrea",
|
"equatorial_guinea": "Equatorial guinea",
|
||||||
"country.estonia": "Estonia",
|
"eritrea": "Eritrea",
|
||||||
"country.eswatini": "Eswatini",
|
"estonia": "Estonia",
|
||||||
"country.ethiopia": "Ethiopia",
|
"eswatini": "Eswatini",
|
||||||
"country.falkland_islands": "Falkland islands",
|
"ethiopia": "Ethiopia",
|
||||||
"country.faroe_islands": "Faroe islands",
|
"falkland_islands": "Falkland islands",
|
||||||
"country.fiji": "Fiji",
|
"faroe_islands": "Faroe islands",
|
||||||
"country.finland": "Finland",
|
"fiji": "Fiji",
|
||||||
"country.france": "France",
|
"finland": "Finland",
|
||||||
"country.french_guiana": "French guiana",
|
"france": "France",
|
||||||
"country.french_polynesia": "French polynesia",
|
"french_guiana": "French guiana",
|
||||||
"country.gabon": "Gabon",
|
"french_polynesia": "French polynesia",
|
||||||
"country.gambia": "Gambia",
|
"gabon": "Gabon",
|
||||||
"country.georgia": "Georgia",
|
"gambia": "Gambia",
|
||||||
"country.germany": "Germany",
|
"georgia": "Georgia",
|
||||||
"country.ghana": "Ghana",
|
"germany": "Germany",
|
||||||
"country.gibraltar": "Gibraltar",
|
"ghana": "Ghana",
|
||||||
"country.greece": "Greece",
|
"gibraltar": "Gibraltar",
|
||||||
"country.greenland": "Greenland",
|
"greece": "Greece",
|
||||||
"country.grenada": "Grenada",
|
"greenland": "Greenland",
|
||||||
"country.guadeloupe": "Guadeloupe",
|
"grenada": "Grenada",
|
||||||
"country.guam": "Guam",
|
"guadeloupe": "Guadeloupe",
|
||||||
"country.guatemala": "Guatemala",
|
"guam": "Guam",
|
||||||
"country.guernsey": "Guernsey",
|
"guatemala": "Guatemala",
|
||||||
"country.guinea": "Guinea",
|
"guernsey": "Guernsey",
|
||||||
"country.guinea_bissau": "Guinea bissau",
|
"guinea": "Guinea",
|
||||||
"country.guyana": "Guyana",
|
"guinea_bissau": "Guinea bissau",
|
||||||
"country.haiti": "Haiti",
|
"guyana": "Guyana",
|
||||||
"country.honduras": "Honduras",
|
"haiti": "Haiti",
|
||||||
"country.hong_kong": "Hong kong",
|
"honduras": "Honduras",
|
||||||
"country.hungary": "Hungary",
|
"hong_kong": "Hong kong",
|
||||||
"country.iceland": "Iceland",
|
"hungary": "Hungary",
|
||||||
"country.india": "India",
|
"iceland": "Iceland",
|
||||||
"country.indonesia": "Indonesia",
|
"india": "India",
|
||||||
"country.iran": "Iran",
|
"indonesia": "Indonesia",
|
||||||
"country.iraq": "Iraq",
|
"iran": "Iran",
|
||||||
"country.ireland": "Ireland",
|
"iraq": "Iraq",
|
||||||
"country.isle_of_man": "Isle of man",
|
"ireland": "Ireland",
|
||||||
"country.israel": "Israel",
|
"isle_of_man": "Isle of man",
|
||||||
"country.italy": "Italy",
|
"israel": "Israel",
|
||||||
"country.jamaica": "Jamaica",
|
"italy": "Italy",
|
||||||
"country.japan": "Japan",
|
"jamaica": "Jamaica",
|
||||||
"country.jersey": "Jersey",
|
"japan": "Japan",
|
||||||
"country.jordan": "Jordan",
|
"jersey": "Jersey",
|
||||||
"country.kazakhstan": "Kazakhstan",
|
"jordan": "Jordan",
|
||||||
"country.kenya": "Kenya",
|
"kazakhstan": "Kazakhstan",
|
||||||
"country.kiribati": "Kiribati",
|
"kenya": "Kenya",
|
||||||
"country.kosovo": "Kosovo",
|
"kiribati": "Kiribati",
|
||||||
"country.kuwait": "Kuwait",
|
"kosovo": "Kosovo",
|
||||||
"country.kyrgyzstan": "Kyrgyzstan",
|
"kuwait": "Kuwait",
|
||||||
"country.laos": "Laos",
|
"kyrgyzstan": "Kyrgyzstan",
|
||||||
"country.latvia": "Latvia",
|
"laos": "Laos",
|
||||||
"country.lebanon": "Lebanon",
|
"latvia": "Latvia",
|
||||||
"country.lesotho": "Lesotho",
|
"lebanon": "Lebanon",
|
||||||
"country.liberia": "Liberia",
|
"lesotho": "Lesotho",
|
||||||
"country.libya": "Libya",
|
"liberia": "Liberia",
|
||||||
"country.liechtenstein": "Liechtenstein",
|
"libya": "Libya",
|
||||||
"country.lithuania": "Lithuania",
|
"liechtenstein": "Liechtenstein",
|
||||||
"country.luxembourg": "Luxembourg",
|
"lithuania": "Lithuania",
|
||||||
"country.macau": "Macau",
|
"luxembourg": "Luxembourg",
|
||||||
"country.madagascar": "Madagascar",
|
"macau": "Macau",
|
||||||
"country.malawi": "Malawi",
|
"madagascar": "Madagascar",
|
||||||
"country.malaysia": "Malaysia",
|
"malawi": "Malawi",
|
||||||
"country.maldives": "Maldives",
|
"malaysia": "Malaysia",
|
||||||
"country.mali": "Mali",
|
"maldives": "Maldives",
|
||||||
"country.malta": "Malta",
|
"mali": "Mali",
|
||||||
"country.marshall_islands": "Marshall islands",
|
"malta": "Malta",
|
||||||
"country.martinique": "Martinique",
|
"marshall_islands": "Marshall islands",
|
||||||
"country.mauritania": "Mauritania",
|
"martinique": "Martinique",
|
||||||
"country.mauritius": "Mauritius",
|
"mauritania": "Mauritania",
|
||||||
"country.mayotte": "Mayotte",
|
"mauritius": "Mauritius",
|
||||||
"country.mexico": "Mexico",
|
"mayotte": "Mayotte",
|
||||||
"country.micronesia": "Micronesia",
|
"mexico": "Mexico",
|
||||||
"country.moldova": "Moldova",
|
"micronesia": "Micronesia",
|
||||||
"country.monaco": "Monaco",
|
"moldova": "Moldova",
|
||||||
"country.mongolia": "Mongolia",
|
"monaco": "Monaco",
|
||||||
"country.montenegro": "Montenegro",
|
"mongolia": "Mongolia",
|
||||||
"country.montserrat": "Montserrat",
|
"montenegro": "Montenegro",
|
||||||
"country.morocco": "Morocco",
|
"montserrat": "Montserrat",
|
||||||
"country.mozambique": "Mozambique",
|
"morocco": "Morocco",
|
||||||
"country.myanmar": "Myanmar",
|
"mozambique": "Mozambique",
|
||||||
"country.namibia": "Namibia",
|
"myanmar": "Myanmar",
|
||||||
"country.nauru": "Nauru",
|
"namibia": "Namibia",
|
||||||
"country.nepal": "Nepal",
|
"nauru": "Nauru",
|
||||||
"country.netherlands": "Netherlands",
|
"nepal": "Nepal",
|
||||||
"country.new_caledonia": "New caledonia",
|
"netherlands": "Netherlands",
|
||||||
"country.new_zealand": "New zealand",
|
"new_caledonia": "New caledonia",
|
||||||
"country.nicaragua": "Nicaragua",
|
"new_zealand": "New zealand",
|
||||||
"country.niger": "Niger",
|
"nicaragua": "Nicaragua",
|
||||||
"country.nigeria": "Nigeria",
|
"niger": "Niger",
|
||||||
"country.niue": "Niue",
|
"nigeria": "Nigeria",
|
||||||
"country.norfolk_island": "Norfolk island",
|
"niue": "Niue",
|
||||||
"country.north_korea": "North korea",
|
"norfolk_island": "Norfolk island",
|
||||||
"country.north_macedonia": "North macedonia",
|
"north_korea": "North korea",
|
||||||
"country.northern_mariana_islands": "Northern mariana islands",
|
"north_macedonia": "North macedonia",
|
||||||
"country.norway": "Norway",
|
"northern_mariana_islands": "Northern mariana islands",
|
||||||
"country.oman": "Oman",
|
"norway": "Norway",
|
||||||
"country.pakistan": "Pakistan",
|
"oman": "Oman",
|
||||||
"country.palau": "Palau",
|
"pakistan": "Pakistan",
|
||||||
"country.palestine": "Palestine",
|
"palau": "Palau",
|
||||||
"country.panama": "Panama",
|
"palestine": "Palestine",
|
||||||
"country.papua_new_guinea": "Papua new guinea",
|
"panama": "Panama",
|
||||||
"country.paraguay": "Paraguay",
|
"papua_new_guinea": "Papua new guinea",
|
||||||
"country.peru": "Peru",
|
"paraguay": "Paraguay",
|
||||||
"country.philippines": "Philippines",
|
"peru": "Peru",
|
||||||
"country.pitcairn_islands": "Pitcairn islands",
|
"philippines": "Philippines",
|
||||||
"country.poland": "Poland",
|
"pitcairn_islands": "Pitcairn islands",
|
||||||
"country.portugal": "Portugal",
|
"poland": "Poland",
|
||||||
"country.puerto_rico": "Puerto rico",
|
"portugal": "Portugal",
|
||||||
"country.qatar": "Qatar",
|
"puerto_rico": "Puerto rico",
|
||||||
"country.reunion": "Reunion",
|
"qatar": "Qatar",
|
||||||
"country.romania": "Romania",
|
"reunion": "Reunion",
|
||||||
"country.russia": "Russia",
|
"romania": "Romania",
|
||||||
"country.rwanda": "Rwanda",
|
"russia": "Russia",
|
||||||
"country.saint_barthelemy": "Saint barthelemy",
|
"rwanda": "Rwanda",
|
||||||
"country.saint_helena": "Saint helena",
|
"saint_barthelemy": "Saint barthelemy",
|
||||||
"country.saint_kitts_and_nevis": "Saint kitts and nevis",
|
"saint_helena": "Saint helena",
|
||||||
"country.saint_lucia": "Saint lucia",
|
"saint_kitts_and_nevis": "Saint kitts and nevis",
|
||||||
"country.saint_martin": "Saint martin",
|
"saint_lucia": "Saint lucia",
|
||||||
"country.saint_pierre_and_miquelon": "Saint pierre and miquelon",
|
"saint_martin": "Saint martin",
|
||||||
"country.saint_vincent_and_the_grenadines": "Saint vincent and the grenadines",
|
"saint_pierre_and_miquelon": "Saint pierre and miquelon",
|
||||||
"country.samoa": "Samoa",
|
"saint_vincent_and_the_grenadines": "Saint vincent and the grenadines",
|
||||||
"country.san_marino": "San marino",
|
"samoa": "Samoa",
|
||||||
"country.sao_tome_and_principe": "Sao tome and principe",
|
"san_marino": "San marino",
|
||||||
"country.saudi_arabia": "Saudi arabia",
|
"sao_tome_and_principe": "Sao tome and principe",
|
||||||
"country.senegal": "Senegal",
|
"saudi_arabia": "Saudi arabia",
|
||||||
"country.serbia": "Serbia",
|
"senegal": "Senegal",
|
||||||
"country.seychelles": "Seychelles",
|
"serbia": "Serbia",
|
||||||
"country.sierra_leone": "Sierra leone",
|
"seychelles": "Seychelles",
|
||||||
"country.singapore": "Singapore",
|
"sierra_leone": "Sierra leone",
|
||||||
"country.sint_maarten": "Sint maarten",
|
"singapore": "Singapore",
|
||||||
"country.slovakia": "Slovakia",
|
"sint_maarten": "Sint maarten",
|
||||||
"country.slovenia": "Slovenia",
|
"slovakia": "Slovakia",
|
||||||
"country.solomon_islands": "Solomon islands",
|
"slovenia": "Slovenia",
|
||||||
"country.somalia": "Somalia",
|
"solomon_islands": "Solomon islands",
|
||||||
"country.south_africa": "South africa",
|
"somalia": "Somalia",
|
||||||
"country.south_georgia_and_south_sandwich_islands": "South georgia and south sandwich islands",
|
"south_africa": "South africa",
|
||||||
"country.south_korea": "South korea",
|
"south_georgia_and_south_sandwich_islands": "South georgia and south sandwich islands",
|
||||||
"country.south_sudan": "South sudan",
|
"south_korea": "South korea",
|
||||||
"country.spain": "Spain",
|
"south_sudan": "South sudan",
|
||||||
"country.sri_lanka": "Sri lanka",
|
"spain": "Spain",
|
||||||
"country.sudan": "Sudan",
|
"sri_lanka": "Sri lanka",
|
||||||
"country.suriname": "Suriname",
|
"sudan": "Sudan",
|
||||||
"country.svalbard_and_jan_mayen": "Svalbard and jan mayen",
|
"suriname": "Suriname",
|
||||||
"country.sweden": "Sweden",
|
"svalbard_and_jan_mayen": "Svalbard and jan mayen",
|
||||||
"country.switzerland": "Switzerland",
|
"sweden": "Sweden",
|
||||||
"country.syria": "Syria",
|
"switzerland": "Switzerland",
|
||||||
"country.taiwan": "Taiwan",
|
"syria": "Syria",
|
||||||
"country.tajikistan": "Tajikistan",
|
"taiwan": "Taiwan",
|
||||||
"country.tanzania": "Tanzania",
|
"tajikistan": "Tajikistan",
|
||||||
"country.thailand": "Thailand",
|
"tanzania": "Tanzania",
|
||||||
"country.timor_leste": "Timor leste",
|
"thailand": "Thailand",
|
||||||
"country.togo": "Togo",
|
"timor_leste": "Timor leste",
|
||||||
"country.tokelau": "Tokelau",
|
"togo": "Togo",
|
||||||
"country.tonga": "Tonga",
|
"tokelau": "Tokelau",
|
||||||
"country.trinidad_and_tobago": "Trinidad and tobago",
|
"tonga": "Tonga",
|
||||||
"country.tunisia": "Tunisia",
|
"trinidad_and_tobago": "Trinidad and tobago",
|
||||||
"country.turkey": "Turkey",
|
"tunisia": "Tunisia",
|
||||||
"country.turkmenistan": "Turkmenistan",
|
"turkey": "Turkey",
|
||||||
"country.turks_and_caicos_islands": "Turks and caicos islands",
|
"turkmenistan": "Turkmenistan",
|
||||||
"country.tuvalu": "Tuvalu",
|
"turks_and_caicos_islands": "Turks and caicos islands",
|
||||||
"country.us_virgin_islands": "Us virgin islands",
|
"tuvalu": "Tuvalu",
|
||||||
"country.uganda": "Uganda",
|
"us_virgin_islands": "Us virgin islands",
|
||||||
"country.ukraine": "Ukraine",
|
"uganda": "Uganda",
|
||||||
"country.united_arab_emirates": "United arab emirates",
|
"ukraine": "Ukraine",
|
||||||
"country.united_kingdom": "United kingdom",
|
"united_arab_emirates": "United arab emirates",
|
||||||
"country.united_states": "United states",
|
"united_kingdom": "United kingdom",
|
||||||
"country.uruguay": "Uruguay",
|
"united_states": "United states",
|
||||||
"country.uzbekistan": "Uzbekistan",
|
"uruguay": "Uruguay",
|
||||||
"country.vanuatu": "Vanuatu",
|
"uzbekistan": "Uzbekistan",
|
||||||
"country.vatican_city": "Vatican city",
|
"vanuatu": "Vanuatu",
|
||||||
"country.venezuela": "Venezuela",
|
"vatican_city": "Vatican city",
|
||||||
"country.vietnam": "Vietnam",
|
"venezuela": "Venezuela",
|
||||||
"country.wallis_and_futuna": "Wallis and futuna",
|
"vietnam": "Vietnam",
|
||||||
"country.western_sahara": "Western sahara",
|
"wallis_and_futuna": "Wallis and futuna",
|
||||||
"country.yemen": "Yemen",
|
"western_sahara": "Western sahara",
|
||||||
"country.zambia": "Zambia",
|
"yemen": "Yemen",
|
||||||
"country.zimbabwe": "Zimbabwe"
|
"zambia": "Zambia",
|
||||||
|
"zimbabwe": "Zimbabwe"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
"account": "Account",
|
"account": "Account",
|
||||||
"personalInfo": "Personal information",
|
"personalInfo": "Personal information",
|
||||||
"phoneNumber": "Phone number",
|
"phoneNumber": "Phone number",
|
||||||
|
"contactInfo": "Contact info",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
"security": "Security",
|
"security": "Security",
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"verifiedAddress": "Verified addresses",
|
"recentSessions": "Recent Sessions",
|
||||||
"recentLogins": "Recent logins",
|
|
||||||
"activeDevices": "Active devices",
|
"activeDevices": "Active devices",
|
||||||
"settings": "Settings"
|
"settings": "Settings"
|
||||||
}
|
}
|
||||||
@@ -2,203 +2,10 @@
|
|||||||
"labels": {
|
"labels": {
|
||||||
"search": "جست و جو"
|
"search": "جست و جو"
|
||||||
},
|
},
|
||||||
"country": {
|
|
||||||
"afghanistan": "افغانستان",
|
|
||||||
"aland_islands": "جزایر آلند",
|
|
||||||
"albania": "آلبانی",
|
|
||||||
"algeria": "الجزایر",
|
|
||||||
"american_samoa": "ساموای آمریکایی",
|
|
||||||
"andorra": "آندورا",
|
|
||||||
"angola": "آنگولا",
|
|
||||||
"anguilla": "آنگویلا",
|
|
||||||
"antarctica": "جنوبگان",
|
|
||||||
"antigua_and_barbuda": "آنتیگوا و باربودا",
|
|
||||||
"argentina": "آرژانتین",
|
|
||||||
"armenia": "ارمنستان",
|
|
||||||
"aruba": "آروبا",
|
|
||||||
"australia": "استرالیا",
|
|
||||||
"austria": "اتریش",
|
|
||||||
"azerbaijan": "آذربایجان",
|
|
||||||
"bahamas": "باهاما",
|
|
||||||
"bahrain": "بحرین",
|
|
||||||
"bangladesh": "بنگلادش",
|
|
||||||
"barbados": "باربادوس",
|
|
||||||
"belarus": "بلاروس",
|
|
||||||
"belgium": "بلژیک",
|
|
||||||
"belize": "بلیز",
|
|
||||||
"benin": "بنین",
|
|
||||||
"bermuda": "برمودا",
|
|
||||||
"bhutan": "بوتان",
|
|
||||||
"bolivia": "بولیوی",
|
|
||||||
"bosnia_and_herzegovina": "بوسنی و هرزگوین",
|
|
||||||
"botswana": "بوتسوانا",
|
|
||||||
"brazil": "برزیل",
|
|
||||||
"british_virgin_islands": "جزایر ویرجین بریتانیا",
|
|
||||||
"brunei": "برونئی",
|
|
||||||
"bulgaria": "بلغارستان",
|
|
||||||
"burkina_faso": "بورکینافاسو",
|
|
||||||
"burundi": "بوروندی",
|
|
||||||
"cambodia": "کامبوج",
|
|
||||||
"cameroon": "کامرون",
|
|
||||||
"canada": "کانادا",
|
|
||||||
"cape_verde": "کیپ ورد",
|
|
||||||
"cayman_islands": "جزایر کیمن",
|
|
||||||
"central_african_republic": "جمهوری آفریقای مرکزی",
|
|
||||||
"chad": "چاد",
|
|
||||||
"chile": "شیلی",
|
|
||||||
"china": "چین",
|
|
||||||
"colombia": "کلمبیا",
|
|
||||||
"comoros": "کومور",
|
|
||||||
"costa_rica": "کاستاریکا",
|
|
||||||
"cote_divoire": "ساحل عاج",
|
|
||||||
"croatia": "کرواسی",
|
|
||||||
"cuba": "کوبا",
|
|
||||||
"cyprus": "قبرس",
|
|
||||||
"czech_republic": "جمهوری چک",
|
|
||||||
"denmark": "دانمارک",
|
|
||||||
"djibouti": "جیبوتی",
|
|
||||||
"dominica": "دومینیکا",
|
|
||||||
"dominican_republic": "جمهوری دومینیکن",
|
|
||||||
"ecuador": "اکوادور",
|
|
||||||
"egypt": "مصر",
|
|
||||||
"el_salvador": "السالوادور",
|
|
||||||
"equatorial_guinea": "گینه استوایی",
|
|
||||||
"eritrea": "اریتره",
|
|
||||||
"estonia": "استونی",
|
|
||||||
"eswatini": "سوازیلند",
|
|
||||||
"ethiopia": "اتیوپی",
|
|
||||||
"fiji": "فیجی",
|
|
||||||
"finland": "فنلاند",
|
|
||||||
"france": "فرانسه",
|
|
||||||
"gabon": "گابن",
|
|
||||||
"gambia": "گامبیا",
|
|
||||||
"georgia": "گرجستان",
|
|
||||||
"germany": "آلمان",
|
|
||||||
"ghana": "غنا",
|
|
||||||
"greece": "یونان",
|
|
||||||
"guatemala": "گواتمالا",
|
|
||||||
"guinea": "گینه",
|
|
||||||
"guinea_bissau": "گینه بیسائو",
|
|
||||||
"guyana": "گویان",
|
|
||||||
"haiti": "هائیتی",
|
|
||||||
"honduras": "هندوراس",
|
|
||||||
"hungary": "مجارستان",
|
|
||||||
"iceland": "ایسلند",
|
|
||||||
"india": "هندوستان",
|
|
||||||
"indonesia": "اندونزی",
|
|
||||||
"iran": "ایران",
|
|
||||||
"iraq": "عراق",
|
|
||||||
"ireland": "ایرلند",
|
|
||||||
"israel": "اسرائیل",
|
|
||||||
"italy": "ایتالیا",
|
|
||||||
"jamaica": "جامائیکا",
|
|
||||||
"japan": "ژاپن",
|
|
||||||
"jordan": "اردن",
|
|
||||||
"kazakhstan": "قزاقستان",
|
|
||||||
"kenya": "کنیا",
|
|
||||||
"kuwait": "کویت",
|
|
||||||
"kyrgyzstan": "قرقیزستان",
|
|
||||||
"laos": "لائوس",
|
|
||||||
"latvia": "لتونی",
|
|
||||||
"lebanon": "لبنان",
|
|
||||||
"lesotho": "لسوتو",
|
|
||||||
"liberia": "لیبریا",
|
|
||||||
"libya": "لیبی",
|
|
||||||
"luxembourg": "لوکزامبورگ",
|
|
||||||
"malaysia": "مالزی",
|
|
||||||
"maldives": "مالدیو",
|
|
||||||
"mali": "مالی",
|
|
||||||
"malta": "مالت",
|
|
||||||
"mauritania": "موریتانی",
|
|
||||||
"mauritius": "موریس",
|
|
||||||
"mexico": "مکزیک",
|
|
||||||
"moldova": "مولداوی",
|
|
||||||
"monaco": "موناکو",
|
|
||||||
"mongolia": "مغولستان",
|
|
||||||
"morocco": "مراکش",
|
|
||||||
"mozambique": "موزامبیک",
|
|
||||||
"myanmar": "میانمار",
|
|
||||||
"namibia": "نامیبیا",
|
|
||||||
"nepal": "نپال",
|
|
||||||
"netherlands": "هلند",
|
|
||||||
"new_zealand": "نیوزیلند",
|
|
||||||
"nicaragua": "نیکاراگوئه",
|
|
||||||
"niger": "نیجر",
|
|
||||||
"nigeria": "نیجریه",
|
|
||||||
"north_korea": "کره شمالی",
|
|
||||||
"north_macedonia": "مقدونیه",
|
|
||||||
"norway": "نروژ",
|
|
||||||
"oman": "عمان",
|
|
||||||
"pakistan": "پاکستان",
|
|
||||||
"palau": "پالائو",
|
|
||||||
"panama": "پاناما",
|
|
||||||
"papua_new_guinea": "پاپوآ گینه نو",
|
|
||||||
"paraguay": "پاراگوئه",
|
|
||||||
"peru": "پرو",
|
|
||||||
"philippines": "فیلیپین",
|
|
||||||
"poland": "لهستان",
|
|
||||||
"portugal": "پرتغال",
|
|
||||||
"qatar": "قطر",
|
|
||||||
"romania": "رومانی",
|
|
||||||
"russia": "روسیه",
|
|
||||||
"rwanda": "رواندا",
|
|
||||||
"saudi_arabia": "عربستان سعودی",
|
|
||||||
"senegal": "سنگال",
|
|
||||||
"serbia": "صربستان",
|
|
||||||
"seychelles": "سیشل",
|
|
||||||
"sierra_leone": "سیرالئون",
|
|
||||||
"singapore": "سنگاپور",
|
|
||||||
"south_africa": "آفریقای جنوبی",
|
|
||||||
"south_korea": "کره جنوبی",
|
|
||||||
"south_sudan": "سودان جنوبی",
|
|
||||||
"spain": "اسپانیا",
|
|
||||||
"sri_lanka": "سریلانکا",
|
|
||||||
"sudan": "سودان",
|
|
||||||
"suriname": "سورینام",
|
|
||||||
"sweden": "سوئد",
|
|
||||||
"switzerland": "سوئیس",
|
|
||||||
"syria": "سوریه",
|
|
||||||
"taiwan": "تایوان",
|
|
||||||
"tajikistan": "تاجیکستان",
|
|
||||||
"tanzania": "تانزانیا",
|
|
||||||
"thailand": "تایلند",
|
|
||||||
"timor_leste": "تیمور شرقی",
|
|
||||||
"togo": "توگو",
|
|
||||||
"tonga": "تونگا",
|
|
||||||
"trinidad_and_tobago": "ترینیداد و توباگو",
|
|
||||||
"tunisia": "تونس",
|
|
||||||
"turkey": "ترکیه",
|
|
||||||
"turkmenistan": "ترکمنستان",
|
|
||||||
"tuvalu": "تووالو",
|
|
||||||
"uganda": "اوگاندا",
|
|
||||||
"ukraine": "اوکراین",
|
|
||||||
"united_arab_emirates": "امارات متحده عربی",
|
|
||||||
"united_kingdom": "انگلستان",
|
|
||||||
"united_states": "ایالات متحده آمریکا",
|
|
||||||
"uruguay": "اروگوئه",
|
|
||||||
"uzbekistan": "ازبکستان",
|
|
||||||
"vanuatu": "وانواتو",
|
|
||||||
"venezuela": "ونزوئلا",
|
|
||||||
"vietnam": "ویتنام",
|
|
||||||
"yemen": "یمن",
|
|
||||||
"zambia": "زامبیا",
|
|
||||||
"zimbabwe": "زیمبابوه"
|
|
||||||
},
|
|
||||||
"messages": {
|
"messages": {
|
||||||
"pageNotFound": "صفحه مورد نظر پیدا نشد.",
|
"pageNotFound": "صفحه مورد نظر پیدا نشد.",
|
||||||
"noResualtFound": "نتیجه ای یافت نشد.",
|
"noResualtFound": "نتیجه ای یافت نشد.",
|
||||||
"serverError": "خطای سمت سرور"
|
"serverError": "خطای سمت سرور",
|
||||||
},
|
"networkError": "خطای شبکه. لطفاً اتصال اینترنت خود را بررسی کرده و دوباره تلاش کنید."
|
||||||
"side": {
|
|
||||||
"account": "حساب کاربری",
|
|
||||||
"personalInfo": "اطلاعات شخصی",
|
|
||||||
"contactInfo": "شماره تماس",
|
|
||||||
"email": "ایمیل",
|
|
||||||
"security": "امنیت",
|
|
||||||
"password": "رمز عبور",
|
|
||||||
"confirmedIps": "آدرس های تایید شده",
|
|
||||||
"recentSessions": "ورود های اخیر",
|
|
||||||
"activeSessions": "نشست های فعال",
|
|
||||||
"setting": "تنظیمات"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,182 +1,184 @@
|
|||||||
{
|
{
|
||||||
"country.afghanistan": "افغانستان",
|
"country": {
|
||||||
"country.aland_islands": "جزایر آلند",
|
"afghanistan": "افغانستان",
|
||||||
"country.albania": "آلبانی",
|
"aland_islands": "جزایر آلند",
|
||||||
"country.algeria": "الجزایر",
|
"albania": "آلبانی",
|
||||||
"country.american_samoa": "ساموای آمریکایی",
|
"algeria": "الجزایر",
|
||||||
"country.andorra": "آندورا",
|
"american_samoa": "ساموای آمریکایی",
|
||||||
"country.angola": "آنگولا",
|
"andorra": "آندورا",
|
||||||
"country.anguilla": "آنگویلا",
|
"angola": "آنگولا",
|
||||||
"country.antarctica": "جنوبگان",
|
"anguilla": "آنگویلا",
|
||||||
"country.antigua_and_barbuda": "آنتیگوا و باربودا",
|
"antarctica": "جنوبگان",
|
||||||
"country.argentina": "آرژانتین",
|
"antigua_and_barbuda": "آنتیگوا و باربودا",
|
||||||
"country.armenia": "ارمنستان",
|
"argentina": "آرژانتین",
|
||||||
"country.aruba": "آروبا",
|
"armenia": "ارمنستان",
|
||||||
"country.australia": "استرالیا",
|
"aruba": "آروبا",
|
||||||
"country.austria": "اتریش",
|
"australia": "استرالیا",
|
||||||
"country.azerbaijan": "آذربایجان",
|
"austria": "اتریش",
|
||||||
"country.bahamas": "باهاما",
|
"azerbaijan": "آذربایجان",
|
||||||
"country.bahrain": "بحرین",
|
"bahamas": "باهاما",
|
||||||
"country.bangladesh": "بنگلادش",
|
"bahrain": "بحرین",
|
||||||
"country.barbados": "باربادوس",
|
"bangladesh": "بنگلادش",
|
||||||
"country.belarus": "بلاروس",
|
"barbados": "باربادوس",
|
||||||
"country.belgium": "بلژیک",
|
"belarus": "بلاروس",
|
||||||
"country.belize": "بلیز",
|
"belgium": "بلژیک",
|
||||||
"country.benin": "بنین",
|
"belize": "بلیز",
|
||||||
"country.bermuda": "برمودا",
|
"benin": "بنین",
|
||||||
"country.bhutan": "بوتان",
|
"bermuda": "برمودا",
|
||||||
"country.bolivia": "بولیوی",
|
"bhutan": "بوتان",
|
||||||
"country.bosnia_and_herzegovina": "بوسنی و هرزگوین",
|
"bolivia": "بولیوی",
|
||||||
"country.botswana": "بوتسوانا",
|
"bosnia_and_herzegovina": "بوسنی و هرزگوین",
|
||||||
"country.brazil": "برزیل",
|
"botswana": "بوتسوانا",
|
||||||
"country.british_virgin_islands": "جزایر ویرجین بریتانیا",
|
"brazil": "برزیل",
|
||||||
"country.brunei": "برونئی",
|
"british_virgin_islands": "جزایر ویرجین بریتانیا",
|
||||||
"country.bulgaria": "بلغارستان",
|
"brunei": "برونئی",
|
||||||
"country.burkina_faso": "بورکینافاسو",
|
"bulgaria": "بلغارستان",
|
||||||
"country.burundi": "بوروندی",
|
"burkina_faso": "بورکینافاسو",
|
||||||
"country.cambodia": "کامبوج",
|
"burundi": "بوروندی",
|
||||||
"country.cameroon": "کامرون",
|
"cambodia": "کامبوج",
|
||||||
"country.canada": "کانادا",
|
"cameroon": "کامرون",
|
||||||
"country.cape_verde": "کیپ ورد",
|
"canada": "کانادا",
|
||||||
"country.cayman_islands": "جزایر کیمن",
|
"cape_verde": "کیپ ورد",
|
||||||
"country.central_african_republic": "جمهوری آفریقای مرکزی",
|
"cayman_islands": "جزایر کیمن",
|
||||||
"country.chad": "چاد",
|
"central_african_republic": "جمهوری آفریقای مرکزی",
|
||||||
"country.chile": "شیلی",
|
"chad": "چاد",
|
||||||
"country.china": "چین",
|
"chile": "شیلی",
|
||||||
"country.colombia": "کلمبیا",
|
"china": "چین",
|
||||||
"country.comoros": "کومور",
|
"colombia": "کلمبیا",
|
||||||
"country.costa_rica": "کاستاریکا",
|
"comoros": "کومور",
|
||||||
"country.cote_divoire": "ساحل عاج",
|
"costa_rica": "کاستاریکا",
|
||||||
"country.croatia": "کرواسی",
|
"cote_divoire": "ساحل عاج",
|
||||||
"country.cuba": "کوبا",
|
"croatia": "کرواسی",
|
||||||
"country.cyprus": "قبرس",
|
"cuba": "کوبا",
|
||||||
"country.czech_republic": "جمهوری چک",
|
"cyprus": "قبرس",
|
||||||
"country.denmark": "دانمارک",
|
"czech_republic": "جمهوری چک",
|
||||||
"country.djibouti": "جیبوتی",
|
"denmark": "دانمارک",
|
||||||
"country.dominica": "دومینیکا",
|
"djibouti": "جیبوتی",
|
||||||
"country.dominican_republic": "جمهوری دومینیکن",
|
"dominica": "دومینیکا",
|
||||||
"country.ecuador": "اکوادور",
|
"dominican_republic": "جمهوری دومینیکن",
|
||||||
"country.egypt": "مصر",
|
"ecuador": "اکوادور",
|
||||||
"country.el_salvador": "السالوادور",
|
"egypt": "مصر",
|
||||||
"country.equatorial_guinea": "گینه استوایی",
|
"el_salvador": "السالوادور",
|
||||||
"country.eritrea": "اریتره",
|
"equatorial_guinea": "گینه استوایی",
|
||||||
"country.estonia": "استونی",
|
"eritrea": "اریتره",
|
||||||
"country.eswatini": "سوازیلند",
|
"estonia": "استونی",
|
||||||
"country.ethiopia": "اتیوپی",
|
"eswatini": "سوازیلند",
|
||||||
"country.fiji": "فیجی",
|
"ethiopia": "اتیوپی",
|
||||||
"country.finland": "فنلاند",
|
"fiji": "فیجی",
|
||||||
"country.france": "فرانسه",
|
"finland": "فنلاند",
|
||||||
"country.gabon": "گابن",
|
"france": "فرانسه",
|
||||||
"country.gambia": "گامبیا",
|
"gabon": "گابن",
|
||||||
"country.georgia": "گرجستان",
|
"gambia": "گامبیا",
|
||||||
"country.germany": "آلمان",
|
"georgia": "گرجستان",
|
||||||
"country.ghana": "غنا",
|
"germany": "آلمان",
|
||||||
"country.greece": "یونان",
|
"ghana": "غنا",
|
||||||
"country.guatemala": "گواتمالا",
|
"greece": "یونان",
|
||||||
"country.guinea": "گینه",
|
"guatemala": "گواتمالا",
|
||||||
"country.guinea_bissau": "گینه بیسائو",
|
"guinea": "گینه",
|
||||||
"country.guyana": "گویان",
|
"guinea_bissau": "گینه بیسائو",
|
||||||
"country.haiti": "هائیتی",
|
"guyana": "گویان",
|
||||||
"country.honduras": "هندوراس",
|
"haiti": "هائیتی",
|
||||||
"country.hungary": "مجارستان",
|
"honduras": "هندوراس",
|
||||||
"country.iceland": "ایسلند",
|
"hungary": "مجارستان",
|
||||||
"country.india": "هندوستان",
|
"iceland": "ایسلند",
|
||||||
"country.indonesia": "اندونزی",
|
"india": "هندوستان",
|
||||||
"country.iran": "ایران",
|
"indonesia": "اندونزی",
|
||||||
"country.iraq": "عراق",
|
"iran": "ایران",
|
||||||
"country.ireland": "ایرلند",
|
"iraq": "عراق",
|
||||||
"country.israel": "اسرائیل",
|
"ireland": "ایرلند",
|
||||||
"country.italy": "ایتالیا",
|
"israel": "اسرائیل",
|
||||||
"country.jamaica": "جامائیکا",
|
"italy": "ایتالیا",
|
||||||
"country.japan": "ژاپن",
|
"jamaica": "جامائیکا",
|
||||||
"country.jordan": "اردن",
|
"japan": "ژاپن",
|
||||||
"country.kazakhstan": "قزاقستان",
|
"jordan": "اردن",
|
||||||
"country.kenya": "کنیا",
|
"kazakhstan": "قزاقستان",
|
||||||
"country.kuwait": "کویت",
|
"kenya": "کنیا",
|
||||||
"country.kyrgyzstan": "قرقیزستان",
|
"kuwait": "کویت",
|
||||||
"country.laos": "لائوس",
|
"kyrgyzstan": "قرقیزستان",
|
||||||
"country.latvia": "لتونی",
|
"laos": "لائوس",
|
||||||
"country.lebanon": "لبنان",
|
"latvia": "لتونی",
|
||||||
"country.lesotho": "لسوتو",
|
"lebanon": "لبنان",
|
||||||
"country.liberia": "لیبریا",
|
"lesotho": "لسوتو",
|
||||||
"country.libya": "لیبی",
|
"liberia": "لیبریا",
|
||||||
"country.luxembourg": "لوکزامبورگ",
|
"libya": "لیبی",
|
||||||
"country.malaysia": "مالزی",
|
"luxembourg": "لوکزامبورگ",
|
||||||
"country.maldives": "مالدیو",
|
"malaysia": "مالزی",
|
||||||
"country.mali": "مالی",
|
"maldives": "مالدیو",
|
||||||
"country.malta": "مالت",
|
"mali": "مالی",
|
||||||
"country.mauritania": "موریتانی",
|
"malta": "مالت",
|
||||||
"country.mauritius": "موریس",
|
"mauritania": "موریتانی",
|
||||||
"country.mexico": "مکزیک",
|
"mauritius": "موریس",
|
||||||
"country.moldova": "مولداوی",
|
"mexico": "مکزیک",
|
||||||
"country.monaco": "موناکو",
|
"moldova": "مولداوی",
|
||||||
"country.mongolia": "مغولستان",
|
"monaco": "موناکو",
|
||||||
"country.morocco": "مراکش",
|
"mongolia": "مغولستان",
|
||||||
"country.mozambique": "موزامبیک",
|
"morocco": "مراکش",
|
||||||
"country.myanmar": "میانمار",
|
"mozambique": "موزامبیک",
|
||||||
"country.namibia": "نامیبیا",
|
"myanmar": "میانمار",
|
||||||
"country.nepal": "نپال",
|
"namibia": "نامیبیا",
|
||||||
"country.netherlands": "هلند",
|
"nepal": "نپال",
|
||||||
"country.new_zealand": "نیوزیلند",
|
"netherlands": "هلند",
|
||||||
"country.nicaragua": "نیکاراگوئه",
|
"new_zealand": "نیوزیلند",
|
||||||
"country.niger": "نیجر",
|
"nicaragua": "نیکاراگوئه",
|
||||||
"country.nigeria": "نیجریه",
|
"niger": "نیجر",
|
||||||
"country.north_korea": "کره شمالی",
|
"nigeria": "نیجریه",
|
||||||
"country.north_macedonia": "مقدونیه",
|
"north_korea": "کره شمالی",
|
||||||
"country.norway": "نروژ",
|
"north_macedonia": "مقدونیه",
|
||||||
"country.oman": "عمان",
|
"norway": "نروژ",
|
||||||
"country.pakistan": "پاکستان",
|
"oman": "عمان",
|
||||||
"country.palau": "پالائو",
|
"pakistan": "پاکستان",
|
||||||
"country.panama": "پاناما",
|
"palau": "پالائو",
|
||||||
"country.papua_new_guinea": "پاپوآ گینه نو",
|
"panama": "پاناما",
|
||||||
"country.paraguay": "پاراگوئه",
|
"papua_new_guinea": "پاپوآ گینه نو",
|
||||||
"country.peru": "پرو",
|
"paraguay": "پاراگوئه",
|
||||||
"country.philippines": "فیلیپین",
|
"peru": "پرو",
|
||||||
"country.poland": "لهستان",
|
"philippines": "فیلیپین",
|
||||||
"country.portugal": "پرتغال",
|
"poland": "لهستان",
|
||||||
"country.qatar": "قطر",
|
"portugal": "پرتغال",
|
||||||
"country.romania": "رومانی",
|
"qatar": "قطر",
|
||||||
"country.russia": "روسیه",
|
"romania": "رومانی",
|
||||||
"country.rwanda": "رواندا",
|
"russia": "روسیه",
|
||||||
"country.saudi_arabia": "عربستان سعودی",
|
"rwanda": "رواندا",
|
||||||
"country.senegal": "سنگال",
|
"saudi_arabia": "عربستان سعودی",
|
||||||
"country.serbia": "صربستان",
|
"senegal": "سنگال",
|
||||||
"country.seychelles": "سیشل",
|
"serbia": "صربستان",
|
||||||
"country.sierra_leone": "سیرالئون",
|
"seychelles": "سیشل",
|
||||||
"country.singapore": "سنگاپور",
|
"sierra_leone": "سیرالئون",
|
||||||
"country.south_africa": "آفریقای جنوبی",
|
"singapore": "سنگاپور",
|
||||||
"country.south_korea": "کره جنوبی",
|
"south_africa": "آفریقای جنوبی",
|
||||||
"country.south_sudan": "سودان جنوبی",
|
"south_korea": "کره جنوبی",
|
||||||
"country.spain": "اسپانیا",
|
"south_sudan": "سودان جنوبی",
|
||||||
"country.sri_lanka": "سریلانکا",
|
"spain": "اسپانیا",
|
||||||
"country.sudan": "سودان",
|
"sri_lanka": "سریلانکا",
|
||||||
"country.suriname": "سورینام",
|
"sudan": "سودان",
|
||||||
"country.sweden": "سوئد",
|
"suriname": "سورینام",
|
||||||
"country.switzerland": "سوئیس",
|
"sweden": "سوئد",
|
||||||
"country.syria": "سوریه",
|
"switzerland": "سوئیس",
|
||||||
"country.taiwan": "تایوان",
|
"syria": "سوریه",
|
||||||
"country.tajikistan": "تاجیکستان",
|
"taiwan": "تایوان",
|
||||||
"country.tanzania": "تانزانیا",
|
"tajikistan": "تاجیکستان",
|
||||||
"country.thailand": "تایلند",
|
"tanzania": "تانزانیا",
|
||||||
"country.timor_leste": "تیمور شرقی",
|
"thailand": "تایلند",
|
||||||
"country.togo": "توگو",
|
"timor_leste": "تیمور شرقی",
|
||||||
"country.tonga": "تونگا",
|
"togo": "توگو",
|
||||||
"country.trinidad_and_tobago": "ترینیداد و توباگو",
|
"tonga": "تونگا",
|
||||||
"country.tunisia": "تونس",
|
"trinidad_and_tobago": "ترینیداد و توباگو",
|
||||||
"country.turkey": "ترکیه",
|
"tunisia": "تونس",
|
||||||
"country.turkmenistan": "ترکمنستان",
|
"turkey": "ترکیه",
|
||||||
"country.tuvalu": "تووالو",
|
"turkmenistan": "ترکمنستان",
|
||||||
"country.uganda": "اوگاندا",
|
"tuvalu": "تووالو",
|
||||||
"country.ukraine": "اوکراین",
|
"uganda": "اوگاندا",
|
||||||
"country.united_arab_emirates": "امارات متحده عربی",
|
"ukraine": "اوکراین",
|
||||||
"country.united_kingdom": "انگلستان",
|
"united_arab_emirates": "امارات متحده عربی",
|
||||||
"country.united_states": "ایالات متحده آمریکا",
|
"united_kingdom": "انگلستان",
|
||||||
"country.uruguay": "اروگوئه",
|
"united_states": "ایالات متحده آمریکا",
|
||||||
"country.uzbekistan": "ازبکستان",
|
"uruguay": "اروگوئه",
|
||||||
"country.vanuatu": "وانواتو",
|
"uzbekistan": "ازبکستان",
|
||||||
"country.venezuela": "ونزوئلا",
|
"vanuatu": "وانواتو",
|
||||||
"country.vietnam": "ویتنام",
|
"venezuela": "ونزوئلا",
|
||||||
"country.yemen": "یمن",
|
"vietnam": "ویتنام",
|
||||||
"country.zambia": "زامبیا",
|
"yemen": "یمن",
|
||||||
"country.zimbabwe": "زیمبابوه"
|
"zambia": "زامبیا",
|
||||||
|
"zimbabwe": "زیمبابوه"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"side": {
|
|
||||||
"account": "حساب کاربری",
|
|
||||||
"personalInfo": "اطلاعات شخصی",
|
|
||||||
"phoneNumber": "شماره تماس",
|
|
||||||
"email": "ایمیل",
|
|
||||||
"security": "امنیت",
|
|
||||||
"password": "رمز عبور",
|
|
||||||
"verifiedAddress": "آدرس های تایید شده",
|
|
||||||
"recentLogins": "ورود های اخیر",
|
|
||||||
"activeDevices": "دستگاه های فعال",
|
|
||||||
"settings": "تنظیمات"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
13
public/locales/fa/sideNav.json
Normal file
13
public/locales/fa/sideNav.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"side": {
|
||||||
|
"account": "حساب کاربری",
|
||||||
|
"personalInfo": "اطلاعات شخصی",
|
||||||
|
"contactInfo": "شماره تماس",
|
||||||
|
"email": "ایمیل",
|
||||||
|
"security": "امنیت",
|
||||||
|
"password": "رمز عبور",
|
||||||
|
"recentSessions": "ورود های اخیر",
|
||||||
|
"activeSessions": "نشست های فعال",
|
||||||
|
"setting": "تنظیمات"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,7 +34,7 @@ export function CardContainer({
|
|||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
alignItems: { xs: 'flex-start', sm: 'center' },
|
alignItems: { xs: 'flex-start', sm: 'center' },
|
||||||
flexDirection: { xs: 'column', sm: 'row' },
|
flexDirection: { xs: 'column', sm: 'row' },
|
||||||
bgcolor: highlighted ? 'primary.light' : 'background.default',
|
bgcolor: highlighted ? 'background.primary' : 'background.default',
|
||||||
p: 2,
|
p: 2,
|
||||||
borderRadius: 1,
|
borderRadius: 1,
|
||||||
gap: { xs: 1, sm: 0 },
|
gap: { xs: 1, sm: 0 },
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export const Toolbar: React.FC<ToolbarProps> = ({
|
|||||||
<MuiToolbar
|
<MuiToolbar
|
||||||
sx={{
|
sx={{
|
||||||
height: (t) => t.spacing(isMobile ? 8 : 10.5),
|
height: (t) => t.spacing(isMobile ? 8 : 10.5),
|
||||||
|
bgcolor: 'background.paper',
|
||||||
px: isMobile ? 3 : 2,
|
px: isMobile ? 3 : 2,
|
||||||
borderBottom: (t) => `1px solid ${t.palette.divider}`,
|
borderBottom: (t) => `1px solid ${t.palette.divider}`,
|
||||||
boxSizing: 'content-box',
|
boxSizing: 'content-box',
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ const getIcon = (icon?: Iconsax) => (isSelected: boolean) =>
|
|||||||
<Icon Component={icon} variant={isSelected ? 'Bold' : 'Broken'} />
|
<Icon Component={icon} variant={isSelected ? 'Bold' : 'Broken'} />
|
||||||
) : undefined;
|
) : undefined;
|
||||||
|
|
||||||
|
// TODO: make this a hook
|
||||||
export function buildNavItems(routes: RouteConfig[]): NavItemConfig[] {
|
export function buildNavItems(routes: RouteConfig[]): NavItemConfig[] {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation('sideNav');
|
||||||
|
|
||||||
return routes.flatMap((route) => {
|
return routes.flatMap((route) => {
|
||||||
// Check if route itself does not have a navItem but its child has
|
// Check if route itself does not have a navItem but its child has
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export const Loading = () => {
|
|||||||
sx={{
|
sx={{
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
backgroundColor: (t) => t.palette.background.default,
|
backgroundColor: 'background.default',
|
||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
inset: '0',
|
inset: '0',
|
||||||
zIndex: (t) => t.zIndex.tooltip + 1,
|
zIndex: (t) => t.zIndex.tooltip + 1,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
} from '../types/completionFormApiTypes';
|
} from '../types/completionFormApiTypes';
|
||||||
import apiClient from '@/lib/apiClient';
|
import apiClient from '@/lib/apiClient';
|
||||||
|
|
||||||
|
// TODO: define generic type
|
||||||
export const sendEmailOtpApi = async (payload: SendEmailOtpPayload) => {
|
export const sendEmailOtpApi = async (payload: SendEmailOtpPayload) => {
|
||||||
return apiClient.post('/User/SendEmailOtp', payload);
|
return apiClient.post('/User/SendEmailOtp', payload);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export const AccountCreatedClubBanner = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack direction="row" spacing={2} sx={{ pb: 1 }} alignItems="start">
|
<Stack direction="row" spacing={2} sx={{ pb: 1 }} alignItems="start">
|
||||||
|
{/* FIXME: Replace with Club Logo (this icon is placeholder) */}
|
||||||
<Profile2User size={80} color={theme.palette.club.main} />
|
<Profile2User size={80} color={theme.palette.club.main} />
|
||||||
|
|
||||||
<Stack spacing={0.5}>
|
<Stack spacing={0.5}>
|
||||||
|
|||||||
@@ -1,27 +1,32 @@
|
|||||||
import { Paper } from '@mui/material';
|
import { Paper, type SxProps, type Theme } from '@mui/material';
|
||||||
import { type PropsWithChildren } from 'react';
|
import { type PropsWithChildren } from 'react';
|
||||||
|
|
||||||
export interface AuthenticationCardProps extends PropsWithChildren {
|
export interface AuthenticationCardProps extends PropsWithChildren {
|
||||||
maxWidth?: string;
|
maxWidth?: string;
|
||||||
|
sx?: SxProps<Theme>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Beacuse in the otp verify there is a element outside of the authentication card
|
// Beacuse in the otp verify there is a element outside of the authentication card
|
||||||
export const AuthenticationCard = ({
|
export const AuthenticationCard = ({
|
||||||
children,
|
children,
|
||||||
maxWidth,
|
maxWidth,
|
||||||
|
sx,
|
||||||
}: AuthenticationCardProps) => {
|
}: AuthenticationCardProps) => {
|
||||||
return (
|
return (
|
||||||
<Paper
|
<Paper
|
||||||
elevation={0}
|
elevation={0}
|
||||||
sx={{
|
sx={{
|
||||||
borderRadius: 4,
|
borderRadius: 2,
|
||||||
p: {
|
p: {
|
||||||
xs: 4,
|
xs: 3,
|
||||||
md: 6,
|
md: 6,
|
||||||
},
|
},
|
||||||
marginInline: 2,
|
marginInline: 2,
|
||||||
width: (t) => `calc(100% - ${t.spacing(2)})`,
|
boxSizing: 'border-box',
|
||||||
|
width: '100%',
|
||||||
maxWidth: maxWidth ?? '552px',
|
maxWidth: maxWidth ?? '552px',
|
||||||
|
|
||||||
|
...sx,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export function CountryCodeSelector({
|
|||||||
const open = Boolean(anchorEl);
|
const open = Boolean(anchorEl);
|
||||||
const searchInputRef = useRef<HTMLInputElement>(null);
|
const searchInputRef = useRef<HTMLInputElement>(null);
|
||||||
const menuWidth = menuAnchor ? menuAnchor.clientWidth : 'auto';
|
const menuWidth = menuAnchor ? menuAnchor.clientWidth : 'auto';
|
||||||
const { t, i18n } = useTranslation();
|
const { t, i18n } = useTranslation(['country', 'common']);
|
||||||
|
|
||||||
const selectedCountry =
|
const selectedCountry =
|
||||||
countries.find((c) => c.phone === value) || countries[0];
|
countries.find((c) => c.phone === value) || countries[0];
|
||||||
@@ -176,7 +176,7 @@ export function CountryCodeSelector({
|
|||||||
inputRef={searchInputRef}
|
inputRef={searchInputRef}
|
||||||
size="small"
|
size="small"
|
||||||
fullWidth
|
fullWidth
|
||||||
label={t('labels.search')}
|
label={t('labels.search', { ns: 'common' })}
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
/>
|
/>
|
||||||
@@ -207,7 +207,7 @@ export function CountryCodeSelector({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
<ListItemText primary={t(country.label)} />
|
<ListItemText primary={t(country.label, { ns: 'country' })} />
|
||||||
<Typography color="text.secondary">
|
<Typography color="text.secondary">
|
||||||
{country.phone}
|
{country.phone}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import { format } from 'date-fns';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { toLocaleDigits } from '@/utils/persianDigit';
|
import { toLocaleDigits } from '@/utils/persianDigit';
|
||||||
import { type DateOfBirthProps } from '../../types/settingForm';
|
import { type DateOfBirthProps } from '../../types/settingForm';
|
||||||
|
import { Icon } from '@rkheftan/harmony-ui';
|
||||||
|
import { Calendar } from 'iconsax-react';
|
||||||
|
|
||||||
export default function DateOfBirth({ value, onChange }: DateOfBirthProps) {
|
export default function DateOfBirth({ value, onChange }: DateOfBirthProps) {
|
||||||
const { t, i18n } = useTranslation('completionForm');
|
const { t, i18n } = useTranslation('completionForm');
|
||||||
@@ -49,6 +51,10 @@ export default function DateOfBirth({ value, onChange }: DateOfBirthProps) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const CustomCalendarIcon = () => (
|
||||||
|
<Icon Component={Calendar} color="primary.main" variant="Bold" />
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LocalizationProvider dateAdapter={Adapter} adapterLocale={locale}>
|
<LocalizationProvider dateAdapter={Adapter} adapterLocale={locale}>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
@@ -57,12 +63,18 @@ export default function DateOfBirth({ value, onChange }: DateOfBirthProps) {
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
format={formatString}
|
format={formatString}
|
||||||
dayOfWeekFormatter={dayOfWeekFormatter}
|
dayOfWeekFormatter={dayOfWeekFormatter}
|
||||||
slots={{ day: CustomDay }}
|
slots={{ day: CustomDay, openPickerIcon: CustomCalendarIcon }}
|
||||||
|
disableFuture
|
||||||
slotProps={{
|
slotProps={{
|
||||||
textField: {
|
textField: {
|
||||||
fullWidth: true,
|
fullWidth: true,
|
||||||
|
},
|
||||||
|
popper: {
|
||||||
sx: {
|
sx: {
|
||||||
flex: '1 1 260px',
|
'& .MuiDateCalendar-root': {
|
||||||
|
// TODO: fix this to use textfield width instead of defining hardcode
|
||||||
|
width: '309px',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -8,41 +8,56 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
InputAdornment,
|
InputAdornment,
|
||||||
IconButton,
|
IconButton,
|
||||||
CircularProgress,
|
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { TickCircle, Edit } from 'iconsax-react';
|
import { TickCircle, Edit2 } from 'iconsax-react';
|
||||||
import { Icon } from '@rkheftan/harmony-ui';
|
import { Icon } from '@rkheftan/harmony-ui';
|
||||||
import { type EmailSectionProps } from '../../types/settingForm';
|
import { type EmailSectionProps } from '../../types/settingForm';
|
||||||
|
import { toLocaleDigits } from '@/utils/persianDigit';
|
||||||
|
import { isNumeric } from '@/utils/regexes/isNumeric';
|
||||||
|
|
||||||
export function EmailSection({
|
export function EmailSection({
|
||||||
showEmail,
|
showEmail,
|
||||||
setShowEmail,
|
setShowEmail,
|
||||||
email,
|
email,
|
||||||
setEmail,
|
setEmail,
|
||||||
correctEmail,
|
|
||||||
codeSent,
|
codeSent,
|
||||||
verificationCode,
|
verificationCode,
|
||||||
setVerificationCode,
|
setVerificationCode,
|
||||||
buttonState,
|
buttonState,
|
||||||
getButtonLabel,
|
|
||||||
handleSendCode,
|
handleSendCode,
|
||||||
handleVerifyCode,
|
handleVerifyCode,
|
||||||
emailVerified,
|
emailVerified,
|
||||||
loading,
|
isVerifyingCode,
|
||||||
|
isSendingCode,
|
||||||
handleEditEmail,
|
handleEditEmail,
|
||||||
|
errors,
|
||||||
|
touched,
|
||||||
|
handleBlur,
|
||||||
|
countdown,
|
||||||
}: EmailSectionProps) {
|
}: EmailSectionProps) {
|
||||||
const { t } = useTranslation('completionForm');
|
const { t, i18n } = useTranslation('completionForm');
|
||||||
|
|
||||||
const onSendCodeClick = () => {
|
|
||||||
if (!correctEmail) return;
|
|
||||||
handleSendCode();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleToggleEmail = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleToggleEmail = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setShowEmail(e.target.checked);
|
setShowEmail(e.target.checked);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleVerificationCodeChange = (
|
||||||
|
e: React.ChangeEvent<HTMLInputElement>,
|
||||||
|
) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
// Allow only digits and enforce the max length
|
||||||
|
if (isNumeric(value) && value.length <= 4) {
|
||||||
|
setVerificationCode(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatTimerValue = () => {
|
||||||
|
const m = String(Math.floor(countdown / 60)).padStart(2, '0');
|
||||||
|
const s = String(countdown % 60).padStart(2, '0');
|
||||||
|
return toLocaleDigits(`${m}:${s}`, i18n.language);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
@@ -51,7 +66,6 @@ export function EmailSection({
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: 1,
|
gap: 1,
|
||||||
px: { xs: 2, sm: 0 },
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Switch checked={showEmail} onChange={handleToggleEmail} />
|
<Switch checked={showEmail} onChange={handleToggleEmail} />
|
||||||
@@ -68,7 +82,6 @@ export function EmailSection({
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
gap: 2,
|
gap: 2,
|
||||||
px: { xs: 2, sm: 0 },
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
@@ -84,14 +97,18 @@ export function EmailSection({
|
|||||||
variant="outlined"
|
variant="outlined"
|
||||||
fullWidth
|
fullWidth
|
||||||
value={email}
|
value={email}
|
||||||
|
autoFocus
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
error={email.length > 0 && !correctEmail}
|
onBlur={() => handleBlur('email')}
|
||||||
|
disabled={isSendingCode || codeSent}
|
||||||
|
error={touched.email && !!errors.email}
|
||||||
|
helperText={touched.email && errors.email}
|
||||||
sx={{ flex: '1 1 260px' }}
|
sx={{ flex: '1 1 260px' }}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
input: {
|
input: {
|
||||||
startAdornment:
|
startAdornment:
|
||||||
!loading && emailVerified ? (
|
!isVerifyingCode && emailVerified ? (
|
||||||
<InputAdornment position="end">
|
<InputAdornment position="start">
|
||||||
<Icon
|
<Icon
|
||||||
Component={TickCircle}
|
Component={TickCircle}
|
||||||
size="medium"
|
size="medium"
|
||||||
@@ -100,49 +117,50 @@ export function EmailSection({
|
|||||||
/>
|
/>
|
||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
) : null,
|
) : null,
|
||||||
endAdornment:
|
endAdornment: codeSent ? (
|
||||||
buttonState === 'counting' ? (
|
<InputAdornment position="end">
|
||||||
<InputAdornment position="start">
|
<IconButton onClick={handleEditEmail}>
|
||||||
<IconButton onClick={handleEditEmail}>
|
<Icon
|
||||||
<Icon
|
Component={Edit2}
|
||||||
Component={Edit}
|
color="primary.main"
|
||||||
color="primary.main"
|
size="medium"
|
||||||
size="medium"
|
/>
|
||||||
/>
|
</IconButton>
|
||||||
</IconButton>
|
</InputAdornment>
|
||||||
</InputAdornment>
|
) : null,
|
||||||
) : null,
|
|
||||||
sx: {
|
|
||||||
paddingLeft: buttonState === 'counting' ? 0 : undefined,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{!loading && !emailVerified && (
|
{!isVerifyingCode &&
|
||||||
<Button
|
!emailVerified &&
|
||||||
type="button"
|
(buttonState === 'counting' ? (
|
||||||
variant="text"
|
<Typography
|
||||||
onClick={onSendCodeClick}
|
sx={{
|
||||||
sx={{
|
minWidth: '140px',
|
||||||
minWidth: '140px',
|
width: { xs: '100%', sm: '156px' },
|
||||||
width: { xs: '100%', sm: '156px' },
|
alignSelf: 'center',
|
||||||
alignSelf: 'center',
|
textAlign: 'center',
|
||||||
textTransform: 'none',
|
}}
|
||||||
}}
|
color="primary"
|
||||||
>
|
variant="body1"
|
||||||
{getButtonLabel()}
|
>
|
||||||
</Button>
|
{formatTimerValue()}
|
||||||
)}
|
</Typography>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
variant="text"
|
||||||
|
onClick={handleSendCode}
|
||||||
|
loading={isSendingCode}
|
||||||
|
sx={{
|
||||||
|
width: { xs: '100%', sm: '156px' },
|
||||||
|
alignSelf: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('completion.vericationCodeButton')}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
{email && (
|
{!emailVerified && codeSent && (
|
||||||
<Typography
|
|
||||||
sx={{ color: correctEmail ? 'success.main' : 'error.main' }}
|
|
||||||
variant="caption"
|
|
||||||
>
|
|
||||||
{correctEmail ? '' : t('completion.emailCorrectForm')}
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
{!emailVerified && codeSent && correctEmail && (
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@@ -153,27 +171,27 @@ export function EmailSection({
|
|||||||
>
|
>
|
||||||
<TextField
|
<TextField
|
||||||
label={t('completion.verificationCode')}
|
label={t('completion.verificationCode')}
|
||||||
|
autoFocus
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
|
type="text"
|
||||||
value={verificationCode}
|
value={verificationCode}
|
||||||
onChange={(e) => setVerificationCode(e.target.value)}
|
onChange={handleVerificationCodeChange}
|
||||||
sx={{ flex: '1 1 260px' }}
|
sx={{ flex: '1 1 260px' }}
|
||||||
disabled={loading}
|
disabled={isVerifyingCode}
|
||||||
|
onBlur={() => handleBlur('verificationCode')}
|
||||||
|
error={touched.verificationCode && !!errors.verificationCode}
|
||||||
|
helperText={touched.verificationCode && errors.verificationCode}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="outlined"
|
||||||
onClick={handleVerifyCode}
|
onClick={handleVerifyCode}
|
||||||
disabled={loading}
|
loading={isVerifyingCode}
|
||||||
sx={{
|
sx={{
|
||||||
width: { xs: '100%', sm: '156px' },
|
width: { xs: '100%', sm: '156px' },
|
||||||
border: 0.5,
|
alignSelf: 'center',
|
||||||
textTransform: 'none',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{loading ? (
|
{t('completion.checkCodeButton')}
|
||||||
<CircularProgress size={20} />
|
|
||||||
) : (
|
|
||||||
t('completion.checkCodeButton')
|
|
||||||
)}
|
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
FormGroup,
|
FormGroup,
|
||||||
Typography,
|
Typography,
|
||||||
InputAdornment,
|
InputAdornment,
|
||||||
|
Grid,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { TickCircle, Eye, EyeSlash, CloseCircle } from 'iconsax-react';
|
import { TickCircle, Eye, EyeSlash, CloseCircle } from 'iconsax-react';
|
||||||
@@ -27,12 +28,15 @@ export function PasswordSection({
|
|||||||
hasUpperAndLower,
|
hasUpperAndLower,
|
||||||
hasSpecialChar,
|
hasSpecialChar,
|
||||||
validPassword,
|
validPassword,
|
||||||
showValidations,
|
errors,
|
||||||
|
touched,
|
||||||
|
handleBlur,
|
||||||
}: PasswordSectionProps) {
|
}: PasswordSectionProps) {
|
||||||
const { t } = useTranslation('completionForm');
|
const { t } = useTranslation('completionForm');
|
||||||
const [showPasswordText, setShowPasswordText] = useState(false);
|
const [showPasswordText, setShowPasswordText] = useState(false);
|
||||||
const [showPasswordRepetitionText, setShowPasswordRepetitionText] =
|
const [showPasswordRepetitionText, setShowPasswordRepetitionText] =
|
||||||
useState(false);
|
useState(false);
|
||||||
|
const [showValidations, setShowValidation] = useState(false);
|
||||||
|
|
||||||
const handleTogglePasswordSection = (
|
const handleTogglePasswordSection = (
|
||||||
e: React.ChangeEvent<HTMLInputElement>,
|
e: React.ChangeEvent<HTMLInputElement>,
|
||||||
@@ -44,6 +48,11 @@ export function PasswordSection({
|
|||||||
const handleTogglePasswordRepetitionEye = () =>
|
const handleTogglePasswordRepetitionEye = () =>
|
||||||
setShowPasswordRepetitionText((prev) => !prev);
|
setShowPasswordRepetitionText((prev) => !prev);
|
||||||
|
|
||||||
|
const handleBlurPassword = () => {
|
||||||
|
handleBlur('password');
|
||||||
|
setShowValidation(false);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
@@ -51,7 +60,6 @@ export function PasswordSection({
|
|||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
gap: 0.5,
|
gap: 0.5,
|
||||||
px: { xs: 2, sm: -4 },
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -70,45 +78,30 @@ export function PasswordSection({
|
|||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
{showPasswordSection && (
|
{showPasswordSection && (
|
||||||
<Box
|
<Grid container spacing={2}>
|
||||||
sx={{
|
<Grid size={{ xs: 12, md: 6 }}>
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
gap: 2,
|
|
||||||
px: { xs: 2, sm: 0 },
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: 'flex',
|
|
||||||
flexWrap: 'wrap',
|
|
||||||
gap: 2,
|
|
||||||
justifyContent: { xs: 'center', sm: 'flex-start' },
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TextField
|
<TextField
|
||||||
|
fullWidth
|
||||||
label={t('completion.password')}
|
label={t('completion.password')}
|
||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
type={showPasswordText ? 'text' : 'password'}
|
type={showPasswordText ? 'text' : 'password'}
|
||||||
|
autoFocus
|
||||||
|
onBlur={handleBlurPassword}
|
||||||
|
onFocus={() => setShowValidation(true)}
|
||||||
|
error={touched.password && !!errors.password}
|
||||||
|
helperText={touched.password && errors.password}
|
||||||
sx={{
|
sx={{
|
||||||
flex: '1 1 260px',
|
flex: '1 1 260px',
|
||||||
'& .MuiInputBase-input': {
|
'& .MuiInputBase-input': {
|
||||||
pr: 8, // Increased padding to accommodate both icons
|
pr: 8, // Increased padding to accommodate both icons
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
InputProps={{
|
slotProps={{
|
||||||
endAdornment: (
|
input: {
|
||||||
<InputAdornment position="end">
|
endAdornment: (
|
||||||
<Box
|
<InputAdornment position="end">
|
||||||
sx={{
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'flex-end',
|
|
||||||
minWidth: '64px', // Adjusted to fit both icons
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={handleTogglePasswordEye}
|
onClick={handleTogglePasswordEye}
|
||||||
sx={{ p: 0.5 }}
|
sx={{ p: 0.5 }}
|
||||||
@@ -127,46 +120,40 @@ export function PasswordSection({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
{validPassword && (
|
</InputAdornment>
|
||||||
<Icon
|
),
|
||||||
Component={TickCircle}
|
startAdornment: validPassword && (
|
||||||
size="medium"
|
<InputAdornment position="start">
|
||||||
color="success.main"
|
<Icon
|
||||||
variant="Bold"
|
Component={TickCircle}
|
||||||
/>
|
size="medium"
|
||||||
)}
|
color="success.main"
|
||||||
</Box>
|
variant="Bold"
|
||||||
</InputAdornment>
|
/>
|
||||||
),
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid size={{ xs: 12, md: 6 }}>
|
||||||
<TextField
|
<TextField
|
||||||
|
fullWidth
|
||||||
label={t('completion.passwordRepetition')}
|
label={t('completion.passwordRepetition')}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={confirmPassword}
|
value={confirmPassword}
|
||||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||||
error={confirmPassword.length > 0 && !matchPassword}
|
onBlur={() => handleBlur('confirmPassword')}
|
||||||
helperText={
|
error={touched.confirmPassword && !!errors.confirmPassword}
|
||||||
confirmPassword.length > 0 && !matchPassword
|
helperText={touched.confirmPassword && errors.confirmPassword}
|
||||||
? t('completion.notCompatibility')
|
|
||||||
: ' '
|
|
||||||
}
|
|
||||||
type={showPasswordRepetitionText ? 'text' : 'password'}
|
type={showPasswordRepetitionText ? 'text' : 'password'}
|
||||||
sx={{
|
sx={{
|
||||||
flex: '1 1 260px',
|
flex: '1 1',
|
||||||
}}
|
}}
|
||||||
InputProps={{
|
slotProps={{
|
||||||
endAdornment: (
|
input: {
|
||||||
<InputAdornment position="end">
|
endAdornment: (
|
||||||
<Box
|
<InputAdornment position="end">
|
||||||
sx={{
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'flex-end',
|
|
||||||
minWidth: '64px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={handleTogglePasswordRepetitionEye}
|
onClick={handleTogglePasswordRepetitionEye}
|
||||||
sx={{ p: 0.5 }}
|
sx={{ p: 0.5 }}
|
||||||
@@ -185,65 +172,55 @@ export function PasswordSection({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
{confirmPassword.length > 0 && (
|
</InputAdornment>
|
||||||
<Icon
|
),
|
||||||
Component={matchPassword ? TickCircle : CloseCircle}
|
startAdornment: confirmPassword.length > 0 && (
|
||||||
size="medium"
|
<InputAdornment position="start">
|
||||||
color={matchPassword ? 'success.main' : 'error.main'}
|
<Icon
|
||||||
variant="Bold"
|
Component={matchPassword ? TickCircle : CloseCircle}
|
||||||
/>
|
size="medium"
|
||||||
)}
|
color={matchPassword ? 'success.main' : 'error.main'}
|
||||||
</Box>
|
variant="Bold"
|
||||||
</InputAdornment>
|
/>
|
||||||
),
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Grid>
|
||||||
|
|
||||||
{password && showValidations && (
|
{showValidations && (
|
||||||
<Box
|
<>
|
||||||
sx={{
|
<Grid size={{ xs: 12, md: 6 }}>
|
||||||
display: 'flex',
|
|
||||||
flexWrap: 'wrap',
|
|
||||||
gap: 2,
|
|
||||||
justifyContent: { xs: 'center', sm: 'flex-start' },
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
flex: '1 1 260px',
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PasswordValidationItem
|
<PasswordValidationItem
|
||||||
isValid={hasNumber}
|
isValid={hasNumber}
|
||||||
label={t('completion.hasNumber')}
|
label={t('completion.hasNumber')}
|
||||||
/>
|
/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid size={{ xs: 12, md: 6 }}>
|
||||||
<PasswordValidationItem
|
<PasswordValidationItem
|
||||||
isValid={hasMinLength}
|
isValid={hasMinLength}
|
||||||
label={t('completion.hasMinLength')}
|
label={t('completion.hasMinLength')}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Grid>
|
||||||
<Box
|
|
||||||
sx={{
|
<Grid size={{ xs: 12, md: 6 }}>
|
||||||
flex: '1 1 260px',
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PasswordValidationItem
|
<PasswordValidationItem
|
||||||
isValid={hasUpperAndLower}
|
isValid={hasUpperAndLower}
|
||||||
label={t('completion.hasUpperAndLower')}
|
label={t('completion.hasUpperAndLower')}
|
||||||
/>
|
/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid size={{ xs: 12, md: 6 }}>
|
||||||
<PasswordValidationItem
|
<PasswordValidationItem
|
||||||
isValid={hasSpecialChar}
|
isValid={hasSpecialChar}
|
||||||
label={t('completion.hasSpecialChar')}
|
label={t('completion.hasSpecialChar')}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Grid>
|
||||||
</Box>
|
</>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Grid>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import {
|
|||||||
Select,
|
Select,
|
||||||
Box,
|
Box,
|
||||||
Autocomplete,
|
Autocomplete,
|
||||||
|
Grid,
|
||||||
type SelectChangeEvent,
|
type SelectChangeEvent,
|
||||||
|
FormHelperText,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Woman, Man } from 'iconsax-react';
|
import { Woman, Man } from 'iconsax-react';
|
||||||
@@ -30,12 +32,15 @@ export function PersonalInfoFields({
|
|||||||
setSex,
|
setSex,
|
||||||
country,
|
country,
|
||||||
setCountry,
|
setCountry,
|
||||||
|
errors,
|
||||||
|
touched,
|
||||||
|
handleBlur,
|
||||||
}: PersonalInfoFieldsProps) {
|
}: PersonalInfoFieldsProps) {
|
||||||
const { t } = useTranslation('completionForm');
|
const { t } = useTranslation(['completionForm', 'country']);
|
||||||
|
|
||||||
const countryOptions = countries.map((c) => ({
|
const countryOptions = countries.map((c) => ({
|
||||||
code: c.code,
|
code: c.code,
|
||||||
label: t(c.label, { ns: 'countries' }),
|
label: t(c.label, { ns: 'country' }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const currentCountry = countryOptions.find((c) => c.code === country) || null;
|
const currentCountry = countryOptions.find((c) => c.code === country) || null;
|
||||||
@@ -60,119 +65,115 @@ export function PersonalInfoFields({
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'center' }}>
|
<Grid container spacing={2}>
|
||||||
<Box
|
<Grid size={{ xs: 12, md: 6 }}>
|
||||||
sx={{
|
<TextField
|
||||||
display: 'flex',
|
autoFocus
|
||||||
flexDirection: 'column',
|
fullWidth
|
||||||
gap: 2,
|
label={t('completion.name')}
|
||||||
width: '100%',
|
placeholder={t('completion.name')}
|
||||||
px: { xs: 2, sm: 4, md: 1 },
|
variant="outlined"
|
||||||
}}
|
value={firstName}
|
||||||
>
|
onChange={(e) => setFirstName(e.target.value)}
|
||||||
<Box
|
onBlur={() => handleBlur('firstName')}
|
||||||
sx={{
|
error={touched.firstName && !!errors.firstName}
|
||||||
display: 'flex',
|
helperText={touched.firstName && errors.firstName}
|
||||||
flexWrap: 'wrap',
|
/>
|
||||||
gap: 2,
|
</Grid>
|
||||||
justifyContent: { xs: 'center', sm: 'flex-start' },
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TextField
|
|
||||||
label={t('completion.name')}
|
|
||||||
placeholder={t('completion.name')}
|
|
||||||
variant="outlined"
|
|
||||||
value={firstName}
|
|
||||||
onChange={(e) => setFirstName(e.target.value)}
|
|
||||||
sx={{ flex: '1 1 260px' }}
|
|
||||||
/>
|
|
||||||
<TextField
|
|
||||||
label={t('completion.familyName')}
|
|
||||||
placeholder={t('completion.familyName')}
|
|
||||||
variant="outlined"
|
|
||||||
value={lastName}
|
|
||||||
onChange={(e) => setLastName(e.target.value)}
|
|
||||||
sx={{ flex: '1 1 260px' }}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box
|
<Grid size={{ xs: 12, md: 6 }}>
|
||||||
sx={{
|
<TextField
|
||||||
display: 'flex',
|
fullWidth
|
||||||
flexWrap: 'wrap',
|
label={t('completion.familyName')}
|
||||||
gap: 2,
|
placeholder={t('completion.familyName')}
|
||||||
justifyContent: { xs: 'center', sm: 'flex-start' },
|
variant="outlined"
|
||||||
}}
|
value={lastName}
|
||||||
>
|
onChange={(e) => setLastName(e.target.value)}
|
||||||
<FormControl sx={{ flex: '1 1 260px' }}>
|
onBlur={() => handleBlur('lastName')}
|
||||||
<InputLabel>{t('completion.gender')}</InputLabel>
|
error={touched.lastName && !!errors.lastName}
|
||||||
<Select
|
helperText={touched.lastName && errors.lastName}
|
||||||
value={sex}
|
/>
|
||||||
label={t('completion.gender')}
|
</Grid>
|
||||||
onChange={handleChangeSex}
|
|
||||||
>
|
|
||||||
{genderOptions.map((g) => (
|
|
||||||
<MenuItem key={g.value} value={g.value}>
|
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
||||||
<Icon Component={g.icon} size="small" color={g.color} />
|
|
||||||
{g.label}
|
|
||||||
</Box>
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<TextField
|
<Grid size={{ xs: 12, md: 6 }}>
|
||||||
label={t('completion.optionalNationalCode')}
|
<FormControl fullWidth error={touched.sex && !!errors.sex}>
|
||||||
placeholder={t('completion.optionalNationalCode')}
|
<InputLabel>{t('completion.gender')}</InputLabel>
|
||||||
value={nationalId}
|
<Select
|
||||||
onChange={(e) => setNationalId(e.target.value)}
|
value={sex || ''}
|
||||||
variant="outlined"
|
label={t('completion.gender')}
|
||||||
sx={{ flex: '1 1 260px' }}
|
onChange={handleChangeSex}
|
||||||
/>
|
onBlur={() => handleBlur('sex')}
|
||||||
</Box>
|
>
|
||||||
|
{genderOptions.map((g) => (
|
||||||
|
<MenuItem key={g.value} value={g.value}>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
|
<Icon Component={g.icon} size="small" color={g.color} />
|
||||||
|
{g.label}
|
||||||
|
</Box>
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
|
||||||
<Box
|
{touched.sex && errors.sex && (
|
||||||
sx={{
|
<FormHelperText>{errors.sex}</FormHelperText>
|
||||||
display: 'flex',
|
)}
|
||||||
flexWrap: 'wrap',
|
</FormControl>
|
||||||
gap: 2,
|
</Grid>
|
||||||
justifyContent: { xs: 'center', sm: 'flex-start' },
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Autocomplete
|
|
||||||
sx={{ flex: '1 1 260px' }}
|
|
||||||
options={countryOptions}
|
|
||||||
getOptionLabel={(option) => option.label}
|
|
||||||
value={currentCountry}
|
|
||||||
onChange={(_, newValue) => setCountry(newValue?.code || '')}
|
|
||||||
renderOption={(props, option) => (
|
|
||||||
<Box component="li" {...props} key={option.code}>
|
|
||||||
<ReactCountryFlag
|
|
||||||
countryCode={option.code}
|
|
||||||
svg
|
|
||||||
style={{
|
|
||||||
height: '1.5rem',
|
|
||||||
width: '1.5rem',
|
|
||||||
// TODO: Check alignment for better styling definition
|
|
||||||
marginTop: '-2px',
|
|
||||||
marginRight: '4px',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{option.label}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
renderInput={(params) => (
|
|
||||||
<TextField {...params} label={t('completion.country')} />
|
|
||||||
)}
|
|
||||||
clearOnEscape
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Box sx={{ flex: '1 1 260px' }}>
|
<Grid size={{ xs: 12, md: 6 }}>
|
||||||
<DateOfBirth value={birthDate} onChange={setBirthDate} />
|
<Autocomplete
|
||||||
</Box>
|
fullWidth
|
||||||
</Box>
|
options={countryOptions}
|
||||||
</Box>
|
getOptionLabel={(option) => option.label}
|
||||||
</Box>
|
noOptionsText={t('completion.noOption')}
|
||||||
|
value={currentCountry}
|
||||||
|
onChange={(_, newValue) => setCountry(newValue?.code || '')}
|
||||||
|
onBlur={() => handleBlur('country')}
|
||||||
|
renderOption={(props, option) => (
|
||||||
|
<Box component="li" {...props} key={option.code}>
|
||||||
|
<ReactCountryFlag
|
||||||
|
countryCode={option.code}
|
||||||
|
svg
|
||||||
|
style={{
|
||||||
|
height: '1.5rem',
|
||||||
|
width: '1.5rem',
|
||||||
|
marginTop: '-2px',
|
||||||
|
marginRight: '4px',
|
||||||
|
marginLeft: '8px',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{option.label}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
renderInput={(params) => (
|
||||||
|
<TextField
|
||||||
|
{...params}
|
||||||
|
label={t('completion.country')}
|
||||||
|
error={touched.country && !!errors.country}
|
||||||
|
helperText={touched.country && errors.country}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
clearOnEscape
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid size={{ xs: 12, md: 6 }}>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
label={t('completion.optionalNationalCode')}
|
||||||
|
placeholder={t('completion.optionalNationalCode')}
|
||||||
|
value={nationalId}
|
||||||
|
onChange={(e) => setNationalId(e.target.value)}
|
||||||
|
variant="outlined"
|
||||||
|
onBlur={() => handleBlur('nationalId')}
|
||||||
|
error={touched.nationalId && !!errors.nationalId}
|
||||||
|
helperText={touched.nationalId && errors.nationalId}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid size={{ xs: 12, md: 6 }}>
|
||||||
|
<DateOfBirth value={birthDate} onChange={setBirthDate} />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { type SubmitProps } from '../../types/settingForm';
|
import { type SubmitProps } from '../../types/settingForm';
|
||||||
|
|
||||||
export function SubmitSection({ onSubmit, loading, error }: SubmitProps) {
|
export function SubmitSection({ onSubmit, loading }: SubmitProps) {
|
||||||
const { t, i18n } = useTranslation('completionForm');
|
const { t, i18n } = useTranslation('completionForm');
|
||||||
const [openDialog, setOpenDialog] = useState(false);
|
const [openDialog, setOpenDialog] = useState(false);
|
||||||
|
|
||||||
@@ -28,9 +28,8 @@ export function SubmitSection({ onSubmit, loading, error }: SubmitProps) {
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexWrap: 'wrap',
|
flexWrap: 'wrap',
|
||||||
gap: 2,
|
gap: 2,
|
||||||
px: { xs: 2, sm: 0 },
|
|
||||||
mb: 2,
|
mb: 2,
|
||||||
justifyContent: { xs: 'center', sm: 'flex-start' },
|
justifyContent: 'flex-start',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
@@ -63,7 +62,6 @@ export function SubmitSection({ onSubmit, loading, error }: SubmitProps) {
|
|||||||
? t('completion.submitting')
|
? t('completion.submitting')
|
||||||
: t('completion.registerButton')}
|
: t('completion.registerButton')}
|
||||||
</Button>
|
</Button>
|
||||||
{error && <Typography color="error">{error}</Typography>}
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ import { PasswordSection } from '../components/UserCompletionForm/PasswordSectio
|
|||||||
import { EmailSection } from '../components/UserCompletionForm/EmailSection';
|
import { EmailSection } from '../components/UserCompletionForm/EmailSection';
|
||||||
import { SubmitSection } from '../components/UserCompletionForm/SubmitSection';
|
import { SubmitSection } from '../components/UserCompletionForm/SubmitSection';
|
||||||
import { useToast } from '@rkheftan/harmony-ui';
|
import { useToast } from '@rkheftan/harmony-ui';
|
||||||
import { toLocaleDigits } from '../../../utils/persianDigit';
|
|
||||||
import i18n from '@/config/i18n';
|
|
||||||
import { Gender } from '../types/settingForm';
|
import { Gender } from '../types/settingForm';
|
||||||
import { useApi } from '@/hooks/useApi';
|
import { useApi } from '@/hooks/useApi';
|
||||||
import {
|
import {
|
||||||
@@ -22,6 +20,9 @@ import { containsNumber } from '@/utils/regexes/containsNumber';
|
|||||||
import { hasUpperAndLowerLetter } from '@/utils/regexes/hasUpperAndLowerLetter';
|
import { hasUpperAndLowerLetter } from '@/utils/regexes/hasUpperAndLowerLetter';
|
||||||
import { isEmail } from '@/utils/regexes/isEmail';
|
import { isEmail } from '@/utils/regexes/isEmail';
|
||||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||||
|
import { FlexBox } from '@/components/common/FlexBox';
|
||||||
|
import { AuthenticationCard } from '../components/AuthenticationCard';
|
||||||
|
import { nationalIdRegex } from '@/utils/regexes/nationalId';
|
||||||
|
|
||||||
export function UserCompletionPage() {
|
export function UserCompletionPage() {
|
||||||
const [params] = useSearchParams();
|
const [params] = useSearchParams();
|
||||||
@@ -33,7 +34,7 @@ export function UserCompletionPage() {
|
|||||||
const [lastName, setLastName] = useState('');
|
const [lastName, setLastName] = useState('');
|
||||||
const [nationalId, setNationalId] = useState('');
|
const [nationalId, setNationalId] = useState('');
|
||||||
const [birthDate, setBirthDate] = useState<Date | null>(null);
|
const [birthDate, setBirthDate] = useState<Date | null>(null);
|
||||||
const [sex, setSex] = useState<Gender>(Gender.Female);
|
const [sex, setSex] = useState<Gender | null>(null);
|
||||||
const [country, setCountry] = useState('');
|
const [country, setCountry] = useState('');
|
||||||
const [showPasswordSection, setShowPasswordSection] = useState(false);
|
const [showPasswordSection, setShowPasswordSection] = useState(false);
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
@@ -48,7 +49,9 @@ export function UserCompletionPage() {
|
|||||||
);
|
);
|
||||||
const [countdown, setCountdown] = useState(0);
|
const [countdown, setCountdown] = useState(0);
|
||||||
const [emailVerified, setEmailVerified] = useState(false);
|
const [emailVerified, setEmailVerified] = useState(false);
|
||||||
const [showPasswordValidations, setShowPasswordValidations] = useState(false);
|
|
||||||
|
const [errors, setErrors] = useState<{ [key: string]: string }>({});
|
||||||
|
const [touched, setTouched] = useState<{ [key: string]: boolean }>({});
|
||||||
|
|
||||||
const hasNumber = containsNumber(password);
|
const hasNumber = containsNumber(password);
|
||||||
const hasMinLength = least8Chars(password);
|
const hasMinLength = least8Chars(password);
|
||||||
@@ -60,98 +63,14 @@ export function UserCompletionPage() {
|
|||||||
const matchPassword = password === confirmPassword;
|
const matchPassword = password === confirmPassword;
|
||||||
const correctEmail = isEmail(email);
|
const correctEmail = isEmail(email);
|
||||||
|
|
||||||
const { execute: sendCode, data: sendCodeData } = useApi(sendEmailOtpApi);
|
const { execute: sendCode, loading: isSendingCode } = useApi(sendEmailOtpApi);
|
||||||
|
|
||||||
const {
|
const { execute: verifyCode, loading: isVerifyingCode } =
|
||||||
execute: verifyCode,
|
useApi(confirmEmailOtpApi);
|
||||||
data: verifyCodeData,
|
|
||||||
loading: isVerifyingCode,
|
|
||||||
} = useApi(confirmEmailOtpApi);
|
|
||||||
|
|
||||||
const {
|
const { execute: submitForm, loading: isSubmitting } = useApi(
|
||||||
execute: submitForm,
|
completeUserInformationApi,
|
||||||
data: submitData,
|
);
|
||||||
loading: isSubmitting,
|
|
||||||
error: submitError,
|
|
||||||
} = useApi(completeUserInformationApi);
|
|
||||||
|
|
||||||
const getErrorMessage = (error: unknown): string | null => {
|
|
||||||
if (!error) return null;
|
|
||||||
if (error instanceof Error) return error.message;
|
|
||||||
return String(error);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (sendCodeData) {
|
|
||||||
if (sendCodeData.data.success) {
|
|
||||||
showToast({
|
|
||||||
message:
|
|
||||||
sendCodeData.data.message || t('completion.successfullCodeSent'),
|
|
||||||
severity: 'success',
|
|
||||||
});
|
|
||||||
setCodeSent(true);
|
|
||||||
setButtonState('counting');
|
|
||||||
setCountdown(120);
|
|
||||||
} else {
|
|
||||||
showToast({
|
|
||||||
message: sendCodeData.data.message || t('completion.problem'),
|
|
||||||
severity: 'error',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [sendCodeData, showToast, t]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (verifyCodeData) {
|
|
||||||
if (verifyCodeData.data.success) {
|
|
||||||
setEmailVerified(true);
|
|
||||||
showToast({
|
|
||||||
message: verifyCodeData.data.message || t('completion.codeVerified'),
|
|
||||||
severity: 'success',
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
showToast({
|
|
||||||
message: verifyCodeData.data.message || t('completion.invalidCode'),
|
|
||||||
severity: 'error',
|
|
||||||
});
|
|
||||||
setEmailVerified(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [verifyCodeData, showToast, t]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (submitData) {
|
|
||||||
showToast({
|
|
||||||
message:
|
|
||||||
submitData.data.message ||
|
|
||||||
t(
|
|
||||||
submitData.data.success
|
|
||||||
? 'completion.submitSuccess'
|
|
||||||
: 'completion.submitError',
|
|
||||||
),
|
|
||||||
severity: submitData.data.success ? 'success' : 'error',
|
|
||||||
});
|
|
||||||
|
|
||||||
if (submitData.data.success) {
|
|
||||||
const returnUrl = params.get('returnUrl');
|
|
||||||
|
|
||||||
navigate(
|
|
||||||
returnUrl
|
|
||||||
? `/account-created?returnUrl=${returnUrl}`
|
|
||||||
: '`/account-created',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if (submitError) {
|
|
||||||
showToast({
|
|
||||||
message: getErrorMessage(submitError) || t('completion.problem'),
|
|
||||||
severity: 'error',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [submitData, submitError, showToast, t, navigate, params]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setShowPasswordValidations(password ? !validPassword : false);
|
|
||||||
}, [password, validPassword]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let timer: NodeJS.Timeout;
|
let timer: NodeJS.Timeout;
|
||||||
@@ -170,26 +89,105 @@ export function UserCompletionPage() {
|
|||||||
return () => clearInterval(timer);
|
return () => clearInterval(timer);
|
||||||
}, [buttonState, countdown]);
|
}, [buttonState, countdown]);
|
||||||
|
|
||||||
const handleSendCode = () => {
|
const handleSendCode = async () => {
|
||||||
sendCode({ email });
|
if (!isEmail(email)) {
|
||||||
};
|
setTouched((prev) => ({ ...prev, email: true }));
|
||||||
|
setErrors((prev) => ({ ...prev, email: t('validation.emailInvalid') }));
|
||||||
const handleVerifyCode = () => {
|
|
||||||
if (!verificationCode.trim()) {
|
|
||||||
showToast({
|
|
||||||
message: 'Please enter the verification code',
|
|
||||||
severity: 'warning',
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
verifyCode({ email, otpCode: verificationCode });
|
|
||||||
|
setTouched((prev) => {
|
||||||
|
const newTouched = { ...prev };
|
||||||
|
delete newTouched.verificationCode;
|
||||||
|
return newTouched;
|
||||||
|
});
|
||||||
|
setErrors((prev) => {
|
||||||
|
const newErrors = { ...prev };
|
||||||
|
delete newErrors.verificationCode;
|
||||||
|
return newErrors;
|
||||||
|
});
|
||||||
|
|
||||||
|
const res = await sendCode({ email });
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
if (res.success) {
|
||||||
|
showToast({
|
||||||
|
message: res.message || t('completion.successfulCodeSent'),
|
||||||
|
severity: 'success',
|
||||||
|
});
|
||||||
|
setCodeSent(true);
|
||||||
|
setButtonState('counting');
|
||||||
|
setCountdown(120);
|
||||||
|
} else {
|
||||||
|
showToast({
|
||||||
|
message: res.message || t('completion.problem'),
|
||||||
|
severity: 'error',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleVerifyCode = async () => {
|
||||||
submitForm({
|
const trimmedCode = verificationCode.trim();
|
||||||
|
|
||||||
|
// Manually trigger the validation error and stop
|
||||||
|
if (!trimmedCode) {
|
||||||
|
setTouched((prev) => ({ ...prev, verificationCode: true }));
|
||||||
|
setErrors((prev) => ({
|
||||||
|
...prev,
|
||||||
|
verificationCode: t('validation.verificationCodeRequired'),
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
} else if (trimmedCode.length < 4) {
|
||||||
|
setTouched((prev) => ({ ...prev, verificationCode: true }));
|
||||||
|
setErrors((prev) => ({
|
||||||
|
...prev,
|
||||||
|
verificationCode: t('validation.verificationCodeInvalid'),
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await verifyCode({ email, otpCode: verificationCode });
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
if (res.success) {
|
||||||
|
setEmailVerified(true);
|
||||||
|
showToast({
|
||||||
|
message: res.message || t('completion.codeVerified'),
|
||||||
|
severity: 'success',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
showToast({
|
||||||
|
message: res.message || t('completion.invalidCode'),
|
||||||
|
severity: 'error',
|
||||||
|
});
|
||||||
|
setEmailVerified(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
setTouched({
|
||||||
|
firstName: true,
|
||||||
|
lastName: true,
|
||||||
|
country: true,
|
||||||
|
email: showEmail,
|
||||||
|
nationalId: true,
|
||||||
|
verificationCode: showEmail,
|
||||||
|
password: showPasswordSection,
|
||||||
|
confirmPassword: showPasswordSection,
|
||||||
|
sex: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const isValid = validateForm();
|
||||||
|
if (!isValid) {
|
||||||
|
return; // Stop the submission
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await submitForm({
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
gender: sex,
|
gender: sex || 0,
|
||||||
nationalId,
|
nationalId,
|
||||||
savePassword: showPasswordSection,
|
savePassword: showPasswordSection,
|
||||||
password: showPasswordSection ? password : undefined,
|
password: showPasswordSection ? password : undefined,
|
||||||
@@ -198,15 +196,26 @@ export function UserCompletionPage() {
|
|||||||
birthDate,
|
birthDate,
|
||||||
country,
|
country,
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const getButtonLabel = () => {
|
if (res) {
|
||||||
if (buttonState === 'counting') {
|
if (res.success) {
|
||||||
const m = String(Math.floor(countdown / 60)).padStart(2, '0');
|
showToast({
|
||||||
const s = String(countdown % 60).padStart(2, '0');
|
message: res.message || t('completion.submitSuccess'),
|
||||||
return toLocaleDigits(`${m}:${s}`, i18n.language);
|
severity: 'success',
|
||||||
|
});
|
||||||
|
|
||||||
|
const returnUrl = params.get('returnUrl');
|
||||||
|
|
||||||
|
navigate(
|
||||||
|
returnUrl ? `/account-created?returnUrl=${returnUrl}` : '/setting',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
showToast({
|
||||||
|
message: res.message || t('completion.submitError'),
|
||||||
|
severity: 'error',
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return t('completion.vericationCodeButton');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEditEmail = () => {
|
const handleEditEmail = () => {
|
||||||
@@ -214,101 +223,248 @@ export function UserCompletionPage() {
|
|||||||
setCodeSent(false);
|
setCodeSent(false);
|
||||||
setEmailVerified(false);
|
setEmailVerified(false);
|
||||||
setVerificationCode('');
|
setVerificationCode('');
|
||||||
|
// We clear both touched and errors
|
||||||
|
setTouched((prev) => {
|
||||||
|
const newTouched = { ...prev };
|
||||||
|
delete newTouched.email;
|
||||||
|
delete newTouched.verificationCode;
|
||||||
|
return newTouched;
|
||||||
|
});
|
||||||
|
setErrors((prev) => {
|
||||||
|
const newErrors = { ...prev };
|
||||||
|
delete newErrors.email;
|
||||||
|
delete newErrors.verificationCode;
|
||||||
|
return newErrors;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const validateForm = () => {
|
||||||
|
const newErrors: { [key: string]: string } = {};
|
||||||
|
|
||||||
|
// Rule 1: First Name is required
|
||||||
|
if (!firstName.trim())
|
||||||
|
newErrors.firstName = t('validation.firstNameRequired');
|
||||||
|
|
||||||
|
// Rule 2: Last Name is required
|
||||||
|
if (!lastName.trim()) newErrors.lastName = t('validation.lastNameRequired');
|
||||||
|
|
||||||
|
// Rule 3: Country is required
|
||||||
|
if (!country) newErrors.country = t('validation.countryRequired');
|
||||||
|
|
||||||
|
// Rule 4: Email is required and must be valid IF the section is shown
|
||||||
|
if (showEmail && !isEmail(email)) {
|
||||||
|
newErrors.email = t('validation.emailInvalid');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rule 5: National ID must be 10 digits IF it's not empty
|
||||||
|
if (nationalId && !nationalIdRegex.test(nationalId)) {
|
||||||
|
newErrors.nationalId = t('validation.nationalIdInvalid');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rule 6: If verification code sent and email section is active
|
||||||
|
if (showEmail && codeSent && !emailVerified) {
|
||||||
|
const trimmedCode = verificationCode.trim();
|
||||||
|
|
||||||
|
if (!trimmedCode) {
|
||||||
|
// Case 1: The code is required but the field is empty.
|
||||||
|
newErrors.verificationCode = t('validation.verificationCodeRequired');
|
||||||
|
} else if (trimmedCode.length < 4) {
|
||||||
|
// Case 2 : The code is entered but is less than 4 digits.
|
||||||
|
newErrors.verificationCode = t('validation.verificationCodeInvalid');
|
||||||
|
} else {
|
||||||
|
// Case 3: The user has typed a code but hasn't clicked "Verify" yet.
|
||||||
|
newErrors.verificationCode = t('validation.mustVerifyCode');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rule 7: Password validation
|
||||||
|
if (showPasswordSection) {
|
||||||
|
// Rule 1: Check if the main password is valid
|
||||||
|
if (!password.trim()) {
|
||||||
|
newErrors.password = t('validation.passwordRequired');
|
||||||
|
} else if (!validPassword) {
|
||||||
|
// 'validPassword' is the boolean you already calculate
|
||||||
|
newErrors.password = t('validation.passwordInvalid');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rule 2: Check if the confirmation password matches
|
||||||
|
if (!confirmPassword.trim()) {
|
||||||
|
newErrors.confirmPassword = t('validation.confirmPasswordRequired');
|
||||||
|
} else if (!matchPassword) {
|
||||||
|
// 'matchPassword' is the boolean you already calculate
|
||||||
|
newErrors.confirmPassword = t('validation.passwordsDoNotMatch');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sex === null) {
|
||||||
|
newErrors.sex = t('validation.genderRequired');
|
||||||
|
}
|
||||||
|
|
||||||
|
setErrors(newErrors);
|
||||||
|
return Object.keys(newErrors).length === 0; // Returns true if form is valid
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBlur = (field: string) => {
|
||||||
|
setTouched((prev) => ({ ...prev, [field]: true }));
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!showPasswordSection) {
|
||||||
|
// We clear both touched and errors to prevent lingering validation messages
|
||||||
|
setTouched((prev) => {
|
||||||
|
const newTouched = { ...prev };
|
||||||
|
delete newTouched.password;
|
||||||
|
delete newTouched.confirmPassword;
|
||||||
|
return newTouched;
|
||||||
|
});
|
||||||
|
setErrors((prev) => {
|
||||||
|
const newErrors = { ...prev };
|
||||||
|
delete newErrors.password;
|
||||||
|
delete newErrors.confirmPassword;
|
||||||
|
return newErrors;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [showPasswordSection]);
|
||||||
|
|
||||||
|
// This effect resets email fields when the section is hidden
|
||||||
|
useEffect(() => {
|
||||||
|
if (!showEmail) {
|
||||||
|
// We clear both touched and errors
|
||||||
|
setTouched((prev) => {
|
||||||
|
const newTouched = { ...prev };
|
||||||
|
delete newTouched.email;
|
||||||
|
delete newTouched.verificationCode;
|
||||||
|
return newTouched;
|
||||||
|
});
|
||||||
|
setErrors((prev) => {
|
||||||
|
const newErrors = { ...prev };
|
||||||
|
delete newErrors.email;
|
||||||
|
delete newErrors.verificationCode;
|
||||||
|
return newErrors;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [showEmail]);
|
||||||
|
|
||||||
|
// re-validate whenever a field the user has touched changes value
|
||||||
|
useEffect(() => {
|
||||||
|
// Only run validation if at least one field has been touched
|
||||||
|
if (Object.keys(touched).length > 0) {
|
||||||
|
validateForm();
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
country,
|
||||||
|
email,
|
||||||
|
nationalId,
|
||||||
|
verificationCode,
|
||||||
|
codeSent,
|
||||||
|
emailVerified,
|
||||||
|
password,
|
||||||
|
confirmPassword,
|
||||||
|
showPasswordSection,
|
||||||
|
sex,
|
||||||
|
touched,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<FlexBox
|
||||||
|
direction="column"
|
||||||
|
align="center"
|
||||||
|
justify="center"
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: 'background.default',
|
|
||||||
minHeight: '100vh',
|
minHeight: '100vh',
|
||||||
display: 'flex',
|
gap: 3,
|
||||||
flexDirection: 'column',
|
py: { md: 0, xs: 4 },
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
p: { xs: 1, sm: 2, md: 3 },
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ mb: 2 }}>
|
<Logo />
|
||||||
<Logo />
|
<AuthenticationCard
|
||||||
</Box>
|
|
||||||
<Box
|
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
maxHeight: { sm: '80vh', xs: 'unset' },
|
||||||
maxWidth: 730,
|
flex: { sm: 'unset', xs: 1 },
|
||||||
backgroundColor: 'background.paper',
|
overflowY: 'auto',
|
||||||
border: '1px solid #ccc',
|
scrollbarGutter: 'stable both-edges',
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
gap: 2,
|
|
||||||
px: { xs: 2, sm: 4, md: 6 },
|
|
||||||
py: { xs: 3, sm: 4 },
|
|
||||||
}}
|
}}
|
||||||
|
maxWidth="730px"
|
||||||
>
|
>
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
|
<Box
|
||||||
<Typography variant="h5" color="text.primary">
|
sx={{
|
||||||
{t('completion.title')}
|
display: 'flex',
|
||||||
</Typography>
|
flexDirection: 'column',
|
||||||
<Typography variant="body2" color="text.secondary">
|
gap: 2,
|
||||||
{t('completion.description')}
|
}}
|
||||||
</Typography>
|
>
|
||||||
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
|
||||||
|
<Typography variant="h5" color="text.primary">
|
||||||
|
{t('completion.title')}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" color="text.secondary">
|
||||||
|
{t('completion.description')}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<PersonalInfoFields
|
||||||
|
firstName={firstName}
|
||||||
|
setFirstName={setFirstName}
|
||||||
|
lastName={lastName}
|
||||||
|
setLastName={setLastName}
|
||||||
|
nationalId={nationalId}
|
||||||
|
setNationalId={setNationalId}
|
||||||
|
birthDate={birthDate}
|
||||||
|
setBirthDate={setBirthDate}
|
||||||
|
sex={sex}
|
||||||
|
setSex={setSex}
|
||||||
|
country={country}
|
||||||
|
setCountry={setCountry}
|
||||||
|
errors={errors}
|
||||||
|
touched={touched}
|
||||||
|
handleBlur={handleBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<PasswordSection
|
||||||
|
showPasswordSection={showPasswordSection}
|
||||||
|
setShowPasswordSection={setShowPasswordSection}
|
||||||
|
password={password}
|
||||||
|
setPassword={setPassword}
|
||||||
|
confirmPassword={confirmPassword}
|
||||||
|
setConfirmPassword={setConfirmPassword}
|
||||||
|
matchPassword={matchPassword}
|
||||||
|
hasNumber={hasNumber}
|
||||||
|
hasMinLength={hasMinLength}
|
||||||
|
hasUpperAndLower={hasUpperAndLower}
|
||||||
|
hasSpecialChar={hasSpecialChar}
|
||||||
|
validPassword={validPassword}
|
||||||
|
errors={errors}
|
||||||
|
touched={touched}
|
||||||
|
handleBlur={handleBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<EmailSection
|
||||||
|
showEmail={showEmail}
|
||||||
|
setShowEmail={setShowEmail}
|
||||||
|
email={email}
|
||||||
|
setEmail={setEmail}
|
||||||
|
correctEmail={correctEmail}
|
||||||
|
codeSent={codeSent}
|
||||||
|
verificationCode={verificationCode}
|
||||||
|
setVerificationCode={setVerificationCode}
|
||||||
|
buttonState={buttonState}
|
||||||
|
countdown={countdown}
|
||||||
|
handleSendCode={handleSendCode}
|
||||||
|
handleVerifyCode={handleVerifyCode}
|
||||||
|
emailVerified={emailVerified}
|
||||||
|
isVerifyingCode={isVerifyingCode}
|
||||||
|
isSendingCode={isSendingCode}
|
||||||
|
handleEditEmail={handleEditEmail}
|
||||||
|
errors={errors}
|
||||||
|
touched={touched}
|
||||||
|
handleBlur={handleBlur}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SubmitSection onSubmit={handleSubmit} loading={isSubmitting} />
|
||||||
</Box>
|
</Box>
|
||||||
|
</AuthenticationCard>
|
||||||
<PersonalInfoFields
|
</FlexBox>
|
||||||
firstName={firstName}
|
|
||||||
setFirstName={setFirstName}
|
|
||||||
lastName={lastName}
|
|
||||||
setLastName={setLastName}
|
|
||||||
nationalId={nationalId}
|
|
||||||
setNationalId={setNationalId}
|
|
||||||
birthDate={birthDate}
|
|
||||||
setBirthDate={setBirthDate}
|
|
||||||
sex={sex}
|
|
||||||
setSex={setSex}
|
|
||||||
country={country}
|
|
||||||
setCountry={setCountry}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<PasswordSection
|
|
||||||
showPasswordSection={showPasswordSection}
|
|
||||||
setShowPasswordSection={setShowPasswordSection}
|
|
||||||
password={password}
|
|
||||||
setPassword={setPassword}
|
|
||||||
confirmPassword={confirmPassword}
|
|
||||||
setConfirmPassword={setConfirmPassword}
|
|
||||||
matchPassword={matchPassword}
|
|
||||||
hasNumber={hasNumber}
|
|
||||||
hasMinLength={hasMinLength}
|
|
||||||
hasUpperAndLower={hasUpperAndLower}
|
|
||||||
hasSpecialChar={hasSpecialChar}
|
|
||||||
validPassword={validPassword}
|
|
||||||
showValidations={showPasswordValidations}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<EmailSection
|
|
||||||
showEmail={showEmail}
|
|
||||||
setShowEmail={setShowEmail}
|
|
||||||
email={email}
|
|
||||||
setEmail={setEmail}
|
|
||||||
correctEmail={correctEmail}
|
|
||||||
codeSent={codeSent}
|
|
||||||
verificationCode={verificationCode}
|
|
||||||
setVerificationCode={setVerificationCode}
|
|
||||||
buttonState={buttonState}
|
|
||||||
getButtonLabel={getButtonLabel}
|
|
||||||
handleSendCode={handleSendCode}
|
|
||||||
handleVerifyCode={handleVerifyCode}
|
|
||||||
emailVerified={emailVerified}
|
|
||||||
loading={isVerifyingCode}
|
|
||||||
handleEditEmail={handleEditEmail}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SubmitSection
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
loading={isSubmitting}
|
|
||||||
error={getErrorMessage(submitError)}
|
|
||||||
success={!!submitData?.data.success}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export interface ConfirmEmailOtpPayload {
|
|||||||
export interface CompleteUserInfoPayload {
|
export interface CompleteUserInfoPayload {
|
||||||
firstName: string;
|
firstName: string;
|
||||||
lastName: string;
|
lastName: string;
|
||||||
|
// TODO: fix this
|
||||||
gender: 0 | 1 | 2;
|
gender: 0 | 1 | 2;
|
||||||
nationalId: string;
|
nationalId: string;
|
||||||
birthDate: Date | null;
|
birthDate: Date | null;
|
||||||
|
|||||||
@@ -6,12 +6,18 @@ export enum Gender {
|
|||||||
Male = 2,
|
Male = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ValidationProps {
|
||||||
|
errors: { [key: string]: string };
|
||||||
|
touched: { [key: string]: boolean };
|
||||||
|
handleBlur: (field: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
export interface DateOfBirthProps {
|
export interface DateOfBirthProps {
|
||||||
value: Date | null;
|
value: Date | null;
|
||||||
onChange: (date: Date | null) => void;
|
onChange: (date: Date | null) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EmailSectionProps {
|
export interface EmailSectionProps extends ValidationProps {
|
||||||
showEmail: boolean;
|
showEmail: boolean;
|
||||||
setShowEmail: (checked: boolean) => void;
|
setShowEmail: (checked: boolean) => void;
|
||||||
email: string;
|
email: string;
|
||||||
@@ -21,15 +27,16 @@ export interface EmailSectionProps {
|
|||||||
verificationCode: string;
|
verificationCode: string;
|
||||||
setVerificationCode: (code: string) => void;
|
setVerificationCode: (code: string) => void;
|
||||||
buttonState: 'default' | 'counting' | 'sent';
|
buttonState: 'default' | 'counting' | 'sent';
|
||||||
getButtonLabel: () => string;
|
countdown: number;
|
||||||
handleSendCode: () => void;
|
handleSendCode: () => void;
|
||||||
handleVerifyCode: () => void;
|
handleVerifyCode: () => void;
|
||||||
emailVerified: boolean;
|
emailVerified: boolean;
|
||||||
loading: boolean;
|
isVerifyingCode: boolean;
|
||||||
|
isSendingCode: boolean;
|
||||||
handleEditEmail: () => void;
|
handleEditEmail: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PasswordSectionProps {
|
export interface PasswordSectionProps extends ValidationProps {
|
||||||
showPasswordSection: boolean;
|
showPasswordSection: boolean;
|
||||||
setShowPasswordSection: (checked: boolean) => void;
|
setShowPasswordSection: (checked: boolean) => void;
|
||||||
password: string;
|
password: string;
|
||||||
@@ -42,7 +49,6 @@ export interface PasswordSectionProps {
|
|||||||
hasUpperAndLower: boolean;
|
hasUpperAndLower: boolean;
|
||||||
hasSpecialChar: boolean;
|
hasSpecialChar: boolean;
|
||||||
validPassword: boolean;
|
validPassword: boolean;
|
||||||
showValidations: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ValidationItemProps {
|
export interface ValidationItemProps {
|
||||||
@@ -50,13 +56,13 @@ export interface ValidationItemProps {
|
|||||||
label: string;
|
label: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PersonalInfoFieldsProps {
|
export interface PersonalInfoFieldsProps extends ValidationProps {
|
||||||
firstName: string;
|
firstName: string;
|
||||||
setFirstName: (v: string) => void;
|
setFirstName: (v: string) => void;
|
||||||
lastName: string;
|
lastName: string;
|
||||||
setLastName: (v: string) => void;
|
setLastName: (v: string) => void;
|
||||||
sex: Gender;
|
sex: Gender | null;
|
||||||
setSex: Dispatch<SetStateAction<Gender>>;
|
setSex: Dispatch<SetStateAction<Gender | null>>;
|
||||||
country: string;
|
country: string;
|
||||||
setCountry: (country: string) => void;
|
setCountry: (country: string) => void;
|
||||||
nationalId: string;
|
nationalId: string;
|
||||||
@@ -68,6 +74,4 @@ export interface PersonalInfoFieldsProps {
|
|||||||
export interface SubmitProps {
|
export interface SubmitProps {
|
||||||
onSubmit: () => void;
|
onSubmit: () => void;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
error: string | null;
|
|
||||||
success: boolean;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export function SocialMedia() {
|
|||||||
} = useApi(changeEmail);
|
} = useApi(changeEmail);
|
||||||
|
|
||||||
const fullScreen = useMediaQuery((theme: Theme) =>
|
const fullScreen = useMediaQuery((theme: Theme) =>
|
||||||
theme.breakpoints.down('sm'),
|
theme?.breakpoints.down('sm'),
|
||||||
);
|
);
|
||||||
const downMd = useMediaQuery((theme: Theme) => theme.breakpoints.down('md'));
|
const downMd = useMediaQuery((theme: Theme) => theme.breakpoints.down('md'));
|
||||||
const computedMaxWidth = (fullScreen ? 'xs' : downMd ? 'sm' : 'md') as
|
const computedMaxWidth = (fullScreen ? 'xs' : downMd ? 'sm' : 'md') as
|
||||||
|
|||||||
@@ -33,19 +33,29 @@ export function useApi<T, P extends any[]>(
|
|||||||
setData(response.data);
|
setData(response.data);
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
const axiosError: AxiosError = err as AxiosError;
|
const axiosError = err as AxiosError;
|
||||||
console.log(axiosError);
|
console.log(axiosError);
|
||||||
|
|
||||||
if (axiosError.response?.status === 401) {
|
// Check for network error first
|
||||||
navigate('/login');
|
if (axiosError.code === 'ERR_NETWORK') {
|
||||||
}
|
|
||||||
|
|
||||||
if (axiosError.response?.status === 500) {
|
|
||||||
toast({
|
toast({
|
||||||
message: t('messages.serverError'),
|
message: t('messages.networkError'),
|
||||||
severity: 'error',
|
severity: 'error',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// Check for HTTP status codes on the response object
|
||||||
|
else if (axiosError.response) {
|
||||||
|
if (axiosError.response.status === 401) {
|
||||||
|
navigate('/login');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (axiosError.response.status === 500) {
|
||||||
|
toast({
|
||||||
|
message: t('messages.serverError'),
|
||||||
|
severity: 'error',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setError(err);
|
setError(err);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ export const CustomThemeProvider: React.FC<{ children: React.ReactNode }> = ({
|
|||||||
colorSchemeSelector: 'class',
|
colorSchemeSelector: 'class',
|
||||||
},
|
},
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
|
shape: {
|
||||||
|
borderRadius: 8,
|
||||||
|
},
|
||||||
typography: typography,
|
typography: typography,
|
||||||
components: {
|
components: {
|
||||||
MuiButton: {
|
MuiButton: {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { NavigateWithToast } from '@/components/NavigateWithToast';
|
|||||||
import {
|
import {
|
||||||
Calendar,
|
Calendar,
|
||||||
Devices,
|
Devices,
|
||||||
LocationTick,
|
|
||||||
Mobile,
|
Mobile,
|
||||||
PasswordCheck,
|
PasswordCheck,
|
||||||
Personalcard,
|
Personalcard,
|
||||||
@@ -105,6 +104,10 @@ export const appRoutes: RouteConfig[] = [
|
|||||||
element: <Layout />,
|
element: <Layout />,
|
||||||
authorize: true,
|
authorize: true,
|
||||||
children: [
|
children: [
|
||||||
|
{
|
||||||
|
path: '/setting/',
|
||||||
|
element: <Navigate to="/setting/profile" replace />,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/setting/profile',
|
path: '/setting/profile',
|
||||||
element: <UserInformationPage />,
|
element: <UserInformationPage />,
|
||||||
@@ -151,13 +154,6 @@ export const appRoutes: RouteConfig[] = [
|
|||||||
icon: PasswordCheck,
|
icon: PasswordCheck,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/setting/security#confirmed-ips',
|
|
||||||
navConfig: {
|
|
||||||
title: 'side.confirmedIps',
|
|
||||||
icon: LocationTick,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/setting/security#recent-sessions',
|
path: '/setting/security#recent-sessions',
|
||||||
navConfig: {
|
navConfig: {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type {
|
|||||||
TypeBackground,
|
TypeBackground,
|
||||||
TypeText,
|
TypeText,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import type { PaletteColorOptions } from 'node_modules/@mui/material';
|
import type { PaletteColorOptions } from '@mui/material';
|
||||||
|
|
||||||
export interface Palette {
|
export interface Palette {
|
||||||
primary: {
|
primary: {
|
||||||
|
|||||||
@@ -117,10 +117,12 @@ export const PALETTE: Palette = {
|
|||||||
light: {
|
light: {
|
||||||
default: grey[100],
|
default: grey[100],
|
||||||
paper: '#FFFFFF',
|
paper: '#FFFFFF',
|
||||||
|
primary: blue[50],
|
||||||
},
|
},
|
||||||
dark: {
|
dark: {
|
||||||
default: '#121212',
|
default: '#121212',
|
||||||
paper: grey[900],
|
paper: grey[900],
|
||||||
|
primary: grey[800],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// Text colors
|
// Text colors
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ declare module '@mui/material/styles' {
|
|||||||
interface PaletteOptions {
|
interface PaletteOptions {
|
||||||
club?: PaletteOptions['primary']; // Extend PaletteOptions for optional 'club'
|
club?: PaletteOptions['primary']; // Extend PaletteOptions for optional 'club'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TypeBackground {
|
||||||
|
primary: string;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@mui/material/Button' {
|
declare module '@mui/material/Button' {
|
||||||
@@ -16,6 +20,8 @@ declare module '@mui/material/Button' {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: move this to types
|
||||||
|
// has typo
|
||||||
export type PalleteColor =
|
export type PalleteColor =
|
||||||
| 'primary'
|
| 'primary'
|
||||||
| 'secondary'
|
| 'secondary'
|
||||||
|
|||||||
1
src/utils/regexes/nationalId.ts
Normal file
1
src/utils/regexes/nationalId.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const nationalIdRegex = /^\d{10}$/;
|
||||||
Reference in New Issue
Block a user