Skip to content

Commit

Permalink
ES-513: wiring signup UI with signup service endpoints (mosip#31)
Browse files Browse the repository at this point in the history
* chore: change query hook from verb to noun

* chore: adapt naming convention

* chore: use tanstack query and dev tool

* fix: fix registration type imports

* fix: resolve validate dom nesting

* feat: check account status

* fix: remove duplicates

* fix: control step with zustand and refactor structures

* fix: remove sign up context

* fix: fix ui of OTP step

* fix: fix ui of Phone step

* fix: fix deprecated css classes

* feat: conditionally render font based on language

* fix: show lang with corresponding font family

* fix: redirect user back to callback url

* fix: fix default input text

* fix: fix phone status and account registration status steps

* fix: fix ui of account setup status step

* fix: translate setup account title and description

* fix: adapt the layout of the design when scrolling

* fix: add arrow to popover

* fix: fix ul list disc breaking styling

* feat: reset otp on resending otp

* chore: add tailwindcss prettier

* feat: add storybook

* fix: fix styling and format

* fix: fix button styles

* feat: add stories for components

* fix: remove pnpm lock

* fix(AccountSetup): fix styles

* fix(Phone): fix styles

* fix(Phone): fix phone input

* fix(Phone): fix phone input height

* fix: use identifier prefix

* fix(Phone): fix back icon margin

* fix: change api env variable

* chore(Readme): add storybook script

* fix: add identifier prefix

* fix(form): fix form validation msg design

* fix(Step): fix alert in step

* fix(Phone): fix challenge generation handler

* fix(validation): add dynamic identifier prefix validation

* fix(otp): add identifier prefix

* fix(termpolicy): fix open term and policy modals

* fix: handle account registration status in the next step

* fix: fix otp step

* fix(phone): use sitekey from api

* fix: fix package duplication

* fix: fix types

* fix: pass locale and isRegenerate to api

* fix: fix otp handler when phone already registered

* fix: refactor resend delay

* fix: add loading state to account setup submit

* fix: refactor otp handler

* fix: reset error msg before resend otp

* fix: fix redirect to sign in or sign up

* fix: fix otp error msg

* fix: add popup timeout

* fix: handle account registration status

* fix: fix language dropdown layer

* fix: remove deprecated env variables

* fix: rename PUBLIC_URL to BASE_URL

* fix: use 3-character locale

* fix: switch language priority

* fix: map error code to related translation

* fix: empty error response template

* fix: fix account registration status conditions

* fix: fix popup timeout

* fix: redirect users on invalid transaction and timeout

* fix: fix label

* fix: reset input upon passing global error

* fix: fix i18n language

* fix: remove text

* fix: fix otp timeout flow

* fix: fix language initialization

* fix: fix conditions handler

* fix: refactor active message

* fix: fix transaction timeout error flow

* fix: fix number of time status checked

* fix: set error message for request limit

* fix: use only invalid_transaction

* chore: remove deprecated env variable

* fix: fix no action taken by user

* fix: ignore garbage-collect

* fix: fix generate challenge key

---------

Co-authored-by: Bunsy <[email protected]>
Signed-off-by: Sreang Rathanak <[email protected]>
  • Loading branch information
2 people authored and Sreang Rathanak committed Jan 15, 2024
1 parent 5a96c3b commit 70a51b9
Show file tree
Hide file tree
Showing 28 changed files with 764 additions and 377 deletions.
3 changes: 1 addition & 2 deletions signup-ui/.env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
REACT_APP_API_BASE_URL="http://localhost:3333"
REACT_APP_CAPTCHA_SITE_KEY=""
REACT_APP_API_BASE_URL="http://localhost:8088/v1/signup"
REACT_APP_REDIRECT_SIGN_IN_URL="https://esignet.camdgc-dev.mosip.net/authorize"
REACT_APP_REDIRECT_SIGN_UP_URL="http://localhost:3000/signup"
2 changes: 1 addition & 1 deletion signup-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"react-router-dom": "^6.17.0",
"react-scripts": "^5.0.1",
"react-select": "^5.7.7",
"react-timer-hook": "^3.0.7",
"react-tooltip": "^5.21.6",
"rooks": "^7.14.1",
"tailwind-merge": "^2.0.0",
Expand Down Expand Up @@ -92,7 +93,6 @@
"@storybook/react-webpack5": "^7.6.0",
"@storybook/test": "^7.6.0",
"@tanstack/react-query-devtools": "^5.8.4",
"@tanstack/react-query-devtools": "^5.8.4",
"@types/lodash": "^4.14.200",
"@types/react-google-recaptcha": "^2.1.7",
"autoprefixer": "^10.4.16",
Expand Down
2 changes: 1 addition & 1 deletion signup-ui/public/env-config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
window._env_ = {
DEFAULT_LANG: "en",
DEFAULT_LANG: "km",
};
11 changes: 0 additions & 11 deletions signup-ui/public/locales/default.json

This file was deleted.

16 changes: 15 additions & 1 deletion signup-ui/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"complete_your_registration": "Please enter the requested details to complete your registration.",
"username": "Username",
"username_placeholder": "Enter username",
"full_name": "Full Name",
"full_name": "Full Name in Khmer",
"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",
Expand Down Expand Up @@ -52,5 +52,19 @@
"privacy_and_policy_title": "Privacy & Policy",
"footer": {
"powered_by": "Powered by"
},
"error_response": {
"invalid_transaction": "",
"invalid_otp_channel": "",
"invalid_captcha": "",
"send_otp_failed": "",
"active_otp_found": "",
"unknown_error": "",
"challenge_failed": "",
"invalid_challenge_type": "",
"invalid_challenge_format": "",
"already-registered": "",
"timed_out": "",
"request_limit": ""
}
}
14 changes: 14 additions & 0 deletions signup-ui/public/locales/km.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,19 @@
"privacy_and_policy_title": "Privacy & Policy",
"footer": {
"powered_by": "ដំណើរការដោយ"
},
"error_response": {
"invalid_transaction": "",
"invalid_otp_channel": "",
"invalid_captcha": "",
"send_otp_failed": "",
"active_otp_found": "",
"unknown_error": "",
"challenge_failed": "",
"invalid_challenge_type": "",
"invalid_challenge_format": "",
"already-registered": "",
"timed_out": "",
"request_limit": ""
}
}
35 changes: 16 additions & 19 deletions signup-ui/src/components/language.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,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 { langFontMapping } from "~constants/language";
import { langFontMapping, languages_2Letters } from "~constants/language";
import { cn } from "~utils/cn";

