From a3fba3d4208353911683d039786fc29206d74482 Mon Sep 17 00:00:00 2001 From: Gurukiran P <76690271+GurukiranP@users.noreply.github.com> Date: Thu, 23 Nov 2023 12:57:13 +0530 Subject: [PATCH] [ES-432] [ES-402] Signup UI: common page layout across UI module (#13) * ES432: (resolved conflicts) Added common files such as error indicator and loading indicator and others and replaced the current loading indicator imports with the new loading Signed-off-by: GurukiranP * [ES-432] [ES-402] Signup UI: common page layout across the UI module Signed-off-by: GurukiranP * [ES-402] Moved loading svg to assests folder and removed unused setupTest file Signed-off-by: GurukiranP --------- Signed-off-by: GurukiranP --- signup-ui/public/images/footer_logo.png | Bin 0 -> 3428 bytes signup-ui/public/locales/en.json | 95 ++++++++++-------- signup-ui/public/locales/km.json | 95 ++++++++++-------- signup-ui/src/App.css | 15 +++ signup-ui/src/assets/svg/loading-icon.svg | 15 +++ signup-ui/src/common/ErrorIndicator.tsx | 71 +++++++++++++ signup-ui/src/common/LoadingIndicator.tsx | 41 ++++++++ signup-ui/src/components/language.tsx | 65 ++++++++---- signup-ui/src/components/ui/button.tsx | 4 +- signup-ui/src/components/ui/footer.tsx | 26 +++-- signup-ui/src/constants/states.tsx | 8 ++ signup-ui/src/constants/types.tsx | 18 ++++ signup-ui/src/models/errorIndicator.model.ts | 9 ++ signup-ui/src/models/langConfig.model.ts | 9 ++ .../src/models/loadingIndicator.model.ts | 10 ++ signup-ui/src/models/navheader.model.ts | 8 ++ .../components/AccountSetupProgress.tsx | 4 +- .../src/pages/SignUpPage/SignUpPageLayout.tsx | 7 +- signup-ui/src/reportWebVitals.ts | 15 +++ signup-ui/src/services/langConfig.service.tsx | 32 ++++++ 20 files changed, 424 insertions(+), 123 deletions(-) create mode 100644 signup-ui/public/images/footer_logo.png create mode 100644 signup-ui/src/assets/svg/loading-icon.svg create mode 100644 signup-ui/src/common/ErrorIndicator.tsx create mode 100644 signup-ui/src/common/LoadingIndicator.tsx create mode 100644 signup-ui/src/constants/states.tsx create mode 100644 signup-ui/src/constants/types.tsx create mode 100644 signup-ui/src/models/errorIndicator.model.ts create mode 100644 signup-ui/src/models/langConfig.model.ts create mode 100644 signup-ui/src/models/loadingIndicator.model.ts create mode 100644 signup-ui/src/models/navheader.model.ts create mode 100644 signup-ui/src/reportWebVitals.ts create mode 100644 signup-ui/src/services/langConfig.service.tsx diff --git a/signup-ui/public/images/footer_logo.png b/signup-ui/public/images/footer_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..9773f974c1b02417a39d894757e44ff2799e2f73 GIT binary patch literal 3428 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 G0002M5S%Fh literal 0 HcmV?d00001 diff --git a/signup-ui/public/locales/en.json b/signup-ui/public/locales/en.json index bfa106ae..ae4ed8c3 100644 --- a/signup-ui/public/locales/en.json +++ b/signup-ui/public/locales/en.json @@ -1,44 +1,55 @@ { - "enter_your_number": "Please enter your mobile number to continue", - "fail_to_send_otp": "Failed to send OTP. Please provide a valid mobile number.", - "otp_header": "Enter OTP", - "otp_subheader": "Please enter {{no_of_digit}}-digit OTP received on your number {{mobile_number}}", - "resend_otp_detail": "You can resend the OTP in {{resend_time}}", - "resend_otp": "Resend OTP", - "incorrect_otp": "Entered OTP is incorrect", - "successful": "Successful!", - "error": "Error!", - "signup_failed": "Sign-Up Failed!", - "mobile_number_verified": "Your mobile number has been verified successfully.", - "complete_registration_process": "Please continue to setup you account and complete the registration process.", - "mobile_number_already_registered": "The provided mobile number is already registered. Please use the Login option to proceed.", - "setup_account":"Setup Account", - "complete_your_registration": "Please enter the requested details to complete your registration.", - "username": "Username", - "full_name": "Full Name", - "full_name_placeholder": "Enter Full Name in Khmer", - "full_name_tooltip": "Maximum 30 characters allowed and it should not contain any digit or special characters except “ “ space.", - "password": "Password", - "password_placeholder": "Enter Password", - "confirm_password": "Confirm Password", - "confirm_password_placeholder": "Enter Password", - "terms_and_condition": "I agree to Cambodia’s Terms & Conditions and Privacy Policy, to store & process my information as required.", - "transaction_timeout": "The transaction has timed out. Please try again.", - "setup_progress": "Account setup in progress", - "setup_progress_wait": "Please wait while we setup your account.", - "congratulations": "Congratulations!", - "account_created_successfully": "Your account has been created successfully.", - "login_to_proceed": "Login to proceed.", - "login": "Login", - "okay": "Okay", - "continue": "Continue", - "page_under_construction": "Page Under Construction!", - "page_under_construction_detail": "Our experts are working hard to make this page available. Meanwhile, we request you to please visit after some time.", - "something_went_wrong": "Something went wrong!", - "something_went_wrong_detail": "Our experts are working hard to make things working again.", - "attemps_left": "{{attemptLeft}} of {{totalAttempt}} attempts left", - "terms_and_conditions_title": "Terms & Conditions", - "terms_and_conditions_content": "

Some heading goes here

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry’s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

", - "privacy_and_policy_content": "

Some heading goes here

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry’s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

", - "privacy_and_policy_title": "Privacy & Policy" + "enter_your_number": "Please enter your mobile number to continue", + "enter_your_number_placeholder": "Enter 8-9 digit phone number", + "fail_to_send_otp": "Failed to send OTP. Please provide a valid mobile number.", + "otp_header": "Enter OTP", + "otp_subheader": "Please enter {no_of_digit}-digit OTP received on your number", + "resend_otp_detail": "You can resend the OTP in ", + "resend_otp": "Resend OTP", + "incorrect_otp": "Entered OTP is incorrect", + "go_back_to_landing_page": "Go back to landing page", + "successful": "Successful!", + "error": "Error!", + "signup_failed": "Sign-Up Failed!", + "mobile_number_verified": "Your mobile number has been verified successfully. Please continue to setup you account and complete the registration process.", + "mobile_number_already_registered": "The provided mobile number is already registered. Please use the Login option to proceed.", + "setup_account": "Setup Account", + "complete_your_registration": "Please enter the requested details to complete your registration.", + "username": "Username", + "username_placeholder": "Enter username", + "full_name": "Full Name", + "full_name_placeholder": "Enter Full Name in Khmer", + "full_name_tooltip": "Maximum 30 characters allowed and it should not contain any digit or special characters except “ “ space.", + "password": "Password", + "password_placeholder": "Enter Password", + "password_rules": "
  • Require at least 8 characters long
  • Require at least 1 digit
  • Require at least 1 special character
  • Require at least 1 capital letter
  • ", + "confirm_password": "Confirm Password", + "confirm_password_placeholder": "Enter Password", + "terms_and_condition": "I agree to Cambodia’s Terms & Conditions and Privacy Policy, to store & process my information as required.", + "transaction_timeout": "The transaction has timed out. Please try again.", + "setup_progress": "Account setup in progress", + "setup_progress_wait": "Please wait while we setup your account.", + "congratulations": "Congratulations!", + "account_created_successfully": "Your account has been created successfully.", + "login_to_proceed": "Login to proceed.", + "login": "Login", + "okay": "Okay", + "continue": "Continue", + "page_under_construction": "Page Under Construction!", + "page_under_construction_detail": "Our experts are working hard to make this page available. Meanwhile, we request you to please visit after some time.", + "something_went_wrong": "Something went wrong!", + "something_went_wrong_detail": "Our experts are working hard to make things working again.", + "attempts_left": "{attemptLeft} of {totalAttempt} attempts left", + "captcha_token_validation": "Please verify that you are a human.", + "full_name_validation": "Please enter a valid name", + "password_validation": "Please enter a valid password", + "password_validation_must_match": "Passwords must match", + "terms_and_conditions_validation": "You must accept the terms and conditions", + "terms_and_conditions_title": "Terms & Conditions", + "terms_and_conditions_content": "

    Some heading goes here

    Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry’s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

    ", + "privacy_and_policy_content": "

    Some heading goes here

    Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry’s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

    ", + "privacy_and_policy_title": "Privacy & Policy", + "footer": { + "powered_by": "Powered by" + } } diff --git a/signup-ui/public/locales/km.json b/signup-ui/public/locales/km.json index 6d84dbd9..9d4da7e3 100644 --- a/signup-ui/public/locales/km.json +++ b/signup-ui/public/locales/km.json @@ -1,44 +1,55 @@ { - "enter_your_number": "សូមបញ្ចូលលេខទូរសព្ទរបស់អ្នកដើម្បីបន្ត", - "fail_to_send_otp": "មិនអាចផ្ញើលេខសម្ងាត់បានទេ! សូមពិនិត្យនិង ផ្ញើលេខទូរសព្ទរបស់អ្នកម្ដងទៀត។", - "otp_header": "បញ្ចូលលេខសម្ងាត់", - "otp_subheader": "សូមបញ្ចូល {{no_of_digit}}-លេខសម្ងាត់ដែលបានទទួលពីលេខទូរសព្ទរបស់អ្នក។ {{mobile_number}}", - "resend_otp_detail": "អ្នកអាចផ្ញើលេខសម្ងាត់ឡើងវិញក្នុងរយៈពេល {{resend_time}}", - "resend_otp": "ផ្ញើលេខសម្ងាត់ឡើងវិញ", - "incorrect_otp": "លេខសម្ងាត់មិនត្រឹមត្រូវ", - "successful": "ជោគជ័យ!", - "error": "កំហុស!", - "signup_failed": "ការចុះឈ្មោះបរាជ័យ!", - "mobile_number_verified": "លេខទូរសព្ទរបស់អ្នកត្រូវបានផ្ទៀងផ្ទាត់ដោយជោគជ័យ។", - "complete_registration_process": "សូមបន្តបង្កើតគណនីរបស់អ្នក រួចបញ្ចប់ដំណើរការចុះឈ្មោះ។", - "mobile_number_already_registered": "លេខទូរសព្ទ្ទដែលបានផ្តល់ត្រូវបានចុះឈ្មោះរួចហើយ។ សូមប្រើជម្រើសចូលគណនីដើម្បីបន្ត។", - "setup_account":"បង្កើតគណនី", - "complete_your_registration": "សូមបញ្ចូលព័ត៌មានលម្អិតដែលបានស្នើសុំដើម្បីបំពេញការចុះឈ្មោះរបស់អ្នក។", - "username": "ឈ្មោះ​អ្នកប្រើប្រាស់", - "full_name": "គោត្តនាម-នាម", - "full_name_placeholder": "បញ្ចូលគោត្តនាម-នាមជាភាសាខ្មែរ", - "full_name_tooltip": "ជាអតិបរមា 30 តួអក្សរត្រូវបានអនុញ្ញាត និងមិនគួរមានលេខ ឬតួអក្សរពិសេសណាមួយឡើយ លើកលែងតែ “ “ ចន្លោះ។", - "password": "ពាក្យសម្ងាត់", - "password_placeholder": "បញ្ចូលពាក្យសម្ងាត់", - "confirm_password": "បញ្ជាក់ពាក្យសម្ងាត់", - "confirm_password_placeholder": "បញ្ចូលពាក្យសម្ងាត់", - "terms_and_condition": "ខ្ញុំយល់ព្រមតាមលក្ខខណ្ឌ និងគោលការណ៍ឯកជនភាពរបស់ប្រទេសកម្ពុជា ដើម្បីរក្សាទុក និងដំណើរការព័ត៌មានរបស់ខ្ញុំតាមតម្រូវការ។", - "transaction_timeout": "ប្រតិបត្តិការបានផុតកំណត់។ សូម​ព្យាយាម​ម្តង​ទៀត។", - "setup_progress": "ការបង្កើតគណនីកំពុងដំណើរការ", - "setup_progress_wait": "សូមធ្វើការរង់ចាំខណៈពេលគណនីរបស់អ្នកកំពុងរៀបចំ។", - "congratulations": "សូមអបអរសាទរ!", - "account_created_successfully": "គណនីរបស់អ្នកត្រូវបានបង្កើតដោយជោគជ័យ។", - "login_to_proceed": "សូមចូលគណនីដើម្បីបន្ត។", - "login": "ចូលគណនី", - "okay": "យល់ព្រម", - "continue": "បន្ត", - "page_under_construction": "ទំព័រកំពុងសាងសង់!", - "page_under_construction_detail": "អ្នកជំនាញកំពុងធ្វើការដើម្បីឱ្យទំព័រនេះអាចប្រើប្រាស់បាន។ សូមចូលម្ដងទៀតនៅពេលក្រោយ។", - "something_went_wrong": "មានអ្វីមួយខុសប្រក្រតី!", - "something_went_wrong_detail": "អ្នកជំនាញកំពុងធ្វើការដើម្បីឱ្យអ្វីៗដំណើរការឡើងវិញ។", - "attemps_left": "ការព្យាយាមនៅសល់{{attemptLeft}} នៃ {{totalAttempt}}", - "terms_and_conditions_title": "Terms & Conditions", - "terms_and_conditions_content": "

    Some heading goes here

    Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry’s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

    ", - "privacy_and_policy_content": "

    Some heading goes here

    Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry’s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

    ", - "privacy_and_policy_title": "Privacy & Policy" + "enter_your_number": "សូមបញ្ចូលលេខទូរសព្ទរបស់អ្នកដើម្បីបន្ត", + "enter_your_number_placeholder": "បញ្ចូលលេខទូរស័ព្ទចំនួន 8-9 ខ្ទង់", + "fail_to_send_otp": "មិនអាចផ្ញើលេខសម្ងាត់បានទេ! សូមពិនិត្យនិង ផ្ញើលេខទូរសព្ទរបស់អ្នកម្ដងទៀត។", + "otp_header": "បញ្ចូលលេខសម្ងាត់", + "otp_subheader": "សូមបញ្ចូល {no_of_digit}-លេខសម្ងាត់ដែលបានទទួលពីលេខទូរសព្ទរបស់អ្នក។", + "resend_otp_detail": "អ្នកអាចផ្ញើលេខសម្ងាត់ឡើងវិញក្នុងរយៈពេល ", + "resend_otp": "ផ្ញើលេខសម្ងាត់ឡើងវិញ", + "incorrect_otp": "លេខសម្ងាត់មិនត្រឹមត្រូវ", + "go_back_to_landing_page": "ត្រឡប់ទៅកាន់ទំព័រដើម", + "successful": "ជោគជ័យ!", + "error": "កំហុស!", + "signup_failed": "ការចុះឈ្មោះបរាជ័យ!", + "mobile_number_verified": "លេខទូរសព្ទរបស់អ្នកត្រូវបានផ្ទៀងផ្ទាត់ដោយជោគជ័យ។ សូមបន្តបង្កើតគណនីរបស់អ្នក រួចបញ្ចប់ដំណើរការចុះឈ្មោះ។", + "mobile_number_already_registered": "លេខទូរសព្ទ្ទដែលបានផ្តល់ត្រូវបានចុះឈ្មោះរួចហើយ។ សូមប្រើជម្រើសចូលគណនីដើម្បីបន្ត។", + "setup_account": "បង្កើតគណនី", + "complete_your_registration": "សូមបញ្ចូលព័ត៌មានលម្អិតដែលបានស្នើសុំដើម្បីបំពេញការចុះឈ្មោះរបស់អ្នក។", + "username": "ឈ្មោះ​អ្នកប្រើប្រាស់", + "username_placeholder": "បញ្ចូលឈ្មោះ​អ្នកប្រើប្រាស់", + "full_name": "គោត្តនាម-នាម", + "full_name_placeholder": "បញ្ចូលគោត្តនាម-នាមជាភាសាខ្មែរ", + "full_name_tooltip": "ជាអតិបរមា 30 តួអក្សរត្រូវបានអនុញ្ញាត និងមិនគួរមានលេខ ឬតួអក្សរពិសេសណាមួយឡើយ លើកលែងតែ “ “ ចន្លោះ។", + "password": "ពាក្យសម្ងាត់", + "password_placeholder": "បញ្ចូលពាក្យសម្ងាត់", + "password_rules": "
  • យ៉ាងតិច 8 តួអក្សរ
  • យ៉ាងតិច 1 តួលេខ
  • យ៉ាងតិច 1 តួអក្សរពិសេស
  • យ៉ាងតិច 1 តួអក្សរធំ
  • ", + "confirm_password": "បញ្ជាក់ពាក្យសម្ងាត់", + "confirm_password_placeholder": "បញ្ចូលពាក្យសម្ងាត់", + "terms_and_condition": "ខ្ញុំយល់ព្រមតាមលក្ខខណ្ឌ និងគោលការណ៍ឯកជនភាពរបស់ប្រទេសកម្ពុជា ដើម្បីរក្សាទុក និងដំណើរការព័ត៌មានរបស់ខ្ញុំតាមតម្រូវការ។", + "transaction_timeout": "ប្រតិបត្តិការបានផុតកំណត់។ សូម​ព្យាយាម​ម្តង​ទៀត។", + "setup_progress": "ការបង្កើតគណនីកំពុងដំណើរការ", + "setup_progress_wait": "សូមធ្វើការរង់ចាំខណៈពេលគណនីរបស់អ្នកកំពុងរៀបចំ។", + "congratulations": "សូមអបអរសាទរ!", + "account_created_successfully": "គណនីរបស់អ្នកត្រូវបានបង្កើតដោយជោគជ័យ។", + "login_to_proceed": "សូមចូលគណនីដើម្បីបន្ត។", + "login": "ចូលគណនី", + "okay": "យល់ព្រម", + "continue": "បន្ត", + "page_under_construction": "ទំព័រកំពុងសាងសង់!", + "page_under_construction_detail": "អ្នកជំនាញកំពុងធ្វើការដើម្បីឱ្យទំព័រនេះអាចប្រើប្រាស់បាន។ សូមចូលម្ដងទៀតនៅពេលក្រោយ។", + "something_went_wrong": "មានអ្វីមួយខុសប្រក្រតី!", + "something_went_wrong_detail": "អ្នកជំនាញកំពុងធ្វើការដើម្បីឱ្យអ្វីៗដំណើរការឡើងវិញ។", + "attempts_left": "ការព្យាយាមនៅសល់ {attemptLeft} នៃ {totalAttempt}", + "captcha_token_validation": "សូមបញ្ជាក់ថាអ្នកជាមនុស្ស", + "full_name_validation": "សូមបញ្ចូលគោត្តនាម-នាមឱ្យបានត្រឹមត្រូវ", + "password_validation": "សូមបញ្ចូលលេខសម្ងាត់ឱ្យបានត្រឹមត្រូវ", + "password_validation_must_match": "លេខចម្ងាត់ត្រូវតែដូចគ្នា", + "terms_and_conditions_validation": "អ្នកត្រូវយល់ព្រមតាមលក្ខខណ្ឌ និងគោលការណ៍", + "terms_and_conditions_title": "Terms & Conditions", + "terms_and_conditions_content": "

    Some heading goes here

    Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry’s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

    ", + "privacy_and_policy_content": "

    Some heading goes here

    Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry’s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

    ", + "privacy_and_policy_title": "Privacy & Policy", + "footer": { + "powered_by": "ដំណើរការដោយ" + } } diff --git a/signup-ui/src/App.css b/signup-ui/src/App.css index 90d78330..c399d87a 100644 --- a/signup-ui/src/App.css +++ b/signup-ui/src/App.css @@ -44,6 +44,21 @@ body { height: 70px; } +.footer-brand-logo { + content: url("../public/images/footer_logo.png"); + height: 25px; +} + +.footer-container { + padding-top: 11px; + padding-bottom: 11px; +} + +.footer-text { + color: #898989; + font-size: smaller; +} + @layer base { :root { --font-sans: "Inter"; diff --git a/signup-ui/src/assets/svg/loading-icon.svg b/signup-ui/src/assets/svg/loading-icon.svg new file mode 100644 index 00000000..a2cb9339 --- /dev/null +++ b/signup-ui/src/assets/svg/loading-icon.svg @@ -0,0 +1,15 @@ + \ No newline at end of file diff --git a/signup-ui/src/common/ErrorIndicator.tsx b/signup-ui/src/common/ErrorIndicator.tsx new file mode 100644 index 00000000..cc1f75ea --- /dev/null +++ b/signup-ui/src/common/ErrorIndicator.tsx @@ -0,0 +1,71 @@ +import { useTranslation } from "react-i18next"; +import { useLocation, useSearchParams } from "react-router-dom"; +import { Buffer } from "buffer"; +import IErrorIndicator from "../models/errorIndicator.model"; + +const fixedInputClass = + "p-2 mt-1 mb-1 w-full text-center text-sm rounded-lg text-red-700 bg-red-100 "; + +/** + * @param {string} prefix optional error key which will be shown before the error msg. + * @param {string} errorCode is a key from locales file under errors namespace + * @param {string} defaultMsg (Optional) is a fallback value if transaction for errorCode not found. + * If defaultMsg is not passed then errorCode key itself became the fallback value. + */ +const ErrorIndicator = ({ + prefix = "", + errorCode, + defaultMsg, + i18nKeyPrefix = "errors", + customClass, +}: IErrorIndicator) => { + const [searchParams, setSearchParams] = useSearchParams(); + const location = useLocation(); + + const { t } = useTranslation("translation", { keyPrefix: i18nKeyPrefix }); + + //Redirecting if transaction invalid + if (errorCode === "invalid_transaction" || errorCode === "link_code_limit_reached") { + let response = location.hash; + + if (!response) { + //TODO naviagte to default error page + return; + } + + let nonce = searchParams.get("nonce"); + let state = searchParams.get("state"); + var decodeOAuth = Buffer.from(response, "base64")?.toString(); + var OAuthDetails = JSON.parse(decodeOAuth); + + let redirect_uri = OAuthDetails.redirectUri; + + if (!redirect_uri) { + //TODO naviagte to default error page + return; + } + + let params = "?"; + if (nonce) { + params = params + "nonce=" + nonce + "&"; + } + params = params + "error_description=" + t("invalid_transaction", defaultMsg) + "&"; + + //REQUIRED + params = params + "state=" + state + "&"; + //REQUIRED + params = params + "error=" + "invalid_transaction"; + + window.location.replace(redirect_uri + params); + return; + } + + return ( +
    + {prefix && t(prefix) + ": "} + {t(errorCode, defaultMsg)} +
    + ); +}; + +export default ErrorIndicator; diff --git a/signup-ui/src/common/LoadingIndicator.tsx b/signup-ui/src/common/LoadingIndicator.tsx new file mode 100644 index 00000000..f60a4d36 --- /dev/null +++ b/signup-ui/src/common/LoadingIndicator.tsx @@ -0,0 +1,41 @@ +import { useTranslation } from "react-i18next"; + +import { Sizes } from "../constants/types"; +import ILoadingIndicator from "../models/loadingIndicator.model"; +import { ReactComponent as LoadingIcon } from "~assets/svg/loading-icon.svg"; + +const dynamicSize: Sizes = { + small: { + width: "1.5rem", + height: "1.5rem", + }, + medium: { + width: "2.5rem", + height: "2.5rem", + }, + large: { + width: "5rem", + height: "5rem", + }, +}; + +const LoadingIndicator = ({ + message = "", + size = "medium", + msgParam = "", + i18nKeyPrefix = "loadingMsgs", +}: ILoadingIndicator) => { + const { t } = useTranslation("translation", { keyPrefix: i18nKeyPrefix }); + + return ( + <> +
    + + Loading... + {message && t(message, msgParam)} +
    + + ); +}; + +export default LoadingIndicator; diff --git a/signup-ui/src/components/language.tsx b/signup-ui/src/components/language.tsx index c7858fba..a97ae104 100644 --- a/signup-ui/src/components/language.tsx +++ b/signup-ui/src/components/language.tsx @@ -1,15 +1,10 @@ +import * as DropdownMenu from "@radix-ui/react-dropdown-menu"; import { useTranslation } from "react-i18next"; import { ReactComponent as TranslationIcon } from "~assets/svg/translation-icon.svg"; import locales from "../../public/locales/default.json"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "./ui/select"; +import { Icons } from "./ui/icons"; export const Language = () => { const { i18n } = useTranslation(); @@ -18,27 +13,53 @@ export const Language = () => { i18n.changeLanguage(language); }; + var dropdownItemClass = + "group text-[14px] leading-none flex items-center relative select-none outline-none data-[disabled]:pointer-events-none hover:font-bold cursor-pointer py-2 first:border-b-[1px]"; + return (
    - - + + + + + + {Object.entries(locales.languages_2Letters).map(([key, value]) => ( + handleLanguageChange(key)} + > + {value} +
    + {i18n.language === key && ( + + )} +
    +
    + ))} + +
    +
    +
    ); }; diff --git a/signup-ui/src/components/ui/button.tsx b/signup-ui/src/components/ui/button.tsx index 138fcb10..fddc3860 100644 --- a/signup-ui/src/components/ui/button.tsx +++ b/signup-ui/src/components/ui/button.tsx @@ -4,7 +4,7 @@ import { cva, type VariantProps } from "class-variance-authority"; import { cn } from "~utils/cn"; -import { Icons } from "./icons"; +import LoadingIndicator from "~/common/LoadingIndicator"; const buttonVariants = cva( "inline-flex items-center justify-center whitespace-nowrap h-[62px] text-lg rounded-lg font-bold ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:bg-muted disabled:pointer-events-none", @@ -63,7 +63,7 @@ const Button = React.forwardRef( {...props} > {isLoading ? ( - + ) : ( <>{children} )} diff --git a/signup-ui/src/components/ui/footer.tsx b/signup-ui/src/components/ui/footer.tsx index 1e003519..c4df5db9 100644 --- a/signup-ui/src/components/ui/footer.tsx +++ b/signup-ui/src/components/ui/footer.tsx @@ -1,15 +1,23 @@ import { forwardRef, ReactNode } from "react"; +import { useTranslation } from "react-i18next"; + +const Footer = ({ i18nKeyPrefix = "footer" }) => { + const footerCheck = process.env.REACT_APP_FOOTER === "true"; + + const { t } = useTranslation("translation", { + keyPrefix: i18nKeyPrefix, + }); -const Footer = () => { return ( -
    -
    - - Powered by eSignet. - -
    -
    + <> + {footerCheck && ( +
    + {t("powered_by")} + footerLogo +
    + )} + ); }; -export default forwardRef(Footer); +export default forwardRef(Footer); \ No newline at end of file diff --git a/signup-ui/src/constants/states.tsx b/signup-ui/src/constants/states.tsx new file mode 100644 index 00000000..21337848 --- /dev/null +++ b/signup-ui/src/constants/states.tsx @@ -0,0 +1,8 @@ +const LoadingStates = { + LOADING: "LOADING", + LOADED: "LOADED", + ERROR: "ERROR", + AUTHENTICATING: "AUTHENTICATING", +}; + +export { LoadingStates }; diff --git a/signup-ui/src/constants/types.tsx b/signup-ui/src/constants/types.tsx new file mode 100644 index 00000000..607f1a6f --- /dev/null +++ b/signup-ui/src/constants/types.tsx @@ -0,0 +1,18 @@ +type HeightWidth = { + height: string; + width: string; +}; + +type SizeValue = "small" | "medium" | "large"; + +type Sizes = { [key in SizeValue]: HeightWidth }; + +type StringMap = { [key: string]: string} +type StringNumberMap = { [key: string]: number} + +type LabelValue = { + label: string; + value: string; +} + +export type { Sizes, SizeValue, HeightWidth, StringMap, StringNumberMap, LabelValue }; diff --git a/signup-ui/src/models/errorIndicator.model.ts b/signup-ui/src/models/errorIndicator.model.ts new file mode 100644 index 00000000..7af7d552 --- /dev/null +++ b/signup-ui/src/models/errorIndicator.model.ts @@ -0,0 +1,9 @@ +interface IErrorIndicator { + prefix: string; + errorCode: string; + defaultMsg: string; + i18nKeyPrefix: string; + customClass: string; +} + +export default IErrorIndicator; diff --git a/signup-ui/src/models/langConfig.model.ts b/signup-ui/src/models/langConfig.model.ts new file mode 100644 index 00000000..8ddfe03a --- /dev/null +++ b/signup-ui/src/models/langConfig.model.ts @@ -0,0 +1,9 @@ +import { StringMap } from "../constants/types"; + +interface ILangConfig { + languages_2Letters: StringMap; + rtlLanguages: string[]; + langCodeMapping: StringMap; +} + +export default ILangConfig; \ No newline at end of file diff --git a/signup-ui/src/models/loadingIndicator.model.ts b/signup-ui/src/models/loadingIndicator.model.ts new file mode 100644 index 00000000..64a70e03 --- /dev/null +++ b/signup-ui/src/models/loadingIndicator.model.ts @@ -0,0 +1,10 @@ +import { SizeValue } from "../constants/types"; + +interface ILoadingIndicator { + message?: string; + size?: SizeValue; + msgParam?: string; + i18nKeyPrefix?: string; +} + +export default ILoadingIndicator; \ No newline at end of file diff --git a/signup-ui/src/models/navheader.model.ts b/signup-ui/src/models/navheader.model.ts new file mode 100644 index 00000000..1d2723e4 --- /dev/null +++ b/signup-ui/src/models/navheader.model.ts @@ -0,0 +1,8 @@ +import { LabelValue } from "../constants/types"; + +interface INavHeader { + langOptions: LabelValue[]; + i18nKeyPrefix?: string; +} + +export default INavHeader; \ No newline at end of file diff --git a/signup-ui/src/pages/SignUpPage/AccountSetup/components/AccountSetupProgress.tsx b/signup-ui/src/pages/SignUpPage/AccountSetup/components/AccountSetupProgress.tsx index 0e2142d2..45ed9809 100644 --- a/signup-ui/src/pages/SignUpPage/AccountSetup/components/AccountSetupProgress.tsx +++ b/signup-ui/src/pages/SignUpPage/AccountSetup/components/AccountSetupProgress.tsx @@ -1,6 +1,6 @@ import { useTranslation } from "react-i18next"; -import { Icons } from "~components/ui/icons"; +import LoadingIndicator from "~/common/LoadingIndicator"; export const AccountSetupProgress = () => { const { t } = useTranslation(); @@ -8,7 +8,7 @@ export const AccountSetupProgress = () => { return (
    - +

    {t("setup_progress")} diff --git a/signup-ui/src/pages/SignUpPage/SignUpPageLayout.tsx b/signup-ui/src/pages/SignUpPage/SignUpPageLayout.tsx index 7da8b9fd..44fa8905 100644 --- a/signup-ui/src/pages/SignUpPage/SignUpPageLayout.tsx +++ b/signup-ui/src/pages/SignUpPage/SignUpPageLayout.tsx @@ -1,8 +1,7 @@ -import { Icons } from "~components/ui/icons"; - import { useSettings } from "./queries"; import { SignUpPage } from "./SignUpPage"; import { SignUpProvider } from "./SignUpProvider"; +import LoadingIndicator from "~/common/LoadingIndicator"; export const SignUpPageLayout = () => { const { data: settings, isLoading } = useSettings(); @@ -10,13 +9,13 @@ export const SignUpPageLayout = () => { if (isLoading || !settings) { return (
    - +
    ); } return ( -
    +
    { + if (onPerfEntry && onPerfEntry instanceof Function) { + import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { + getCLS(onPerfEntry); + getFID(onPerfEntry); + getFCP(onPerfEntry); + getLCP(onPerfEntry); + getTTFB(onPerfEntry); + }); + } +}; + +export default reportWebVitals; diff --git a/signup-ui/src/services/langConfig.service.tsx b/signup-ui/src/services/langConfig.service.tsx new file mode 100644 index 00000000..16148508 --- /dev/null +++ b/signup-ui/src/services/langConfig.service.tsx @@ -0,0 +1,32 @@ +import axios from "axios"; +import ILangConfig from "../models/langConfig.model"; +import { StringMap } from "../constants/types"; + +const defaultConfigEndpoint = "/locales/default.json"; + +/** + * fetchs and return the locale configuration stored in public folder + * @returns json object + */ +const getLocaleConfiguration = async (): Promise => { + const endpoint = process.env.PUBLIC_URL + defaultConfigEndpoint; + + const response = await axios.get(endpoint); + return response.data; +}; + +const getLangCodeMapping = async (): Promise => { + let localConfig: ILangConfig = await getLocaleConfiguration(); + let reverseMap = Object.entries(localConfig.langCodeMapping).reduce( + (pv, [key, value]) => ((pv[value] = key), pv), + {} as StringMap + ); + return reverseMap; +}; + +const langConfigService = { + getLocaleConfiguration, + getLangCodeMapping, +}; + +export default langConfigService;