From aacc0b7ecdfb6b489e2e0c4ad273335d44e76af6 Mon Sep 17 00:00:00 2001 From: kris liu Date: Fri, 12 Aug 2022 09:24:01 +0800 Subject: [PATCH] feat: add Login Page for Remote Frontend --- apps/storefront/src/App.tsx | 12 + apps/storefront/src/components/Layout.tsx | 5 + .../src/components/form/B3TextField.tsx | 81 ++-- .../src/components/spin/B3Sping.tsx | 4 +- apps/storefront/src/locales/en-US/users.ts | 15 + apps/storefront/src/main.css | 7 + apps/storefront/src/pages/index.tsx | 14 + .../src/pages/login/ForgotPassword.tsx | 145 +++++++ apps/storefront/src/pages/login/Login.tsx | 368 ++++++++++++++++++ apps/storefront/src/pages/login/LoginForm.tsx | 108 +++++ .../storefront/src/pages/login/LoginPanel.tsx | 73 ++++ .../src/pages/login/component/LoginWidget.tsx | 40 ++ apps/storefront/src/pages/login/config.ts | 153 ++++++++ apps/storefront/src/pages/login/index.ts | 7 + apps/storefront/src/pages/login/styled.ts | 53 +++ .../src/pages/registered/Registered.tsx | 6 + .../pages/registered/RegisteredBCToB2B.tsx | 2 +- .../storefront/src/pages/registered/config.ts | 40 +- .../src/shared/service/b2b/api/login.ts | 15 + .../shared/service/b2b/graphql/register.ts | 35 ++ .../src/shared/service/b2b/index.ts | 9 + .../src/shared/service/bc/api/login.ts | 11 + .../src/shared/service/bc/graphql/login.ts | 23 ++ .../storefront/src/shared/service/bc/index.ts | 10 + .../src/shared/service/request/b3Fetch.ts | 18 +- apps/storefront/src/utils/b3Storage.ts | 42 ++ apps/storefront/src/utils/index.ts | 12 + apps/storefront/src/utils/validatorRules.ts | 37 ++ 28 files changed, 1273 insertions(+), 72 deletions(-) create mode 100644 apps/storefront/src/pages/index.tsx create mode 100644 apps/storefront/src/pages/login/ForgotPassword.tsx create mode 100644 apps/storefront/src/pages/login/Login.tsx create mode 100644 apps/storefront/src/pages/login/LoginForm.tsx create mode 100644 apps/storefront/src/pages/login/LoginPanel.tsx create mode 100644 apps/storefront/src/pages/login/component/LoginWidget.tsx create mode 100644 apps/storefront/src/pages/login/config.ts create mode 100644 apps/storefront/src/pages/login/index.ts create mode 100644 apps/storefront/src/pages/login/styled.ts create mode 100644 apps/storefront/src/shared/service/b2b/api/login.ts create mode 100644 apps/storefront/src/shared/service/bc/api/login.ts create mode 100644 apps/storefront/src/shared/service/bc/graphql/login.ts create mode 100644 apps/storefront/src/utils/b3Storage.ts create mode 100644 apps/storefront/src/utils/validatorRules.ts diff --git a/apps/storefront/src/App.tsx b/apps/storefront/src/App.tsx index 6a6fc493..4329ddca 100644 --- a/apps/storefront/src/App.tsx +++ b/apps/storefront/src/App.tsx @@ -14,6 +14,10 @@ import { } from '@b3/hooks' import styled from '@emotion/styled' +import { + Login, + ForgotPassword, +} from '@/pages' import { Layout, RegisteredCloseButton, @@ -148,7 +152,15 @@ export default function App() { path="form" element={
} /> + } + /> + } + /> ) : null} diff --git a/apps/storefront/src/components/Layout.tsx b/apps/storefront/src/components/Layout.tsx index 44c1ecbd..52d0f2ab 100644 --- a/apps/storefront/src/components/Layout.tsx +++ b/apps/storefront/src/components/Layout.tsx @@ -91,6 +91,11 @@ export function Layout({ to: '/registeredbctob2b', icon: VerifiedUserIcon, }, + { + label: 'Login', + to: '/login', + icon: VerifiedUserIcon, + }, ].map(({ label, icon: Icon, to, diff --git a/apps/storefront/src/components/form/B3TextField.tsx b/apps/storefront/src/components/form/B3TextField.tsx index b58b3754..cefc7e9e 100644 --- a/apps/storefront/src/components/form/B3TextField.tsx +++ b/apps/storefront/src/components/form/B3TextField.tsx @@ -1,5 +1,6 @@ import { TextField, + Box, } from '@mui/material' import { Controller, @@ -37,6 +38,8 @@ export const B3TextField = ({ fullWidth, muiTextFieldProps, disabled, + labelName, + size, } = rest const b3Lang = useB3Lang() @@ -48,7 +51,7 @@ export const B3TextField = ({ defaultValue, rules: { required: required && b3Lang('intl.global.validate.required', { - label, + label: labelName || labelName, }), validate: validate && ((v: string) => validate(v, b3Lang)), }, @@ -63,9 +66,10 @@ export const B3TextField = ({ rows, disabled, multiline: fieldType === 'multiline', - variant: variant || 'filled', + variant, fullWidth: fullWidth || true, required, + size, } const inputProps = { @@ -101,36 +105,49 @@ export const B3TextField = ({ 'password', 'multiline', ].includes(fieldType) && ( - ( - fieldType === 'number' - ? ( - - ) - : ( - - ) - )} - /> + <> + { + labelName && ( + + {`${labelName} :`} + + ) + + } + ( + fieldType === 'number' + ? ( + + ) + : ( + + ) + )} + /> + ) } diff --git a/apps/storefront/src/components/spin/B3Sping.tsx b/apps/storefront/src/components/spin/B3Sping.tsx index 80b8bc1c..2028f48a 100644 --- a/apps/storefront/src/components/spin/B3Sping.tsx +++ b/apps/storefront/src/components/spin/B3Sping.tsx @@ -15,8 +15,8 @@ interface B3SpingProps { isSpinning: Boolean | undefined, children: ReactNode, tip?: string, - size?: Number & undefined, - thickness?: Number & undefined, + size?: number, + thickness?: number & undefined, isCloseLoading?: Boolean, } diff --git a/apps/storefront/src/locales/en-US/users.ts b/apps/storefront/src/locales/en-US/users.ts index 0d25bfef..ddfdf5cd 100644 --- a/apps/storefront/src/locales/en-US/users.ts +++ b/apps/storefront/src/locales/en-US/users.ts @@ -63,4 +63,19 @@ export default { // register components 'intl.user.register.RegisteredSingleCheckBox.label': 'Email me special promotions and updates', + + // login + 'intl.user.login.loginTipInfo.resetPassword': 'You must reset your password before you may log in again. Please check your email at {email} to reset your password', + 'intl.user.login.loginTipInfo.receivePassword': 'If the entered email address is associated with this store, you will receive a password reset email. If you don\'t receive this e-mail, please check your junk mail folder or contact us for further assistance.', + 'intl.user.login.loginTipInfo.accountsuccess': 'You\'ve been logged out of your account successfully.', + 'intl.user.login.loginTipInfo.accountincorrect': 'Your email address or password is incorrect. Please try again. If you\'ve forgotten your sign in details, just click the \'Forgot your password?\' link below.', + + 'intl.user.login.loginText.forgotPasswordText': 'Forgot your password ?', + 'intl.user.login.loginText.emailAddress': 'Email Address', + 'intl.user.login.loginText.password': 'Password', + + 'intl.user.forgot.forgotText.resetPassword': 'Reset Password', + 'intl.user.forgot.forgotText.requestEmail': 'Fill in your email below to request a new password. An email will be sent to the address below containing a link to verify your email address.', + 'intl.user.forgot.forgotText.resetPasswordBtn': 'Reset Password', + } diff --git a/apps/storefront/src/main.css b/apps/storefront/src/main.css index a249baea..5491289d 100644 --- a/apps/storefront/src/main.css +++ b/apps/storefront/src/main.css @@ -106,3 +106,10 @@ page-break-before: auto; page-break-inside: auto; } + +input[type=number]::-webkit-inner-spin-button, +input[type=number]::-webkit-outer-spin-button +{ + -webkit-appearance: none; + margin: 0; +} diff --git a/apps/storefront/src/pages/index.tsx b/apps/storefront/src/pages/index.tsx new file mode 100644 index 00000000..9b9781cb --- /dev/null +++ b/apps/storefront/src/pages/index.tsx @@ -0,0 +1,14 @@ +export { + Home, +} from './Home' +export { + Form, +} from './Form' + +export { + Login, + ForgotPassword, +} from './login' +export { + Registered, RegisteredBCToB2B, +} from './registered' diff --git a/apps/storefront/src/pages/login/ForgotPassword.tsx b/apps/storefront/src/pages/login/ForgotPassword.tsx new file mode 100644 index 00000000..a6f48096 --- /dev/null +++ b/apps/storefront/src/pages/login/ForgotPassword.tsx @@ -0,0 +1,145 @@ +import { + useState, +} from 'react' + +import { + Box, + Grid, +} from '@mui/material' + +import { + useForm, + SubmitHandler, +} from 'react-hook-form' + +import { + useB3Lang, +} from '@b3/lang' + +import { + useNavigate, +} from 'react-router-dom' + +import { + getForgotPasswordFields, + sendEmail, + LoginConfig, +} from './config' + +import { + B3CustomForm, + B3Sping, +} from '@/components' + +import { + B3ForgotButton, +} from './styled' + +const ForgotPassword = () => { + const [isLoading, setLoading] = useState(false) + const b3Lang = useB3Lang() + const forgotPasswordFields = getForgotPasswordFields(b3Lang) + + const { + control, + handleSubmit, + getValues, + formState: { + errors, + }, + setValue, + } = useForm({ + mode: 'onSubmit', + }) + + const navigate = useNavigate() + + const handleLoginClick: SubmitHandler = async (data) => { + setLoading(true) + const { + emailAddress, + } = data + try { + await sendEmail(emailAddress) + setLoading(false) + navigate('/login?loginFlag=2') + } catch (e) { + console.log(e) + } + } + + return ( + + + {b3Lang('intl.user.forgot.forgotText.resetPassword')} + + + {b3Lang('intl.user.forgot.forgotText.requestEmail')} + + + + + + + + + + + + + {b3Lang('intl.user.forgot.forgotText.resetPasswordBtn')} + + + + + + + + + + + ) +} + +export default ForgotPassword diff --git a/apps/storefront/src/pages/login/Login.tsx b/apps/storefront/src/pages/login/Login.tsx new file mode 100644 index 00000000..e38fae98 --- /dev/null +++ b/apps/storefront/src/pages/login/Login.tsx @@ -0,0 +1,368 @@ +import { + useEffect, + useState, +} from 'react' + +import { + ImageListItem, + Box, + Alert, +} from '@mui/material' + +import { + useB3Lang, +} from '@b3/lang' + +import { + useNavigate, + useLocation, +} from 'react-router-dom' + +import { + getB2BRegisterLogo, + getBCToken, + getBCForcePasswordReset, +} from '@/shared/service/b2b' + +import { + bcLogin, +} from '@/shared/service/bc' + +import { + B3SStorage, +} from '@/utils' + +import { + B3Sping, +} from '@/components/spin/B3Sping' + +import { + LoginContainer, LoginImage, +} from './styled' + +import { + getLogo, + LoginInfoInit, + LoginConfig, + getloginTokenInfo, + loginCheckout, + getLoginFlag, +} from './config' + +import LoginWidget from './component/LoginWidget' + +import LoginForm from './LoginForm' + +import LoginPanel from './LoginPanel' + +const initialLoginInfo = { + loginTitle: 'Sign In', + isShowWidgetHead: false, + isShowWidgetBody: false, + isShowWidgetFooter: false, + loginBtn: 'Sing in', + createAccountPanelTittle: 'Create Account Panel Title', + CreateAccountButtonText: 'Create Account', + btnColor: 'red', + widgetHeadText: '', + widgetBodyText: '', + widgetFooterText: '', + displayStoreLogo: false, +} + +export default function Login() { + const [isLoading, setLoading] = useState(true) + + const [logo, setLogo] = useState('') + const [flag, setLoginFlag] = useState('') + const [loginAccount, setLoginAccount] = useState({}) + const location = useLocation() + + const [loginInfo, setLoginInfo] = useState(initialLoginInfo) + + const navigate = useNavigate() + + const b3Lang = useB3Lang() + + useEffect(() => { + const init = async () => { + try { + const { + quoteConfig, + } = await getB2BRegisterLogo() + + // const { + // loginPageConfig: { + // value: { + // bottomHtmlRegionEnabled, + // bottomHtmlRegionHtml, + // createAccountPanelHtml, + // displayStoreLogo, + // pageTitle, + // primaryButtonColor, + // signInButtonText, + // createAccountButtonText, + // topHtmlRegionEnabled, + // topHtmlRegionHtml, + // }, + // }, + // } = await getB2BLoginPageConfig() + + // const Info = { + // loginTitle: pageTitle, + // loginBtn: signInButtonText, + // CreateAccountButtonText: createAccountButtonText, + // btnColor: primaryButtonColor, + // isShowWidgetHead: topHtmlRegionEnabled, + // widgetHeadText: topHtmlRegionHtml, + // // isShowWidgetBody: true, + // widgetBodyText: createAccountPanelHtml, + // isShowWidgetFooter: bottomHtmlRegionEnabled, + // widgetFooterText: bottomHtmlRegionHtml, + // displayStoreLogo, + // } + + const Info = { + loginTitle: 'Sign In', + loginBtn: 'Sing in', + CreateAccountButtonText: 'Create Account', + btnColor: 'blue', + isShowWidgetHead: true, + widgetHeadText: '
123
', + isShowWidgetBody: true, + widgetBodyText: '
456
', + isShowWidgetFooter: true, + widgetFooterText: '
789
', + displayStoreLogo: true, + } + + const registerLogo = getLogo(quoteConfig) + + const { + search, + } = location + + const loginFlag = getLoginFlag(search, 'loginFlag') + + if (loginFlag) setLoginFlag(loginFlag) + + setLoginInfo(Info) + setLogo(registerLogo) + setLoading(false) + } catch (e) { + // eslint-disable-next-line no-console + console.log(e) + } + } + + init() + }, []) + + const tipInfo = (loginFlag: string, email = '') => { + if (!loginFlag) return + if (loginFlag) { + let str = '' + switch (loginFlag) { + case '1': + str = b3Lang('intl.user.login.loginTipInfo.resetPassword', { + email, + }) + break + case '2': + str = b3Lang('intl.user.login.loginTipInfo.receivePassword') + break + case '3': + str = b3Lang('intl.user.login.loginTipInfo.accountsuccess') + break + case '4': + str = b3Lang('intl.user.login.loginTipInfo.accountincorrect') + break + default: + str = '' + } + return str + } + } + + const getforcePasswordReset = async (email: string) => { + const { + companyUserInfo: { + userInfo: { + forcePasswordReset, + }, + }, + } = await getBCForcePasswordReset(email) + + if (forcePasswordReset) { + setLoginFlag('1') + } else { + setLoginFlag('4') + } + } + + const handleLoginSubmit = async (data: LoginConfig) => { + setLoading(true) + setLoginAccount(data) + + const { + search, + } = location + if (search.includes('isCheckout')) { + try { + await loginCheckout(data) + window.location.reload() + } catch (error) { + console.log(error) + getforcePasswordReset(data.emailAddress) + } + } else { + const loginTokenInfo = getloginTokenInfo() + const { + data: { + token, + }, + } = await getBCToken(loginTokenInfo) + B3SStorage.set('BcToken', token) + + const getBCFieldsValue = { + email: data.emailAddress, + pass: data.password, + } + const { + data: bcData, errors, + } = await bcLogin(getBCFieldsValue) + if (errors?.length || !bcData) { + getforcePasswordReset(data.emailAddress) + } else { + window.location.href = '/' + } + + setLoading(false) + } + } + + const handleCreateAccountSubmit = () => { + navigate('/registered') + } + + const gotoForgotPassword = () => { + navigate('/forgotpassword') + } + + return ( + + + { + logo && loginInfo?.displayStoreLogo && ( + + + {b3Lang('intl.user.register.tips.registerLogo')} + + + ) + } + + { + loginInfo?.loginTitle && ( + + {loginInfo.loginTitle} + + ) + } + + { + flag && ( + + + {tipInfo(flag, loginAccount?.emailAddress || '')} + + + + ) + } + + + + + + + + + + + + + + + + + + + + + + ) +} diff --git a/apps/storefront/src/pages/login/LoginForm.tsx b/apps/storefront/src/pages/login/LoginForm.tsx new file mode 100644 index 00000000..01c9551d --- /dev/null +++ b/apps/storefront/src/pages/login/LoginForm.tsx @@ -0,0 +1,108 @@ +import { + useForm, + SubmitHandler, +} from 'react-hook-form' + +import { + useB3Lang, +} from '@b3/lang' + +import { + Box, +} from '@mui/material' + +import { + B3CustomForm, +} from '@/components' + +import { + LoginInfoInit, + getLoginFields, + LoginConfig, +} from './config' + +import { + B3Button, +} from './styled' + +interface LoginFormProps { + loginInfo: Partial; + handleLoginSubmit: (data: LoginConfig) => void; + gotoForgotPassword: () => void; +} + +const LoginForm = (props: LoginFormProps) => { + const { + loginInfo, + handleLoginSubmit, + gotoForgotPassword, + } = props + + const b3Lang = useB3Lang() + + const loginFields = getLoginFields(b3Lang) + + const { + loginBtn, + btnColor, + } = loginInfo + + const { + control, + handleSubmit, + getValues, + formState: { + errors, + }, + setValue, + } = useForm({ + mode: 'onSubmit', + }) + + const handleLoginClick: SubmitHandler = (data) => { + handleLoginSubmit(data) + } + + return ( + + + + + + { loginBtn } + + gotoForgotPassword()} + > + {b3Lang('intl.user.login.loginText.forgotPasswordText')} + + + + + ) +} + +export default LoginForm diff --git a/apps/storefront/src/pages/login/LoginPanel.tsx b/apps/storefront/src/pages/login/LoginPanel.tsx new file mode 100644 index 00000000..cd506470 --- /dev/null +++ b/apps/storefront/src/pages/login/LoginPanel.tsx @@ -0,0 +1,73 @@ +import { + Box, +} from '@mui/material' +import LoginWidget from './component/LoginWidget' + +import { + LoginInfoInit, +} from './config' + +import { + B3Button, +} from './styled' + +interface LoginPanelProps { + loginInfo: Partial; + handleSubmit?: () => void; +} + +const LoginPanel = (props: LoginPanelProps) => { + const { + loginInfo, + handleSubmit, + } = props + + const { + widgetBodyText = '', + CreateAccountButtonText, + btnColor, + } = loginInfo + + return ( + + {/* + {loginInfo.createAccountPanelTittle} + */} + + + + + {CreateAccountButtonText} + + + + ) +} + +export default LoginPanel diff --git a/apps/storefront/src/pages/login/component/LoginWidget.tsx b/apps/storefront/src/pages/login/component/LoginWidget.tsx new file mode 100644 index 00000000..cadf81e7 --- /dev/null +++ b/apps/storefront/src/pages/login/component/LoginWidget.tsx @@ -0,0 +1,40 @@ +import { + Box, +} from '@mui/material' + +interface StyleWidget { + [key:string]: string; +} + +interface LoginWidgetProps { + isVisible: boolean, + sx: StyleWidget, + html: string, +} + +const LoginWidget = (props: LoginWidgetProps) => { + const { + isVisible, + html, + sx, + } = props + + return ( + <> + { + isVisible && ( + + ) + } + + ) +} + +export default LoginWidget diff --git a/apps/storefront/src/pages/login/config.ts b/apps/storefront/src/pages/login/config.ts new file mode 100644 index 00000000..2250729b --- /dev/null +++ b/apps/storefront/src/pages/login/config.ts @@ -0,0 +1,153 @@ +/* eslint-disable no-console */ +import { + B3Lang, +} from '@b3/lang' +import { + validatorRules, + storeHash, +} from '@/utils' + +import { + bcBaseUrl, +} from '@/utils/basicConfig' + +export interface QuoteConfig { + [key: string]: string +} + +export type LoginConfig = { + emailAddress: string, + password?: string, +} + +export interface LoginInfoInit { + isShowWidgetHead: boolean, + isShowWidgetBody?: boolean, + isShowWidgetFooter: boolean, + loginTitle: string, + loginBtn?: string, + createAccountPanelTittle?: string, + CreateAccountButtonText: string, + btnColor: string, + widgetHeadText: string, + widgetBodyText: string, + widgetFooterText: string, + displayStoreLogo: boolean, +} + +export const getLogo = (quoteConfig:Array): string => { + const item:Array = quoteConfig.filter((list:QuoteConfig) => list.key === 'quote_logo') + + return item[0].isEnabled +} + +export interface B3ButtonProps { + btnColor?: string +} + +export interface ValidateOptions extends Record { + max?: string | Number, + min?: string | Number, +} + +export const getForgotPasswordFields = (lang: B3Lang) => [ + { + name: 'emailAddress', + label: '', + labelName: lang('intl.user.login.loginText.emailAddress'), + required: true, + default: '', + fieldType: 'text', + xs: 12, + size: 'small', + validate: validatorRules(['email']), + }, +] + +export const getLoginFields = (lang: B3Lang) => [ + { + name: 'emailAddress', + label: '', + labelName: lang('intl.user.login.loginText.emailAddress'), + required: true, + default: '', + fieldType: 'text', + xs: 12, + }, + { + name: 'password', + label: '', + labelName: lang('intl.user.login.loginText.password'), + required: true, + default: '', + fieldType: 'password', + xs: 12, + }, +] + +export const loginCheckout = (data: LoginConfig) => { + const requestOptions = { + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + body: JSON.stringify({ + email: data.emailAddress, + password: data.password, + }), + } + + return fetch(`${bcBaseUrl}/internalapi/v1/checkout/customer`, requestOptions) + .then((response) => response.text()) + .then((result) => console.log(result)) + .catch((error) => console.log('error', error)) +} + +export const sendEmail = (emailAddress: string) => { + const myHeaders = new Headers() + + const urlencoded = new URLSearchParams() + urlencoded.append('email', emailAddress) + + const requestOptions: any = { + method: 'POST', + headers: myHeaders, + body: urlencoded, + redirect: 'follow', + } + + return fetch(`${bcBaseUrl}/login.php?action=send_password_email`, requestOptions) + .then((response) => response.text()) + .then((result) => console.log(result)) + .catch((error) => console.log('error', error)) +} + +export const getloginTokenInfo = () => { + const { + origin, + } = window.location + const data = { + storeHash, + method: 'post', + url: '/v3/storefront/api-token', + params: {}, + data: { + channel_id: 1, + expires_at: 1866896353, + allowed_cors_origins: [ + `${origin}`, + ], + }, + } + + return data +} + +export const getLoginFlag = (search:string, key:string) => { + if (!search) { + return '' + } + const searchParams = new URLSearchParams(search) + + return searchParams.get(key) +} diff --git a/apps/storefront/src/pages/login/index.ts b/apps/storefront/src/pages/login/index.ts new file mode 100644 index 00000000..d8a66ce1 --- /dev/null +++ b/apps/storefront/src/pages/login/index.ts @@ -0,0 +1,7 @@ +import Login from './Login' +import ForgotPassword from './ForgotPassword' + +export { + Login, + ForgotPassword, +} diff --git a/apps/storefront/src/pages/login/styled.ts b/apps/storefront/src/pages/login/styled.ts new file mode 100644 index 00000000..5aec031e --- /dev/null +++ b/apps/storefront/src/pages/login/styled.ts @@ -0,0 +1,53 @@ +import { + ComponentType, +} from 'react' + +import { + styled, +} from '@mui/material/styles' + +import Button, { + ButtonProps, +} from '@mui/material/Button' + +import { + B3ButtonProps, +} from './config' + +type StyledButtonProps = ButtonProps & B3ButtonProps + +export const LoginContainer = styled('div')({ + padding: '20px 20px', +}) + +export const LoginImage = styled('div')({ + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + width: '100%', + height: '130px', +}) + +// export const B3Button = styled(Button)(({ +// btnColor, +// }: B3ButtonProps) => ({ +// backgroundColor: btnColor, +// '&:hover': { +// backgroundColor: btnColor, +// }, +// })) + +export const B3Button = styled(Button, { + shouldForwardProp: (props) => + props !== 'btnColor', +})` + backgroundColor: ${(props: StyledButtonProps) => props.btnColor} + '&:hover': { + backgroundColor: ${(props: StyledButtonProps) => props.btnColor} + }, +`as ComponentType + +export const B3ForgotButton = styled(Button)({ + height: '40px', +}) diff --git a/apps/storefront/src/pages/registered/Registered.tsx b/apps/storefront/src/pages/registered/Registered.tsx index 5171ff87..85406e90 100644 --- a/apps/storefront/src/pages/registered/Registered.tsx +++ b/apps/storefront/src/pages/registered/Registered.tsx @@ -25,6 +25,10 @@ import { getB2BAccountFormFields, } from '@/shared/service/b2b' +import { + getBCRegisterCustomFields, +} from '@/shared/service/bc' + import RegisteredStep from './RegisteredStep' import RegisterContent from './RegisterContent' @@ -89,6 +93,8 @@ export default function Registered(props: RegisteredProps) { }) } + await getBCRegisterCustomFields() + const accountFormAllFields = formType.map((item: number) => getB2BAccountFormFields(item)) const accountFormFields = await Promise.all(accountFormAllFields) diff --git a/apps/storefront/src/pages/registered/RegisteredBCToB2B.tsx b/apps/storefront/src/pages/registered/RegisteredBCToB2B.tsx index 72359372..d601f1a4 100644 --- a/apps/storefront/src/pages/registered/RegisteredBCToB2B.tsx +++ b/apps/storefront/src/pages/registered/RegisteredBCToB2B.tsx @@ -201,7 +201,7 @@ export default function RegisteredBCToB2B() { const handleNext = (event: MouseEvent) => { handleSubmit(async (data: CustomFieldItems) => { // eslint-disable-next-line no-console - console.log(data, 'data') + console.log(data) })(event) } diff --git a/apps/storefront/src/pages/registered/config.ts b/apps/storefront/src/pages/registered/config.ts index e62fe887..69a4acf2 100644 --- a/apps/storefront/src/pages/registered/config.ts +++ b/apps/storefront/src/pages/registered/config.ts @@ -7,8 +7,8 @@ import { } from 'date-fns' import { - re, -} from '../../constants' + validatorRules, +} from '@/utils' const inputFormat = 'yyyy-MM-dd' @@ -19,24 +19,21 @@ export interface QuoteConfig { [key: string]: string } -export interface ValidateOptions { +export interface ValidateOptions extends Record { max?: string | Number, min?: string | Number, - [key: string]: any } -export interface RegisterFields { +export interface RegisterFields extends Record { name: string, label?: string, required?: Boolean, fieldType?: string, default?: string | Array | number, - [key: string]: any } -interface ValidateOptionItems { +interface ValidateOptionItems extends Record { max?: number, min?: number, - [key: string]: any } export type ContactInformationItems = Array @@ -53,7 +50,6 @@ interface AccountFormFieldsItemsValueConfigs { type?: string, custom?: boolean id: string | number - // [key: string]: string } interface AccountFormFieldsItems { @@ -107,31 +103,6 @@ export const Base64 = { }, } -export const validatorRules = (validateRuleTypes: string[], options?: ValidateOptions) => (val: string, b3lang: B3Lang) => { - let str = '' - validateRuleTypes.forEach((item: string) => { - if (item === 'email' && val && !re.email.test(val)) { - str = b3lang('intl.user.register.validatorRules.email') - } - if (item === 'phone' && val && !re.phone.test(val)) { - str = b3lang('intl.user.register.validatorRules.phoneNumber') - } - if (item === 'number' && val && !re.number.test(val)) { - str = b3lang('intl.user.register.validatorRules.number') - } - if (item === 'max' && options?.max && +options.max < +val) { - str = b3lang('intl.user.register.validatorRules.max', { - max: options.max, - }) - } - - if (item === 'password' && val && !re.password.test(val)) { - str = b3lang('intl.user.register.validatorRules.passwords') - } - }) - if (str) return str -} - const fieldsType = { text: ['text', 'number', 'password', 'multiline'], @@ -303,6 +274,7 @@ export const conversionItemFormat = (FormFields: AccountFormFieldsList) => { obj.visible = item.visible obj.label = item.labelName obj.custom = obj.custom || item?.custom + obj.variant = 'filled' if (obj.fieldType === 'date' && !obj.default) { obj.default = format(new Date(), inputFormat) diff --git a/apps/storefront/src/shared/service/b2b/api/login.ts b/apps/storefront/src/shared/service/b2b/api/login.ts new file mode 100644 index 00000000..a52cf52c --- /dev/null +++ b/apps/storefront/src/shared/service/b2b/api/login.ts @@ -0,0 +1,15 @@ +import { + B3Request, +} from '../../request/b3Fetch' +import { + RequestType, +} from '../../request/base' +// import { +// storeHash, +// } from '../../../../utils/basicConfig' + +interface CustomFieldItems { + [key: string]: any +} + +export const getBCToken = (data: CustomFieldItems): CustomFieldItems => B3Request.post('/api/v2/proxy', RequestType.B2BRest, data) diff --git a/apps/storefront/src/shared/service/b2b/graphql/register.ts b/apps/storefront/src/shared/service/b2b/graphql/register.ts index 54d942cb..1222b11a 100644 --- a/apps/storefront/src/shared/service/b2b/graphql/register.ts +++ b/apps/storefront/src/shared/service/b2b/graphql/register.ts @@ -109,6 +109,33 @@ const createCompanyUser = (data: any) => `mutation{ } }` +const getLoginPageConfig = () => `{ + loginPageConfig(storeHash: "${storeHash}") { + id + value{ + displayStoreLogo + pageTitle + signInButtonText + createAccountButtonText + primaryButtonColor + createAccountPanelHtml + topHtmlRegionHtml + topHtmlRegionEnabled + bottomHtmlRegionHtml + bottomHtmlRegionEnabled + } + } +}` + +const getForcePasswordReset = (email: string) => `{ + companyUserInfo(storeHash: "${storeHash}", email: "${email}"){ + userType + userInfo { + forcePasswordReset + } + } +}` + export const getB2BAccountFormFields = (type: number): CustomFieldItems => B3Request.graphqlB2B({ query: getAccountFormFields(type), }) @@ -136,3 +163,11 @@ export const createB2BCompanyUser = (data: CustomFieldItems): CustomFieldItems = export const storeB2BBasicInfo = (): CustomFieldItems => B3Request.graphqlB2B({ query: storeBasicInfo(), }) + +export const getB2BLoginPageConfig = (): CustomFieldItems => B3Request.graphqlB2B({ + query: getLoginPageConfig(), +}) + +export const getBCForcePasswordReset = (email:string): CustomFieldItems => B3Request.graphqlB2B({ + query: getForcePasswordReset(email), +}) diff --git a/apps/storefront/src/shared/service/b2b/index.ts b/apps/storefront/src/shared/service/b2b/index.ts index 99407be7..fabe7001 100644 --- a/apps/storefront/src/shared/service/b2b/index.ts +++ b/apps/storefront/src/shared/service/b2b/index.ts @@ -6,6 +6,8 @@ import { createB2BCompanyUser, storeB2BBasicInfo, getB2BAccountFormFields, + getB2BLoginPageConfig, + getBCForcePasswordReset, } from './graphql/register' import { @@ -17,6 +19,10 @@ import { validateBCCompanyExtraFields, } from './api/register' +import { + getBCToken, +} from './api/login' + export { getB2BRegisterCustomFields, getB2BRegisterLogo, @@ -28,4 +34,7 @@ export { storeB2BBasicInfo, validateBCCompanyExtraFields, getB2BAccountFormFields, + getBCToken, + getB2BLoginPageConfig, + getBCForcePasswordReset, } diff --git a/apps/storefront/src/shared/service/bc/api/login.ts b/apps/storefront/src/shared/service/bc/api/login.ts new file mode 100644 index 00000000..92e765e5 --- /dev/null +++ b/apps/storefront/src/shared/service/bc/api/login.ts @@ -0,0 +1,11 @@ +import { + B3Request, +} from '../../request/b3Fetch' +import { + RequestType, +} from '../../request/base' +import { + bcBaseUrl, +} from '../../../../utils/basicConfig' + +export const getBCForgotPassword = (data: any): any => B3Request.post(`${bcBaseUrl}/login.php?action=send_password_email`, RequestType.BCRest, data) diff --git a/apps/storefront/src/shared/service/bc/graphql/login.ts b/apps/storefront/src/shared/service/bc/graphql/login.ts new file mode 100644 index 00000000..cf5bd7bb --- /dev/null +++ b/apps/storefront/src/shared/service/bc/graphql/login.ts @@ -0,0 +1,23 @@ +import { + B3Request, +} from '../../request/b3Fetch' + +// import { +// convertArrayToGraphql, +// storeHash, +// } from '../../../../utils' + +interface CustomFieldItems { + [key: string]: any +} + +const getbcLogin = () => `mutation Login($email: String!, $pass: String!) { + login(email: $email, password: $pass) { + result + } +}` + +export const bcLogin = (data: CustomFieldItems): CustomFieldItems => B3Request.graphqlBC({ + query: getbcLogin(), + variables: data, +}) diff --git a/apps/storefront/src/shared/service/bc/index.ts b/apps/storefront/src/shared/service/bc/index.ts index 33f13441..1840d587 100644 --- a/apps/storefront/src/shared/service/bc/index.ts +++ b/apps/storefront/src/shared/service/bc/index.ts @@ -2,6 +2,16 @@ import { getBCRegisterCustomFields, } from './api/register' +import { + getBCForgotPassword, +} from './api/login' + +import { + bcLogin, +} from './graphql/login' + export { getBCRegisterCustomFields, + getBCForgotPassword, + bcLogin, } diff --git a/apps/storefront/src/shared/service/request/b3Fetch.ts b/apps/storefront/src/shared/service/request/b3Fetch.ts index 9eb0a6e9..f20c1f0e 100644 --- a/apps/storefront/src/shared/service/request/b3Fetch.ts +++ b/apps/storefront/src/shared/service/request/b3Fetch.ts @@ -8,6 +8,14 @@ import { queryParse, } from './base' +import { + B3SStorage, +} from '@/utils' + +import { + bcBaseUrl, +} from '@/utils/basicConfig' + /** * config User-defined configuration items * @param withoutCheck Do not use the default interface status verification, directly return response @@ -71,13 +79,14 @@ function graphqlRequest(type: string, data: T, config?: Y) { method: 'POST', headers: { 'content-type': 'application/json', + ...config, }, - ...config, body: JSON.stringify(data), } + const graphqlB2BUrl = `${B2B_BASIC_URL}/graphql` - const url = type === RequestType.B2BGraphql ? graphqlB2BUrl : '' + const url = type === RequestType.B2BGraphql ? graphqlB2BUrl : `${bcBaseUrl}/graphql` return b3Fetch(url, init, type) } @@ -87,7 +96,10 @@ export const B3Request = { return graphqlRequest(RequestType.B2BGraphql, data) }, graphqlBC: function post(data: T) { - return graphqlRequest(RequestType.BCGraphql, data) + const config = { + Authorization: `Bearer ${B3SStorage.get('BcToken') || ''}`, + } + return graphqlRequest(RequestType.BCGraphql, data, config) }, get: function get(url: string, type: string, data?: T) { if (data) { diff --git a/apps/storefront/src/utils/b3Storage.ts b/apps/storefront/src/utils/b3Storage.ts new file mode 100644 index 00000000..0e7d38de --- /dev/null +++ b/apps/storefront/src/utils/b3Storage.ts @@ -0,0 +1,42 @@ +enum StorageType { + l = 'localStorage', + s = 'sessionStorage' +} + +class MyStorage { + storage: Storage + + constructor(type: StorageType) { + this.storage = type === StorageType.l ? window.localStorage : window.sessionStorage + } + + set( + key: string, + value: any, + ) { + const data = JSON.stringify(value) + this.storage.setItem(key, data) + } + + get(key: string) { + const value = this.storage.getItem(key) + if (value) { + return JSON.parse(value) + } + } + + delete(key: string) { + this.storage.removeItem(key) + } + + clear() { + this.storage.clear() + } +} + +const B3LStorage = new MyStorage(StorageType.l) +const B3SStorage = new MyStorage(StorageType.s) + +export { + B3SStorage, B3LStorage, +} diff --git a/apps/storefront/src/utils/index.ts b/apps/storefront/src/utils/index.ts index 5ce4d726..4fda399a 100644 --- a/apps/storefront/src/utils/index.ts +++ b/apps/storefront/src/utils/index.ts @@ -8,9 +8,21 @@ import { captchaSetkey, } from './basicConfig' +import { + validatorRules, +} from './validatorRules' + +import { + B3LStorage, + B3SStorage, +} from './b3Storage' + export { convertArrayToGraphql, convertObjectToGraphql, storeHash, captchaSetkey, + validatorRules, + B3LStorage, + B3SStorage, } diff --git a/apps/storefront/src/utils/validatorRules.ts b/apps/storefront/src/utils/validatorRules.ts new file mode 100644 index 00000000..8e3bcdf1 --- /dev/null +++ b/apps/storefront/src/utils/validatorRules.ts @@ -0,0 +1,37 @@ +import { + B3Lang, +} from '@b3/lang' + +import { + re, +} from '../constants' + +export interface ValidateOptions extends Record { + max?: string | Number, + min?: string | Number, +} + +export const validatorRules = (validateRuleTypes: string[], options?: ValidateOptions) => (val: string, b3lang: B3Lang) => { + let str = '' + validateRuleTypes.forEach((item: string) => { + if (item === 'email' && val && !re.email.test(val)) { + str = b3lang('intl.user.register.validatorRules.email') + } + if (item === 'phone' && val && !re.phone.test(val)) { + str = b3lang('intl.user.register.validatorRules.phoneNumber') + } + if (item === 'number' && val && !re.number.test(val)) { + str = b3lang('intl.user.register.validatorRules.number') + } + if (item === 'max' && options?.max && +options.max < +val) { + str = b3lang('intl.user.register.validatorRules.max', { + max: options.max, + }) + } + + if (item === 'password' && val && !re.password.test(val)) { + str = b3lang('intl.user.register.validatorRules.passwords') + } + }) + if (str) return str +}