import locales from "../../public/locales/default.json";
import { Icons } from "./ui/icons";
import { cn } from "~utils/cn";

export const Language = () => {
const { i18n } = useTranslation();
Expand All @@ -18,38 +17,36 @@ export const Language = () => {

return (
<div className="flex">
<TranslationIcon className="w-9 h-9 mr-2" />
<TranslationIcon className="mr-2 h-9 w-9" />
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
<span
className="inline-flex items-center justify-center bg-white outline-none hover:cursor-pointer text-[14px]"
className="inline-flex items-center justify-center bg-white text-[14px] outline-none hover:cursor-pointer"
aria-label="Customise options"
>
{
locales.languages_2Letters[
i18n.language as keyof typeof locales.languages_2Letters
languages_2Letters[
i18n.language as keyof typeof languages_2Letters
]
}
<Icons.chevronDown className="h-4 w-4 ml-1" />
<Icons.chevronDown className="ml-1 h-4 w-4" />
</span>
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content
className="min-w-[220px] bg-white rounded-md shadow-[0px_10px_38px_-10px_rgba(0,0,0,_0.35),_0px_10px_20px_-15px_rgba(0,0,0,_0.2)] will-change-[opacity,transform] data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade px-3 py-2"
className="data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade z-50 min-w-[220px] rounded-md bg-white px-3 py-2 shadow-[0px_10px_38px_-10px_rgba(0,0,0,_0.35),_0px_10px_20px_-15px_rgba(0,0,0,_0.2)] will-change-[opacity,transform]"
sideOffset={5}
>
{Object.entries(locales.languages_2Letters).map(([key, value]) => (
{Object.entries(languages_2Letters).map(([key, value]) => (
<DropdownMenu.Item
key={key}
className={
cn(
"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]",
langFontMapping[key],
{
"font-bold": i18n.language === key
},
)
}
className={cn(
"group relative flex cursor-pointer select-none items-center py-2 text-[14px] leading-none outline-none first:border-b-[1px] hover:font-bold data-[disabled]:pointer-events-none",
langFontMapping[key],
{
"font-bold": i18n.language === key,
}
)}
onSelect={() => handleLanguageChange(key)}
>
{value}
Expand Down
27 changes: 27 additions & 0 deletions signup-ui/src/components/ui/active-message.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as React from "react";

import { cn } from "~utils/cn";

export interface ActiveMessageProps
extends React.HTMLAttributes<HTMLDivElement> {}

const ActiveMessage = React.forwardRef<HTMLDivElement, ActiveMessageProps>(
({ className, hidden, children, ...props }, ref) => (
<div
ref={ref}
className={cn(
"flex items-center justify-between bg-destructive/5 px-4 py-2",
{
hidden,
}
)}
{...props}
>
{children}
</div>
)
);

ActiveMessage.displayName = "ActiveMessage";

export { ActiveMessage };
12 changes: 12 additions & 0 deletions signup-ui/src/constants/language.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
export const languages_2Letters = {
km: "ខ្មែរ",
en: "English",
};

export const rtlLanguages = [];

export const langCodeMapping = {
khm: "km",
eng: "en",
};

export const langFontMapping: { [key: string]: string } = {
en: "font-inter",
km: "font-kantumruypro",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,112 @@
import { useMutationState, useQueryClient } from "@tanstack/react-query";
import { useTranslation } from "react-i18next";
import { useLocation } from "react-router-dom";

import { ReactComponent as SuccessIconSvg } from "~assets/svg/success-icon.svg";
import { Button } from "~components/ui/button";
import { Step, StepContent } from "~components/ui/step";
import { getSignInRedirectURL } from "~utils/link";
import { keys as mutationKeys } from "~pages/SignUpPage/mutations";
import { keys as queryKeys } from "~pages/SignUpPage/queries";
import {
RegistrationResponseDto,
RegistrationStatus,
RegistrationStatusResponseDto,
RegistrationWithFailedStatus,
} from "~typings/types";

import { AccountRegistrationStatusLayout } from "./components/AccountRegistrationStatusLayout";

export const AccountRegistrationStatus = () => {
const { t } = useTranslation();
const { hash: fromSignInHash } = useLocation();

const handleAction = () => {
window.location.href = getSignInRedirectURL(fromSignInHash);
};
const queryClient = useQueryClient();

const [registration] = useMutationState<RegistrationResponseDto>({
filters: { mutationKey: mutationKeys.registration, status: "success" },
select: (mutation) => mutation.state.data as RegistrationResponseDto,
});

const registrationStatus =
queryClient.getQueryData<RegistrationStatusResponseDto>(
queryKeys.registrationStatus
);

const registrationStatusState = queryClient.getQueryState(
queryKeys.registrationStatus
);

if (!registration) {
return (
<AccountRegistrationStatusLayout
status="failed"
message={t("something_went_wrong")}
/>
);
}

if (registration && registration.errors.length > 0) {
return (
<AccountRegistrationStatusLayout
status="failed"
message={t(`error_response.${registration.errors[0].errorCode}`)}
/>
);
}

if (
registration.response?.status === RegistrationStatus.PENDING &&
registrationStatusState?.error
) {
return (
<AccountRegistrationStatusLayout
status="failed"
message={t("error_response.request_limit")}
/>
);
}

if (
registration.response?.status === RegistrationStatus.PENDING &&
!registrationStatus
) {
return (
<AccountRegistrationStatusLayout
status="failed"
message={t(`error_response.${registration.errors[0].errorCode}`)}
/>
);
}

if (
registration.response?.status === RegistrationStatus.PENDING &&
registrationStatus
) {
if (registrationStatus.errors.length > 0) {
return (
<AccountRegistrationStatusLayout
status="failed"
message={t(
`error_response.${registrationStatus.errors[0].errorCode}`
)}
/>
);
}
if (
registrationStatus.response &&
[
RegistrationWithFailedStatus.FAILED,
RegistrationWithFailedStatus.PENDING,
].includes(registrationStatus.response.status)
) {
return (
<AccountRegistrationStatusLayout
status="failed"
message={t("something_went_wrong")}
/>
);
}
}

return (
<Step>
<StepContent>
<div className="flex flex-col items-center gap-4 py-4">
<SuccessIconSvg />
<div className="text-center text-lg font-semibold">
<h1>{t("congratulations")}</h1>
<h2>{t("account_created_successfully")}</h2>
</div>
<p className="text-center text-gray-500">{t("login_to_proceed")}</p>
</div>
<Button className="my-4 h-16 w-full" onClick={handleAction}>
{fromSignInHash ? t("login") : t("okay")}
</Button>
</StepContent>
</Step>
<AccountRegistrationStatusLayout
status="success"
message={t("login_to_proceed")}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { useTranslation } from "react-i18next";
import { useLocation } from "react-router-dom";

import { ReactComponent as FailedIconSvg } from "~assets/svg/failed-icon.svg";
import { ReactComponent as SuccessIconSvg } from "~assets/svg/success-icon.svg";
import { Button } from "~components/ui/button";
import { Step, StepContent } from "~components/ui/step";
import { getSignInRedirectURL } from "~utils/link";

interface AccountRegistrationStatusLayoutProps {
status: "success" | "failed";
message: string;
}

export const AccountRegistrationStatusLayout = ({
status,
message,
}: AccountRegistrationStatusLayoutProps) => {
const { t } = useTranslation();
const { hash: fromSignInHash } = useLocation();

const handleAction = (e: any) => {
e.preventDefault();
window.location.href = getSignInRedirectURL(fromSignInHash);
};

return (
<Step>
<StepContent>
<div className="flex flex-col items-center gap-4 py-4">
{status === "success" ? <SuccessIconSvg /> : <FailedIconSvg />}
<div className="text-center text-lg font-semibold">
{status === "success" ? (
<>
<h1>{t("congratulations")}</h1>
<h2>{t("account_created_successfully")}</h2>
</>
) : (
<h1>{t("signup_failed")}</h1>
)}
</div>
<p className="break-all text-center text-gray-500">{message}</p>
</div>
<Button className="my-4 h-16 w-full" onClick={handleAction}>
{fromSignInHash ? t("login") : t("okay")}
</Button>
</StepContent>
</Step>
);
};
Loading

0 comments on commit 70a51b9

Please sign in to comment.