Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Qf 1034 unified registration #2323

Merged
merged 9 commits into from
Feb 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
"/reciters": ["reciter", "home"],
"/profile": ["home", "profile", "collection", "quran-reader"],
"/login": ["login"],
"/forgot-password": ["login"],
"/reset-password": ["login"],
"/about-the-quran": ["about-quran"],
"/notes-and-reflections": ["notes"],
"/ramadan": ["ramadan-activities"],
Expand Down
6 changes: 5 additions & 1 deletion locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,11 @@
"email": "Email",
"firstName": "First Name",
"lastName": "Last Name",
"title": "Title"
"title": "Title",
"username": "Username",
"password": "Password",
"confirm-password": "Confirm Password",
"verification-code": "Verification Code"
},
"from": "From",
"fundraising-sticky-banner": {
Expand Down
94 changes: 90 additions & 4 deletions locales/en/login.json
Original file line number Diff line number Diff line change
@@ -1,24 +1,110 @@
{
"awaiting-confirmation": "Awaiting Confirmation",
"back": "Back",
"continue": "Continue",
"continue-apple": "Continue with Apple",
"continue-email": "Continue with Email",
"continue-facebook": "Continue with Facebook",
"continue-google": "Continue with Google",
"email-placeholder": "Email Address",
"email-placeholder": "Email address",
"first-name-placeholder": "First Name",
"last-name-placeholder": "Last Name",
"username-placeholder": "Username",
"confirm-password-placeholder": "Confirm password",
"confirm-new-password-placeholder": "Confirm new password",
"confirm": "Confirm",
"error": {
"email-required": "Email is missing!",
"invalid-email": "Invalid Email Format!",
"invalid-credentials": "Invalid Email or password",
"login-failed": "Login failed. Please try again.",
"password-required": "Password is missing!"
},
"errors": {
"min": "*{{fieldName}} must be more than or equal to {{min}} digits",
"max": "*{{fieldName}} must be less than or equal to {{max}} digits",
"name": "*{{fieldName}} should be letters and numbers only",
"username": "*{{fieldName}} accept underscore and letters only",
"email": "*Invalid Email Format!",
"confirm": "*Confirm password doesn't match the password",
"required": "*{{fieldName}} is missing!",
"taken": "*{{fieldName}} already exists!",
"invalid": "*This {{fieldName}} is invalid",
"invalidEmailOrPassword": "*Invalid email or password",
"usedToken": "*This token already used",
"expiredToken": "*This token is expired",
"banned": "*Sorry, Your account is banned. Contact Quran.Foundation",
"exactLength": "*Value must be exact length",
"immutable": "*This value cannot be changed",
"badRequest": "*Invalid request",
"notFound": "*Not found",
"verification-code-invalid": "This verification code is invalid",
"verification-code-length": "Verification code must be {{length}} digits",
"verification-resend-failed": "Failed to resend verification code",
"account-banned": "Sorry, Your account is banned. Contact Quran.Foundation",
"verification-failed": "Verification failed. Please try again.",
"signup-failed": "Signup failed. Please try again.",
"signin-failed": "Signin failed. Please try again.",
"forgot-password-failed": "Failed to send password reset email. Please try again.",
"reset-password-failed": "Failed to reset password. Please try again."
},
"feature-1": "Track your goals",
"feature-2": "Maintain reading streaks",
"feature-3": "Create collections",
"feature-4": "Sync your data across browsers",
"feature-5": "And more!",
"feature-6": "<b>New!</b> Notes & Reflections",
"login-cta": "Log-in or Sign up now:",
"feature-6": "New! Notes & Reflections",
"forgot-password": "Forgot password",
"forgot-password-title": "Forgot password?",
"forgot-password-description": "Enter your email address and we'll send you instructions to reset your password.",
"reset-password": "Reset Password",
"set-new-password": "Set a new password",
"back-to-login": "Back to Login",
"forgot-password-success": "Password reset email sent! Please check your inbox.",
"password-reset-success": "Password reset successfully!",
"login-cta": "Sign in or Sign up now:",
"login-error": {
"AuthenticationError": "Authentication failed. Please try again later",
"TokenExpiredError": "You have been logged out, please login again.",
"BannedUserError": "Sorry, Your account is banned. Contact Quran.Foundation."
},
"login-title": "Login to Quran.com",
"other-options": "Other Login Options",
"password-placeholder": "Password",
"new-password-placeholder": "New password",
"privacy-policy": "Protecting your privacy is our priority – By signing up, you consent to our <link>Privacy Policy</link> and <link1>Terms and conditions</link1>.",
"verify-code": "Verify that the provided security code matches the following text:"
"quran-title": "Quran.com",
"sign-in": "Sign in",
"sign-up": "Sign up",
"sign-in-or-sign-up": "Sign in or Sign up",
"verify-code": "Verify that the provided security code matches the following text:",
"welcome-title": "Welcome to",
"welcome-description-1": "The unified registration of",
"welcome-description-2": ".Foundation",
"welcome-description-3": "You will have access to the following websites through your sign in details.",
"quran-text": "Quran",
"unified-registration-1": "The unified registration of ",
"unified-registration-2": "Quran.Foundation",
"unified-registration-3": ". You will have access to the following websites ",
"unified-registration-4": "through your sign in details.",
"reflect-feature-1": "Reflect on the Quran",
"reflect-feature-2": "Join Groups",
"reflect-feature-3": "Interact with others",
"reflect-feature-4": "And more!",
"password-rules": {
"min-length": "Min 8 characters",
"max-length": "Max 20 characters",
"uppercase": "At least one uppercase letter",
"lowercase": "At least one lowercase letter",
"number": "At least one number",
"special": "At least one special character (!@#$%^&*_-)"
},
"check-email-title": "Check email to complete sign up",
"verification-code-sent-to": "We just sent an email to",
"verification-code-instruction": "Please enter the code provided in the email to complete sign up",
"verification-code-spam-note": "Didn't receive an email? Check your spam folder",
"verification-code-resend": "Resend email",
"verification-code-resend-countdown": "Resend verification email in {{seconds}} sec...",
"verification-code-sent": "Verification code sent!",
"reset-password-success": "Password reset successfully!"
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
"react-redux": "^9.1.2",
"react-share": "^5.2.0",
"react-toastify": "^9.0.8",
"react-verification-input": "^4.2.2",
"react-virtuoso": "^2.19.0",
"redux": "^5.0.1",
"redux-persist": "^6.0.0",
Expand Down
5 changes: 5 additions & 0 deletions public/icons/hide.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions public/icons/show.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions public/icons/sun-login.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 24 additions & 2 deletions src/components/FormBuilder/FormBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ const FormBuilder = <T,>({
}
};

const renderExtraSection = (formField: FormBuilderFormField, value: string) => {
if (!formField.extraSection) return null;
if (typeof formField.extraSection === 'function') {
return formField.extraSection(value);
}
return formField.extraSection;
};

return (
<form className={styles.container} onSubmit={handleSubmit(internalOnSubmit)}>
{formFields?.map((formField) => {
Expand All @@ -78,14 +86,28 @@ const FormBuilder = <T,>({
rules={buildReactHookFormRules(formField)}
name={formField.field}
render={({ field, fieldState: { error } }) => {
if (formField.customRender) {
return (
<div className={classNames(styles.inputContainer, formField.containerClassName)}>
{formField.customRender({
value: field.value,
onChange: field.onChange,
placeholder: formField.placeholder,
})}
{error && <span className={styles.errorText}>{error.message}</span>}
{renderExtraSection(formField, field.value)}
</div>
);
}

const inputFieldProps = {
key: formField.field,
value: field.value,
id: formField.field,
name: formField.field,
containerClassName: formField.containerClassName,
fieldSetLegend: formField.fieldSetLegend,
label: formField.label,
label: formField.label as string,
placeholder: formField.placeholder,
onChange: (val) => {
field.onChange(val);
Expand All @@ -107,7 +129,7 @@ const FormBuilder = <T,>({
<div className={classNames(styles.inputContainer, formField.containerClassName)}>
<InputField {...inputFieldProps} />
{error && <span className={styles.errorText}>{error.message}</span>}
{formField.extraSection && <div>{formField.extraSection}</div>}
{renderExtraSection(formField, field.value)}
</div>
);
}}
Expand Down
11 changes: 9 additions & 2 deletions src/components/FormBuilder/FormBuilderTypes.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import { ReactNode } from 'react';

import FieldRule from 'types/FieldRule';
import FormField from 'types/FormField';

export type FormBuilderFieldRule = Pick<FieldRule, 'type' | 'value'> & { errorMessage: string };
export type FormBuilderFormField = Pick<FormField, 'field' | 'type'> & {
defaultValue?: any;
label?: string | JSX.Element;
label?: string | ReactNode;
placeholder?: string;
rules?: FormBuilderFieldRule[];
containerClassName?: string;
checked?: boolean;
fieldSetLegend?: string;
onChange?: (value: unknown) => void;
extraSection?: JSX.Element;
extraSection?: ReactNode | ((value: string) => ReactNode);
customRender?: (props: {
value: string;
onChange: (value: string) => void;
placeholder?: string;
}) => ReactNode;
};
24 changes: 24 additions & 0 deletions src/components/Login/AuthHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { FC } from 'react';

import styles from './login.module.scss';

import QuranLogo from '@/icons/logo_main.svg';
import QRColoredLogo from '@/icons/qr-colored.svg';
import QuranReflectLogo from '@/icons/qr-logo.svg';

const AuthHeader: FC = () => {
return (
<>
<div className={styles.authLogos}>
<QuranLogo />
<div className={styles.qrLogos}>
<QRColoredLogo />
<QuranReflectLogo />
</div>
</div>
<hr className={styles.serviceDivider} />
</>
);
};

export default AuthHeader;
62 changes: 62 additions & 0 deletions src/components/Login/AuthTabs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { FC } from 'react';

import useTranslation from 'next-translate/useTranslation';

import AuthHeader from './AuthHeader';
import styles from './login.module.scss';
import SignInForm from './SignInForm';
import SignUpForm from './SignUpForm';

import Switch, { SwitchSize } from '@/dls/Switch/Switch';
import SignUpRequest from 'types/auth/SignUpRequest';

export enum AuthTab {
SignIn = 'signin',
SignUp = 'signup',
}

interface Props {
activeTab: AuthTab;
onTabChange: (tab: AuthTab) => void;
redirect?: string;
onSignUpSuccess: (data: SignUpRequest) => void;
}

const AuthTabs: FC<Props> = ({ activeTab, onTabChange, redirect, onSignUpSuccess }) => {
const { t } = useTranslation('login');

const items = [
{
name: t('sign-in'),
value: AuthTab.SignIn,
},
{
name: t('sign-up'),
value: AuthTab.SignUp,
},
];

return (
<div className={styles.authContainer}>
<AuthHeader />
<div className={styles.authTabs}>
<h1 className={styles.authTitle}>{t('sign-in-or-sign-up')}</h1>
<div className={styles.authSwitchContainer}>
<Switch
items={items}
selected={activeTab}
onSelect={onTabChange}
size={SwitchSize.Normal}
/>
</div>
{activeTab === AuthTab.SignIn ? (
<SignInForm redirect={redirect} />
) : (
<SignUpForm onSuccess={onSignUpSuccess} />
)}
</div>
</div>
);
};

export default AuthTabs;
2 changes: 1 addition & 1 deletion src/components/Login/CompleteSignupForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import useTranslation from 'next-translate/useTranslation';
import { useSWRConfig } from 'swr';

import buildFormBuilderFormField from '../FormBuilder/buildFormBuilderFormField';
import FormBuilder from '../FormBuilder/FormBuilder';

import styles from './CompleteSignupForm.module.scss';
import EmailVerificationForm from './EmailVerificationForm';

import FormBuilder from '@/components/FormBuilder/FormBuilder';
import { completeSignup } from '@/utils/auth/api';
import { makeUserProfileUrl } from '@/utils/auth/apiPaths';
import { logFormSubmission } from '@/utils/eventLogger';
Expand Down
Loading