diff --git a/CODEOWNERS b/CODEOWNERS
index 0eaa9cee..369c50a4 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -1 +1 @@
-* @bc-micah @LeoChowChina @libruce
\ No newline at end of file
+* @B3BC/buyer-portal
diff --git a/README.md b/README.md
index 63a25cd1..4ce49d9a 100644
--- a/README.md
+++ b/README.md
@@ -1,23 +1,27 @@
-
A monorepo frontend application designed for the BigCommerce B2B Edition Buyer portal. It's built using Turborepo, TypeScript, and React.
## Index
-- [Prerequisites](#-prerequisites)
-- [Core Technologies](#-core-technologies)
-- [Workspaces](#-workspaces)
-- [Tools and Libraries](#-tools-and-libraries)
-- [System Setup](#-system-setup)
-- [Local Development](#-local-development)
+- [Index](#index)
+- [☑ Prerequisites](#-prerequisites)
+ - [Step 1: Access the Storefronts Manager](#step-1-access-the-storefronts-manager)
+ - [Step 2: Enable B2B on Your Channel](#step-2-enable-b2b-on-your-channel)
+ - [Step 3: Contact Us for Additional Support](#step-3-contact-us-for-additional-support)
+- [🚀 Core Technologies](#-core-technologies)
+- [📦 Workspaces](#-workspaces)
+- [🛠 Tools and Libraries](#-tools-and-libraries)
+- [🛠 System Setup](#-system-setup)
+- [⚙ Local Development](#-local-development)
- [Running Project Locally](#running-project-locally)
-- [Contribution](#-contribution)
-- [Contact & Support](#-contact--support)
+- [🤝 Contribution](#-contribution)
+- [📞 Contact \& Support](#-contact--support)
## ☑ Prerequisites
Before you begin, ensure you have the BigCommerce B2B Edition App installed. To set up your storefront with B2B capabilities, follow the steps below:
### Step 1: Access the Storefronts Manager
+
After installing the B2B Edition App, go to the app's dashboard and select the 'Storefronts' section.
@@ -28,6 +32,7 @@ Choose the channel where you wish to enable B2B functionality. Initially, B2B fe
### Step 3: Contact Us for Additional Support
+
For assistance with activating the remote buyer portal or to inquire about multi-storefront support, which allows you to utilize B2B features across multiple channels, please reach out to our team at b2b@bigcommerce.com, or raise an issue right here in this repository.
## 🚀 Core Technologies
@@ -80,6 +85,8 @@ For assistance with activating the remote buyer portal or to inquire about multi
- `VITE_B2B_CLIENT_ID`: The client ID of the BigCommerce App from the [developer portal](https://devtools.bigcommerce.com/).
- `VITE_LOCAL_DEBUG`: Set to "FALSE". This is for connecting our local development with the B2B Edition GraphQL API.
+Environment variables have been updated so you can run your UI directly into production storefronts.
+
6. Start the development server: `yarn RUN dev`.
## Running Project Locally
diff --git a/apps/storefront/.env-example b/apps/storefront/.env-example
index 1263e5ab..1199a9ac 100644
--- a/apps/storefront/.env-example
+++ b/apps/storefront/.env-example
@@ -1,9 +1,26 @@
-VITE_B2B_URL=http://localhost:9000
-VITE_B2B_SOCKET_URL=http://localhost:9000
-VITE_TRANSLATION_SERVICE_URL=http://localhost:5000
+# URL of the B2B API, if doing local development, this should be the URL of the local B2B API with its own port
+VITE_B2B_URL=https://api.bundleb2b.net
+
+# URL of the B2B Socket - this should be the same as the B2B API URL
+VITE_B2B_SOCKET_URL=https://api.bundleb2b.net
+
+# URL of the B2B Translation Service - if doing local development, try with localhost:5000 or check the service URL
+VITE_TRANSLATION_SERVICE_URL=https://api.bundleb2b.net
+
+# CHANNEL ID of the storefront, if your store is multi-storefront, you can use this to specify the storefront - otherwise leave as 1
VITE_CHANNEL_ID=1
+
+# Store hash of the storefront, this is the unique identifier of the storefront
VITE_STORE_HASH=store_hash
+
+# Captcha Site Key for the storefront
VITE_CATPCHA_SETKEY=captcha_setkey
+
+# Client ID issued by B2B Edition for the storefront
VITE_B2B_CLIENT_ID=client_id
-VITE_LOCAL_DEBUG="FALSE"
-VITE_LOCAL_GRAPHQL_ORIGIN="https://b2b-tunnel-.bundleb2b.net"
+
+# Set this to TRUE to debug in your default storefront
+VITE_LOCAL_DEBUG=FALSE
+
+# URL where the GraphQL is hosted, usually the same one as B2B_URL_API. If the GraphQL API is hosted locally, set this to the local URL
+VITE_LOCAL_GRAPHQL_ORIGIN=https://api.bundleb2b.net
diff --git a/apps/storefront/src/App.tsx b/apps/storefront/src/App.tsx
index b8d0c7ed..79b50574 100644
--- a/apps/storefront/src/App.tsx
+++ b/apps/storefront/src/App.tsx
@@ -316,6 +316,15 @@ export default function App() {
useEffect(() => {
const { hash } = window.location
+ if (!hash.includes('login') && !hash.includes('register')) {
+ const recordOpenHash = isOpen ? hash : ''
+ storeDispatch(
+ setGlabolCommonState({
+ recordOpenHash,
+ })
+ )
+ }
+
if (isOpen && hash === '#/') {
setOpenPage({
isOpen: false,
diff --git a/apps/storefront/src/components/B3CustomForm.tsx b/apps/storefront/src/components/B3CustomForm.tsx
index 520b2a69..ad493c62 100644
--- a/apps/storefront/src/components/B3CustomForm.tsx
+++ b/apps/storefront/src/components/B3CustomForm.tsx
@@ -14,7 +14,7 @@ import {
} from './form'
export default function B3CustomForm(props: B3UI.B3CustomFormProps) {
- const { formFields, errors, control, getValues, setValue } = props
+ const { formFields, errors, control, getValues, setValue, setError } = props
const renderFormFields = (fields: any) =>
fields.map((field: B3UI.B3CustomFormValue) => {
@@ -75,6 +75,7 @@ export default function B3CustomForm(props: B3UI.B3CustomFormProps) {
errors={errors}
control={control}
setValue={setValue}
+ setError={setError}
/>
)}
{['rectangle'].includes(fieldType) && (
diff --git a/apps/storefront/src/components/B3StoreContainer.tsx b/apps/storefront/src/components/B3StoreContainer.tsx
index 99c8b6a4..2409f71c 100644
--- a/apps/storefront/src/components/B3StoreContainer.tsx
+++ b/apps/storefront/src/components/B3StoreContainer.tsx
@@ -39,7 +39,7 @@ export default function B3StoreContainer(props: B3StoreContainerProps) {
const getStoreBasicInfo = async () => {
if (
window.location.pathname.includes('account.php') ||
- window.location.hash
+ (window.location.hash && window.location.hash !== '#/')
) {
showPageMask(dispatch, true)
}
diff --git a/apps/storefront/src/components/form/B3ControlFileUpload.tsx b/apps/storefront/src/components/form/B3ControlFileUpload.tsx
index a3a370a8..85c5c45c 100644
--- a/apps/storefront/src/components/form/B3ControlFileUpload.tsx
+++ b/apps/storefront/src/components/form/B3ControlFileUpload.tsx
@@ -1,3 +1,4 @@
+import { useState } from 'react'
import { DropzoneArea, FileObject, PreviewIconProps } from 'react-mui-dropzone'
import { useB3Lang } from '@b3/lang'
import CloudUploadOutlinedIcon from '@mui/icons-material/CloudUploadOutlined'
@@ -5,7 +6,8 @@ import DescriptionRounded from '@mui/icons-material/DescriptionRounded'
import ImageRoundedIcon from '@mui/icons-material/ImageRounded'
import InsertDriveFileRoundedIcon from '@mui/icons-material/InsertDriveFileRounded'
import PictureAsPdfRoundedIcon from '@mui/icons-material/PictureAsPdfRounded'
-import { FormLabel } from '@mui/material'
+import { FormLabel, Typography } from '@mui/material'
+import isEmpty from 'lodash-es/isEmpty'
import { FILE_UPLOAD_ACCEPT_TYPE } from '../../constants'
@@ -43,6 +45,8 @@ interface FileUploadProps extends B3UI.B3UIProps {
previewText?: string
default?: File[]
labelColor?: string
+ errors?: CustomFieldItems
+ required?: boolean
}
const getMaxFileSizeLabel = (maxSize: number) => {
@@ -70,7 +74,12 @@ export default function B3ControlFileUpload(props: FileUploadProps) {
setValue,
label,
labelColor = 'text.primary',
+ required,
+ errors = {},
+ setError,
+ control,
} = props
+ const [deleteCount, setDeleteCount] = useState(0)
const getRejectMessage = (
rejectedFile: File,
@@ -107,6 +116,23 @@ export default function B3ControlFileUpload(props: FileUploadProps) {
})
const handleFilesChange = (files: File[]) => {
+ if (deleteCount > 0 && files.length === 0 && required) {
+ setError(name, {
+ type: 'required',
+ message: b3Lang('global.validate.required', {
+ label,
+ }),
+ })
+ setDeleteCount(0)
+ }
+ if (files.length > 0 && !isEmpty(errors)) {
+ const cError = errors[name]
+ if (!isEmpty(cError)) {
+ delete errors[name]
+ // eslint-disable-next-line no-underscore-dangle
+ control?._setErrors(errors)
+ }
+ }
if (setValue) {
setValue(name, files)
}
@@ -119,10 +145,10 @@ export default function B3ControlFileUpload(props: FileUploadProps) {
sx={{
marginBottom: '5px',
display: 'block',
- color: labelColor,
+ color: (errors as any)[name] ? '#d32f2f' : labelColor,
}}
>
- {label}
+ {`${label} ${required ? '*' : ''}`}
)}
@@ -142,8 +168,22 @@ export default function B3ControlFileUpload(props: FileUploadProps) {
dropzoneText={dropzoneText}
previewText={previewText}
onChange={handleFilesChange}
+ onDelete={() => setDeleteCount(deleteCount + 1)}
/>
+ {(errors as any)[name] ? (
+
+ {(errors as any)[name].message}
+
+ ) : null}
>
) : null
}
diff --git a/apps/storefront/src/components/form/B3ControlTextField.tsx b/apps/storefront/src/components/form/B3ControlTextField.tsx
index 0ef0d326..d8695c53 100644
--- a/apps/storefront/src/components/form/B3ControlTextField.tsx
+++ b/apps/storefront/src/components/form/B3ControlTextField.tsx
@@ -14,6 +14,7 @@ export default function B3ControlTextField({
}: Form.B3UIProps) {
const {
fieldType,
+ isAutoComplete = false,
name,
default: defaultValue,
required,
@@ -115,7 +116,14 @@ export default function B3ControlTextField({
const handleNumberInputWheel = (event: WheelEvent) => {
;(event.target as HTMLElement).blur()
}
-
+ const autoCompleteFn = () => {
+ if (!isAutoComplete) {
+ return {
+ autoComplete: 'off',
+ }
+ }
+ return {}
+ }
const newExtraPadding =
fieldId === 'field_state' && extraPadding.paddingTop === '0px'
? {}
@@ -174,7 +182,8 @@ export default function B3ControlTextField({
(errors as any)[name] ? (errors as any)[name].message : null
}
onKeyDown={isEnterTrigger ? handleKeyDown : () => {}}
- autoComplete={fieldType === 'password' ? 'new-password' : 'off'}
+ {...autoCompleteFn()}
+ // autoComplete={fieldType === 'password' ? 'current-password' : 'email'}
/>
)
}
diff --git a/apps/storefront/src/components/layout/B3CloseAppButton.tsx b/apps/storefront/src/components/layout/B3CloseAppButton.tsx
new file mode 100644
index 00000000..e61bbb67
--- /dev/null
+++ b/apps/storefront/src/components/layout/B3CloseAppButton.tsx
@@ -0,0 +1,46 @@
+import { useContext } from 'react'
+import { useSelector } from 'react-redux'
+import { useNavigate } from 'react-router-dom'
+
+import useMobile from '@/hooks/useMobile'
+import { GlobaledContext } from '@/shared/global'
+import { globalStateSelector } from '@/store'
+
+import { CloseBox, CloseBoxMobile, CloseButton } from '../styled'
+
+export default function B3CloseAppButton() {
+ const [isMobile] = useMobile()
+
+ const { setOpenPageFn } = useSelector(globalStateSelector)
+
+ const {
+ state: { isCloseGotoBCHome },
+ } = useContext(GlobaledContext)
+ const navigate = useNavigate()
+
+ const handleCloseForm = () => {
+ if (isCloseGotoBCHome) {
+ window.location.href = '/'
+ } else {
+ navigate('/')
+ setOpenPageFn({
+ isOpen: false,
+ openUrl: '',
+ })
+ }
+ window.history.replaceState(null, '', window.location.pathname || '/')
+ }
+
+ const Box = isMobile ? CloseBoxMobile : CloseBox
+
+ return (
+
+
+
+ )
+}
diff --git a/apps/storefront/src/components/layout/B3Layout.tsx b/apps/storefront/src/components/layout/B3Layout.tsx
index b93454c8..fae78fa1 100644
--- a/apps/storefront/src/components/layout/B3Layout.tsx
+++ b/apps/storefront/src/components/layout/B3Layout.tsx
@@ -12,6 +12,7 @@ import { getIsTokenGotoPage, RouteItem } from '@/shared/routes/routes'
import B3Dialog from '../B3Dialog'
import CompanyCredit from '../CompanyCredit'
+import B3CloseAppButton from './B3CloseAppButton'
import B3Logo from './B3Logo'
import B3Mainheader from './B3Mainheader'
import B3MobileLayout from './B3MobileLayout'
@@ -124,6 +125,7 @@ export default function B3Layout({ children }: { children: ReactNode }) {
p: '32px 63px 70px 63px',
}}
>
+
{children}
diff --git a/apps/storefront/src/components/layout/B3Logo.tsx b/apps/storefront/src/components/layout/B3Logo.tsx
index 506aa959..54eedde2 100644
--- a/apps/storefront/src/components/layout/B3Logo.tsx
+++ b/apps/storefront/src/components/layout/B3Logo.tsx
@@ -31,17 +31,29 @@ export default function B3Logo() {
return (
diff --git a/apps/storefront/src/components/layout/B3MobileLayout.tsx b/apps/storefront/src/components/layout/B3MobileLayout.tsx
index 23910085..46255fd3 100644
--- a/apps/storefront/src/components/layout/B3MobileLayout.tsx
+++ b/apps/storefront/src/components/layout/B3MobileLayout.tsx
@@ -10,6 +10,7 @@ import CompanyCredit from '../CompanyCredit'
import { getContrastColor } from '../outSideComponents/utils/b3CustomStyles'
import B3AccountInfo from './B3AccountInfo'
+import B3CloseAppButton from './B3CloseAppButton'
import B3Logo from './B3Logo'
import B3Nav from './B3Nav'
@@ -66,30 +67,39 @@ export default function B3MobileLayout({
{role === 2 ? (
) : (
-
- {
- window.location.href = '/cart.php'
+ <>
+
+ {
+ window.location.href = '/cart.php'
+ }}
+ />
+
+
-
+ >
+
+
+ >
)}
@@ -98,7 +108,7 @@ export default function B3MobileLayout({
sx={{
p: 0,
m: 0,
- mb: '2vw',
+ mb: '6vw',
fontSize: '34px',
fontWeight: '400',
color: customColor || '#263238',
diff --git a/apps/storefront/src/components/styled.ts b/apps/storefront/src/components/styled.ts
index 8fd725a8..62cafa25 100644
--- a/apps/storefront/src/components/styled.ts
+++ b/apps/storefront/src/components/styled.ts
@@ -1,5 +1,5 @@
import { Close } from '@mui/icons-material'
-import { TextField } from '@mui/material'
+import { Box, TextField } from '@mui/material'
import { createTheme, styled } from '@mui/material/styles'
const theme = createTheme({
@@ -18,6 +18,14 @@ export const CloseButton = styled(Close)(() => ({
cursor: 'pointer',
}))
+export const CloseBox = styled(Box)(() => ({
+ position: 'absolute',
+ top: 15,
+ right: 20,
+}))
+
+export const CloseBoxMobile = styled(Box)(() => ({}))
+
export const CardContainer = styled('div')(() => ({
padding: '20px 20px',
diff --git a/apps/storefront/src/pages/accountSetting/AccountSetting.tsx b/apps/storefront/src/pages/accountSetting/AccountSetting.tsx
index fba0751b..b819325e 100644
--- a/apps/storefront/src/pages/accountSetting/AccountSetting.tsx
+++ b/apps/storefront/src/pages/accountSetting/AccountSetting.tsx
@@ -134,17 +134,18 @@ function AccountSetting() {
contactInformationTranslatedLabels.forEach(
(element: { fieldId: string; label: string }) => {
- if (element.fieldId === 'field_first_name') {
- element.label = b3Lang('accountSettings.form.firstName')
+ const currentElement = element
+ if (currentElement.fieldId === 'field_first_name') {
+ currentElement.label = b3Lang('accountSettings.form.firstName')
}
- if (element.fieldId === 'field_last_name') {
- element.label = b3Lang('accountSettings.form.lastName')
+ if (currentElement.fieldId === 'field_last_name') {
+ currentElement.label = b3Lang('accountSettings.form.lastName')
}
- if (element.fieldId === 'field_email') {
- element.label = b3Lang('accountSettings.form.email')
+ if (currentElement.fieldId === 'field_email') {
+ currentElement.label = b3Lang('accountSettings.form.email')
}
- if (element.fieldId === 'field_phone_number') {
- element.label = b3Lang('accountSettings.form.phoneNumber')
+ if (currentElement.fieldId === 'field_phone_number') {
+ currentElement.label = b3Lang('accountSettings.form.phoneNumber')
}
}
)
@@ -167,7 +168,8 @@ function AccountSetting() {
const passwordModifiedTranslatedFields = JSON.parse(
JSON.stringify(passwordModified)
).map((element: { label: string; idLang: string }) => {
- element.label = b3Lang(element.idLang)
+ const passwordField = element
+ passwordField.label = b3Lang(element.idLang)
return element
})
diff --git a/apps/storefront/src/pages/accountSetting/utils.ts b/apps/storefront/src/pages/accountSetting/utils.ts
index 29eb6377..bc812bfb 100644
--- a/apps/storefront/src/pages/accountSetting/utils.ts
+++ b/apps/storefront/src/pages/accountSetting/utils.ts
@@ -107,30 +107,32 @@ export const initB2BInfo = (
additionalInformation: Partial[]
) => {
contactInformation.forEach((item: Partial) => {
+ const contactItem = item
if (deCodeField(item?.name || '') === 'first_name') {
- item.default = accountSettings.firstName
+ contactItem.default = accountSettings.firstName
}
if (deCodeField(item?.name || '') === 'last_name') {
- item.default = accountSettings.lastName
+ contactItem.default = accountSettings.lastName
}
if (deCodeField(item?.name || '') === 'phone') {
- item.default = accountSettings.phoneNumber
+ contactItem.default = accountSettings.phoneNumber
}
if (deCodeField(item?.name || '') === 'email') {
- item.default = accountSettings.email
- item.validate = emailValidate
+ contactItem.default = accountSettings.email
+ contactItem.validate = emailValidate
}
})
accountB2BFormFields.forEach((item: Partial) => {
+ const formField = item
if (item.name === 'role') {
- item.default = accountSettings.role
- item.muiSelectProps = {
+ formField.default = accountSettings.role
+ formField.muiSelectProps = {
disabled: true,
}
} else if (item.name === 'company') {
- item.default = accountSettings.company
- item.disabled = true
+ formField.default = accountSettings.company
+ formField.disabled = true
}
})
@@ -138,7 +140,8 @@ export const initB2BInfo = (
const formFields = (accountSettings?.formFields || []).find(
(field: Partial) => field.name === item.bcLabel
)
- if (formFields) item.default = formFields.value
+ const infoItem = item
+ if (formFields) infoItem.default = formFields.value
})
return [
@@ -154,21 +157,22 @@ export const initBcInfo = (
additionalInformation: Partial[]
) => {
contactInformation.forEach((item: Partial) => {
+ const contactInfoItem = item
if (deCodeField(item?.name || '') === 'first_name') {
- item.default = accountSettings.firstName
+ contactInfoItem.default = accountSettings.firstName
}
if (deCodeField(item?.name || '') === 'last_name') {
- item.default = accountSettings.lastName
+ contactInfoItem.default = accountSettings.lastName
}
if (deCodeField(item?.name || '') === 'phone') {
- item.default = accountSettings.phoneNumber
+ contactInfoItem.default = accountSettings.phoneNumber
}
if (deCodeField(item?.name || '') === 'email') {
- item.default = accountSettings.email
- item.validate = emailValidate
+ contactInfoItem.default = accountSettings.email
+ contactInfoItem.validate = emailValidate
}
if (deCodeField(item?.name || '') === 'company') {
- item.default = accountSettings.company
+ contactInfoItem.default = accountSettings.company
}
})
@@ -176,7 +180,8 @@ export const initBcInfo = (
const formFields = (accountSettings?.formFields || []).find(
(field: Partial) => field.name === item.bcLabel
)
- if (formFields) item.default = formFields.value
+ const infoItem = item
+ if (formFields) infoItem.default = formFields.value
})
return [...contactInformation, ...additionalInformation]
diff --git a/apps/storefront/src/pages/address/Address.tsx b/apps/storefront/src/pages/address/Address.tsx
index b3c99c2c..7467f846 100644
--- a/apps/storefront/src/pages/address/Address.tsx
+++ b/apps/storefront/src/pages/address/Address.tsx
@@ -244,7 +244,8 @@ function Address() {
)
translatedFilterFormConfig.map((element: { label: string; idLang: any }) => {
- element.label = b3Lang(element.idLang)
+ const item = element
+ item.label = b3Lang(element.idLang)
return element
})
diff --git a/apps/storefront/src/pages/address/components/AddressForm.tsx b/apps/storefront/src/pages/address/components/AddressForm.tsx
index 936bfad8..d3b20ca9 100644
--- a/apps/storefront/src/pages/address/components/AddressForm.tsx
+++ b/apps/storefront/src/pages/address/components/AddressForm.tsx
@@ -326,18 +326,19 @@ function AddressForm(
) => {
if (type === 'add' && originAddressFields.length > 0) {
allAddressFields.forEach((field: CustomFieldItems) => {
+ const addressField = field
if (field.custom) {
if (isB2BUser) {
const originFields = originAddressFields.filter(
(item: CustomFieldItems) => item.name === field.name
)[0]
- field.default = originFields.default || ''
+ addressField.default = originFields.default || ''
} else {
const originFields = originAddressFields.filter(
(item: CustomFieldItems) =>
item.name === field.name || item.bcLabel === field.bcLabel
)[0]
- field.default = originFields.default || ''
+ addressField.default = originFields.default || ''
}
}
})
@@ -378,6 +379,7 @@ function AddressForm(
})
allAddressFields.forEach((field: CustomFieldItems) => {
+ const currentField = field
if (field.custom && extraFields.length > 0) {
if (isB2BUser) {
const name = deCodeField(field.name)
@@ -391,10 +393,10 @@ function AddressForm(
if (currentExtraField) {
setValue(field.name, currentExtraField.fieldValue || '')
- field.default = currentExtraField.fieldValue || ''
+ currentField.default = currentExtraField.fieldValue || ''
} else {
setValue(field.name, '')
- field.default = originFields.default
+ currentField.default = originFields.default
}
} else {
const currentExtraField = extraFields.filter(
@@ -410,11 +412,11 @@ function AddressForm(
if (currentExtraField) {
setValue(field.name, currentExtraField.fieldValue || '')
- field.default =
+ currentField.default =
currentExtraField.fieldValue || originFields.default
} else {
setValue(field.name, '')
- field.default = originFields.default
+ currentField.default = originFields.default
}
}
} else if (field.name === 'country') {
@@ -425,11 +427,11 @@ function AddressForm(
const { states } = currentCountry[0]
if (states.length > 0) {
- field.options = states
- field.fieldType = 'dropdown'
+ currentField.options = states
+ currentField.fieldType = 'dropdown'
} else {
- field.options = []
- field.fieldType = 'text'
+ currentField.options = []
+ currentField.fieldType = 'text'
}
}
} else {
@@ -488,8 +490,18 @@ function AddressForm(
const translatedAddressFields = JSON.parse(JSON.stringify(addressFields))
translatedAddressFields.forEach(
- (element: { label: string; idLang: string }) => {
- element.label = b3Lang(element.idLang)
+ (element: {
+ label: string
+ idLang: string
+ fieldId: string
+ default: string
+ }) => {
+ const translatedFieldElement = element
+ translatedFieldElement.label = b3Lang(element.idLang) || element.label
+
+ if (!isB2BUser && element.fieldId === 'field_21') {
+ translatedFieldElement.default = ''
+ }
return element
}
diff --git a/apps/storefront/src/pages/address/shared/getAddressFields.ts b/apps/storefront/src/pages/address/shared/getAddressFields.ts
index ad2966b4..d99f1b22 100644
--- a/apps/storefront/src/pages/address/shared/getAddressFields.ts
+++ b/apps/storefront/src/pages/address/shared/getAddressFields.ts
@@ -11,8 +11,6 @@ import {
import { b2bAddressFields } from './config'
-// const addressExtraFieldsType = ['text', 'multiline', 'number', 'dropdown']
-
export interface StateProps {
stateCode: string
stateName: string
@@ -97,10 +95,11 @@ const convertExtraFields = (
const convertB2BExtraFields = getAccountFormFields(b2bExtraFields).address
- convertB2BExtraFields.map((field: ExtraFieldsProp) => {
+ convertB2BExtraFields.map((extraField: ExtraFieldsProp) => {
+ const field = extraField
field.custom = true
- return field
+ return extraField
})
return convertB2BExtraFields
@@ -151,16 +150,17 @@ export const getAddressFields = async (
allAddressFields = bcAddressFields
}
- allAddressFields.map((field: CustomFieldItems) => {
- if (field.name === 'country') {
+ allAddressFields.map((addressField: CustomFieldItems) => {
+ const field = addressField
+ if (addressField.name === 'country') {
field.options = countries
}
- if (field.name === 'state') {
+ if (addressField.name === 'state') {
field.fieldType = 'text'
}
- return field
+ return addressField
})
return allAddressFields
diff --git a/apps/storefront/src/pages/invoice/Invoice.tsx b/apps/storefront/src/pages/invoice/Invoice.tsx
index 023c5864..09f1d463 100644
--- a/apps/storefront/src/pages/invoice/Invoice.tsx
+++ b/apps/storefront/src/pages/invoice/Invoice.tsx
@@ -436,15 +436,17 @@ function Invoice() {
const invoicesList: InvoiceListNode[] = edges
if (type === InvoiceListType.DETAIL && invoicesList.length) {
- invoicesList.forEach((item: InvoiceListNode) => {
+ invoicesList.forEach((invoice: InvoiceListNode) => {
+ const item = invoice
item.node.isCollapse = true
})
}
- invoicesList.forEach((item: InvoiceListNode) => {
+ invoicesList.forEach((invoiceNode: InvoiceListNode) => {
const {
node: { openBalance },
- } = item
+ } = invoiceNode
+ const item = invoiceNode
item.node.disableCurrentCheckbox = false
openBalance.value = (+openBalance.value).toFixed(decimalPlaces)
@@ -730,12 +732,16 @@ function Invoice() {
}, [checkedArr, filterData])
const translatedFilterFormConfigs = filterFormConfig.map((element) => {
+ const config = element
if (element.name === 'status') {
- element.label = b3Lang(filterFormConfigsTranslationVariables.status)
+ config.label = b3Lang(filterFormConfigsTranslationVariables.status)
}
- element.options = element.options.map((option) => {
- option.label = b3Lang(filterFormConfigsTranslationVariables[option.key])
+ config.options = element.options.map((option) => {
+ const elementOption = option
+ elementOption.label = b3Lang(
+ filterFormConfigsTranslationVariables[option.key]
+ )
return option
})
diff --git a/apps/storefront/src/pages/login/Login.tsx b/apps/storefront/src/pages/login/Login.tsx
index d004304a..2aab03b0 100644
--- a/apps/storefront/src/pages/login/Login.tsx
+++ b/apps/storefront/src/pages/login/Login.tsx
@@ -23,6 +23,7 @@ import { b2bLogin, bcLogoutLogin, customerLoginAPI } from '@/shared/service/bc'
import {
B3SStorage,
getCurrentCustomerInfo,
+ loginjump,
logoutSession,
snackbar,
storeHash,
@@ -143,6 +144,9 @@ export default function Login(props: RegisteredProps) {
if (loginFlag) setLoginFlag(loginFlag)
const isLogout = B3SStorage.get('isLogout') === '1'
+ if (loginFlag === '7') {
+ snackbar.error(b3Lang('login.loginText.invoiceErrorTip'))
+ }
if (loginFlag === '3' && !isLogout) {
const { result } = (await bcLogoutLogin()).data.logout
@@ -289,7 +293,13 @@ export default function Login(props: RegisteredProps) {
if (info?.userType === 3 && info?.role === 3) {
navigate('/dashboard')
- } else if (info?.role === 2) {
+ return
+ }
+ const isLoginLandLocation = loginjump(navigate)
+
+ if (!isLoginLandLocation) return
+
+ if (info?.role === 2) {
navigate('/shoppingLists')
} else {
navigate('/orders')
@@ -339,31 +349,51 @@ export default function Login(props: RegisteredProps) {
margin: '30px 0 0 0',
}}
>
-
- {tipInfo(flag, loginAccount?.emailAddress || '')}
-
+ {tipInfo(flag, loginAccount?.emailAddress) && (
+
+ {tipInfo(flag, loginAccount?.emailAddress || '')}
+
+ )}
)}
+ {logo && loginInfo?.displayStoreLogo && (
+
+
+ {
+ window.location.href = '/'
+ }}
+ >
+
+
+
+
+ )}
+ {loginInfo.widgetHeadText && (
+
+ )}
- {loginInfo.widgetHeadText && (
-
- )}
- {logo && loginInfo?.displayStoreLogo && (
-
-
- {
- window.location.href = '/'
- }}
- >
-
-
-
-
- )}
-
- {loginInfo.widgetFooterText && (
-
- )}
+ {loginInfo.widgetFooterText && (
+
+ )}
>
)}
diff --git a/apps/storefront/src/pages/login/LoginForm.tsx b/apps/storefront/src/pages/login/LoginForm.tsx
index c770c889..ec722dc7 100644
--- a/apps/storefront/src/pages/login/LoginForm.tsx
+++ b/apps/storefront/src/pages/login/LoginForm.tsx
@@ -68,45 +68,45 @@ function LoginForm(props: LoginFormProps) {
},
}}
>
-
-
-
-
- {loginBtn}
-
+
+
)
diff --git a/apps/storefront/src/pages/login/config.ts b/apps/storefront/src/pages/login/config.ts
index 419bd2f1..02701e21 100644
--- a/apps/storefront/src/pages/login/config.ts
+++ b/apps/storefront/src/pages/login/config.ts
@@ -76,6 +76,7 @@ export const getLoginFields = (
xs: 12,
variant: 'filled',
validate: validatorRules(['email']),
+ isAutoComplete: true,
},
{
name: 'password',
@@ -87,6 +88,7 @@ export const getLoginFields = (
variant: 'filled',
isEnterTrigger: true,
handleEnterClick: submitLoginFn,
+ isAutoComplete: true,
},
]
diff --git a/apps/storefront/src/pages/order/Order.tsx b/apps/storefront/src/pages/order/Order.tsx
index 369ae370..f177f850 100644
--- a/apps/storefront/src/pages/order/Order.tsx
+++ b/apps/storefront/src/pages/order/Order.tsx
@@ -108,14 +108,16 @@ function Order({ isCompanyOrder = false }: OrderProps) {
setOrderStatuses(orderStatuses[orderStatusesName])
const filterInfoWithTranslatedLabel = filterInfo.map((element) => {
- element.label = b3Lang(element.idLang)
+ const translatedElement = element
+ translatedElement.label = b3Lang(element.idLang)
if (element.name === 'orderStatus') {
- element.options = element.options.map(
+ translatedElement.options = element.options.map(
(option: { customLabel: string; systemLabel: string }) => {
const optionLabel =
orderStatusTranslationVariables[option.systemLabel]
- option.customLabel = b3Lang(optionLabel)
+ const elementOption = option
+ elementOption.customLabel = b3Lang(optionLabel)
return option
}
diff --git a/apps/storefront/src/pages/orderDetail/components/OrderCheckboxProduct.tsx b/apps/storefront/src/pages/orderDetail/components/OrderCheckboxProduct.tsx
index 37d3c37e..59310631 100644
--- a/apps/storefront/src/pages/orderDetail/components/OrderCheckboxProduct.tsx
+++ b/apps/storefront/src/pages/orderDetail/components/OrderCheckboxProduct.tsx
@@ -117,19 +117,21 @@ export default function OrderCheckboxProduct(props: OrderCheckboxProductProps) {
const handleProductQuantityChange =
(product: EditableProductItem) => (e: ChangeEvent) => {
+ const element = product
const valueNum = e.target.value
if (+valueNum >= 0 && +valueNum <= 1000000) {
- product.editQuantity = valueNum
+ element.editQuantity = valueNum
if (type === 'return') {
if (+valueNum > +product.quantity) {
- product.editQuantity = product.quantity
+ element.editQuantity = product.quantity
snackbar.error(
b3Lang(
'purchasedProducts.error.returnedQuantityShouldBeWithinThePurchase'
)
)
} else {
- returnList.forEach((item) => {
+ returnList.forEach((listItem) => {
+ const item = listItem
if (item.returnId === product.id) {
item.returnQty = +valueNum
}
@@ -148,8 +150,9 @@ export default function OrderCheckboxProduct(props: OrderCheckboxProductProps) {
}
const handleNumberInputBlur = (product: EditableProductItem) => () => {
+ const editableProduct = product
if (!product.editQuantity || +product.editQuantity === 0) {
- product.editQuantity = '1'
+ editableProduct.editQuantity = '1'
onProductChange([...products])
}
}
diff --git a/apps/storefront/src/pages/orderDetail/components/OrderDialog.tsx b/apps/storefront/src/pages/orderDetail/components/OrderDialog.tsx
index a8db7c6e..3856c911 100644
--- a/apps/storefront/src/pages/orderDetail/components/OrderDialog.tsx
+++ b/apps/storefront/src/pages/orderDetail/components/OrderDialog.tsx
@@ -122,8 +122,9 @@ export default function OrderDialog({
return
}
const transformedData = returnArr.reduce((result, item) => {
+ const resultedData = result
const key = `return_qty[${item.returnId}]`
- result[key] = item.returnQty
+ resultedData[key] = item.returnQty
return result
}, returnReason)
transformedData.authenticity_token = getXsrfToken()
@@ -250,6 +251,7 @@ export default function OrderDialog({
optionId: option.product_option_id,
optionValue: option.value,
})),
+ allOptions: product.product_options,
})
skus.push(product.sku)
diff --git a/apps/storefront/src/pages/orderDetail/components/OrderShipping.tsx b/apps/storefront/src/pages/orderDetail/components/OrderShipping.tsx
index 65a26bff..b16e9040 100644
--- a/apps/storefront/src/pages/orderDetail/components/OrderShipping.tsx
+++ b/apps/storefront/src/pages/orderDetail/components/OrderShipping.tsx
@@ -39,7 +39,8 @@ export default function OrderShipping() {
const tracking = getTracking(trackingNumber)
if (tracking) {
const { trackingUrl = '' } = tracking
- if (trackingUrl) item.tracking_link = trackingUrl
+ const shippingItem = item
+ if (trackingUrl) shippingItem.tracking_link = trackingUrl
}
})
}
diff --git a/apps/storefront/src/pages/orderDetail/shared/B2BOrderData.ts b/apps/storefront/src/pages/orderDetail/shared/B2BOrderData.ts
index b1e0bdd5..a427b6a9 100644
--- a/apps/storefront/src/pages/orderDetail/shared/B2BOrderData.ts
+++ b/apps/storefront/src/pages/orderDetail/shared/B2BOrderData.ts
@@ -55,7 +55,8 @@ const getOrderShipping = (data: B2BOrderData) => {
],
notShip: {
itemsInfo: products.filter((product: OrderProductItem) => {
- product.not_shipping_number =
+ const orderProduct = product
+ orderProduct.not_shipping_number =
product.quantity - product.quantity_shipped
return (
product.quantity > product.quantity_shipped &&
diff --git a/apps/storefront/src/pages/quickorder/components/QuickOrderFooter.tsx b/apps/storefront/src/pages/quickorder/components/QuickOrderFooter.tsx
index 9ef9d872..a149bfd4 100644
--- a/apps/storefront/src/pages/quickorder/components/QuickOrderFooter.tsx
+++ b/apps/storefront/src/pages/quickorder/components/QuickOrderFooter.tsx
@@ -196,6 +196,7 @@ function QuickOrderFooter(props: QuickOrderFooterProps) {
lineItems.push({
optionSelections: options,
+ allOptions: optionList,
productId: parseInt(currentInventoryInfo.product_id, 10) || 0,
quantity,
variantId: parseInt(currentInventoryInfo.variant_id, 10) || 0,
diff --git a/apps/storefront/src/pages/quickorder/components/QuickOrderPad.tsx b/apps/storefront/src/pages/quickorder/components/QuickOrderPad.tsx
index ecf337fa..5e369c55 100644
--- a/apps/storefront/src/pages/quickorder/components/QuickOrderPad.tsx
+++ b/apps/storefront/src/pages/quickorder/components/QuickOrderPad.tsx
@@ -98,7 +98,7 @@ export default function QuickOrderPad(props: QuickOrderPadProps) {
}}
>
{b3Lang('purchasedProducts.quickOrderPad.notEnoughStock', {
- sku: data.variantSku,
+ variantSku: data.variantSku,
})}
{b3Lang('purchasedProducts.quickOrderPad.outOfStockSku', {
- sku: outOfStock,
+ outOfStock,
})}
{
snackbar.error(
b3Lang('purchasedProducts.quickOrderPad.maxQuantityMessage', {
- minQuantity: data.maxQuantity,
+ maxQuantity: data.maxQuantity,
sku: data.variantSku,
}),
{
@@ -365,7 +367,7 @@ export default function QuickOrderPad(props: QuickOrderPadProps) {
if (+orderQuantityMaximum > 0 && +quantity > +orderQuantityMaximum) {
snackbar.error(
- b3Lang('purchasedProducts.quickOrderPad.minQuantityMessage', {
+ b3Lang('purchasedProducts.quickOrderPad.maxQuantityMessage', {
maxQuantity: orderQuantityMaximum,
sku: productSku,
}),
diff --git a/apps/storefront/src/pages/quickorder/components/QuickorderTable.tsx b/apps/storefront/src/pages/quickorder/components/QuickorderTable.tsx
index b5d98b0b..9540e6e2 100644
--- a/apps/storefront/src/pages/quickorder/components/QuickorderTable.tsx
+++ b/apps/storefront/src/pages/quickorder/components/QuickorderTable.tsx
@@ -327,7 +327,8 @@ function QuickorderTable({
const currentProduct = cacheProductList.find(
(item: CustomFieldItems) =>
item.node.variantId === row.variantId &&
- item.node.productId === row.productId
+ item.node.productId === row.productId &&
+ item.node.id === row.id
)
if (currentProduct && currentProduct.node) {
diff --git a/apps/storefront/src/pages/quote/QuoteDetail.tsx b/apps/storefront/src/pages/quote/QuoteDetail.tsx
index 6691b4f6..bd89bf0f 100644
--- a/apps/storefront/src/pages/quote/QuoteDetail.tsx
+++ b/apps/storefront/src/pages/quote/QuoteDetail.tsx
@@ -207,6 +207,7 @@ function QuoteDetail() {
const newProductsSearch = conversionProductsList(productsSearch)
listProducts.forEach((item) => {
+ const listProduct = item
const productInfo = newProductsSearch.find(
(search: CustomFieldItems) => {
const { id: productId } = search
@@ -215,7 +216,7 @@ function QuoteDetail() {
}
)
- item.productsSearch = productInfo || {}
+ listProduct.productsSearch = productInfo || {}
})
return listProducts
diff --git a/apps/storefront/src/pages/quote/QuoteDraft.tsx b/apps/storefront/src/pages/quote/QuoteDraft.tsx
index fb7b7511..0709b60d 100644
--- a/apps/storefront/src/pages/quote/QuoteDraft.tsx
+++ b/apps/storefront/src/pages/quote/QuoteDraft.tsx
@@ -320,7 +320,11 @@ function QuoteDraft({ setOpenPage }: QuoteDraftProps) {
const isComplete = Object.keys(saveInfo.contactInfo).every(
(key: string) => {
- if (key === 'phoneNumber' || key === 'companyName') {
+ if (
+ key === 'phoneNumber' ||
+ key === 'companyName' ||
+ key === 'quoteTitle'
+ ) {
return true
}
return !!saveInfo.contactInfo[key]
@@ -356,6 +360,7 @@ function QuoteDraft({ setOpenPage }: QuoteDraftProps) {
list.forEach((item: any) => {
let additionalCalculatedPriceTax = 0
let additionalCalculatedPrice = 0
+ const listItem = item
if (item.node.additionalCalculatedPrices?.length) {
item.node.additionalCalculatedPrices.forEach((item: any) => {
@@ -364,8 +369,8 @@ function QuoteDraft({ setOpenPage }: QuoteDraftProps) {
})
}
- item.node.basePrice = +item.node.basePrice + additionalCalculatedPrice
- item.node.tax = +item.node.tax + additionalCalculatedPriceTax
+ listItem.node.basePrice = +item.node.basePrice + additionalCalculatedPrice
+ listItem.node.tax = +item.node.tax + additionalCalculatedPriceTax
})
return {
@@ -412,6 +417,10 @@ function QuoteDraft({ setOpenPage }: QuoteDraftProps) {
const info = B3LStorage.get('MyQuoteInfo')
const contactInfo = info?.contactInfo || {}
+ const quoteTitle = contactInfo?.quoteTitle || ''
+
+ if ('quoteTitle' in contactInfo) delete contactInfo.quoteTitle
+
const isComplete = Object.keys(contactInfo).every((key: string) => {
if (key === 'phoneNumber' || key === 'companyName') {
return true
@@ -544,6 +553,7 @@ function QuoteDraft({ setOpenPage }: QuoteDraftProps) {
subtotal: allPrice.toFixed(decimalPlaces),
companyId: isB2BUser ? companyB2BId || salesRepCompanyId : '',
storeHash,
+ quoteTitle,
discount: '0.00',
channelId,
userEmail: emailAddress,
@@ -671,7 +681,13 @@ function QuoteDraft({ setOpenPage }: QuoteDraftProps) {
marginRight: '0.5rem',
}}
/>
- {backText()}
+
+ {backText()}
+
{
variant: 'filled',
size: 'small',
},
+ {
+ name: 'quoteTitle',
+ label: b3Lang('quoteDraft.contactInfo.quoteTitle'),
+ required: false,
+ default: '',
+ fieldType: 'text',
+ xs: isMobile ? 12 : 6,
+ variant: 'filled',
+ size: 'small',
+ },
]
return contactInfo
diff --git a/apps/storefront/src/pages/quote/components/QuoteDetailHeader.tsx b/apps/storefront/src/pages/quote/components/QuoteDetailHeader.tsx
index 4e7e82cb..f5bb7d05 100644
--- a/apps/storefront/src/pages/quote/components/QuoteDetailHeader.tsx
+++ b/apps/storefront/src/pages/quote/components/QuoteDetailHeader.tsx
@@ -101,6 +101,7 @@ function QuoteDetailHeader(props: QuoteDetailHeaderProps) {
{b3Lang('quoteDetail.header.backToQuoteLists')}
diff --git a/apps/storefront/src/pages/quote/components/QuoteInfo.tsx b/apps/storefront/src/pages/quote/components/QuoteInfo.tsx
index 57922e5c..6712f062 100644
--- a/apps/storefront/src/pages/quote/components/QuoteInfo.tsx
+++ b/apps/storefront/src/pages/quote/components/QuoteInfo.tsx
@@ -25,6 +25,7 @@ const contactInfoKeys: string[] = [
'email',
'companyName',
'phoneNumber',
+ 'quoteTitle',
]
const addressVerifyKeys: string[] = [
@@ -61,6 +62,7 @@ interface QuoteInfoItemProps {
function QuoteInfoItem({ flag, title, info, status }: QuoteInfoItemProps) {
const keyTable = flag === 'info' ? contactInfoKeys : addressKeys
const [isMobile] = useMobile()
+ const b3Lang = useB3Lang()
const noAddresssText =
status === 'Draft'
@@ -97,6 +99,24 @@ function QuoteInfoItem({ flag, title, info, status }: QuoteInfoItemProps) {
{(isComplete || flag === 'info') &&
JSON.stringify(info) !== '{}' &&
keyTable.map((list: Keys) => {
+ if (list === 'quoteTitle') {
+ return status === 'Draft' ? (
+
+ {`${b3Lang('quoteDraft.quoteInfo.quoteTitle')}${
+ info[list] || ''
+ }`}
+
+ ) : (
+ ''
+ )
+ }
+
if (typeof list === 'string') {
return (
@@ -133,6 +153,7 @@ function QuoteInfo({
}: InfoProps) {
const b3Lang = useB3Lang()
const [isMobile] = useMobile()
+
return (
diff --git a/apps/storefront/src/pages/quote/components/QuoteTable.tsx b/apps/storefront/src/pages/quote/components/QuoteTable.tsx
index 9350575b..74f05f94 100644
--- a/apps/storefront/src/pages/quote/components/QuoteTable.tsx
+++ b/apps/storefront/src/pages/quote/components/QuoteTable.tsx
@@ -135,8 +135,9 @@ function QuoteTable(props: ShoppingDetailTableProps, ref: Ref) {
const listItems = paginationTableRef.current?.getList() || []
const newListItems = listItems?.map((item: ListItemProps) => {
const { node } = item
+ const listItem = item
if (node?.id === (product as CustomFieldItems).id) {
- ;(item as CustomFieldItems).node = product
+ ;(listItem as CustomFieldItems).node = product
}
return item
@@ -277,14 +278,15 @@ function QuoteTable(props: ShoppingDetailTableProps, ref: Ref) {
productsSearch: { variants },
basePrice,
} = product.node
+ const newProduct = product
const variantItem = variants.find(
(item: CustomFieldItems) => item.sku === variantSku
)
- product.node.id = optionsProductId
+ newProduct.node.id = optionsProductId
- product.node.basePrice = basePrice
- product.node.tax =
+ newProduct.node.basePrice = basePrice
+ newProduct.node.tax =
variantItem.bc_calculated_price.tax_inclusive -
variantItem.bc_calculated_price.tax_exclusive
})
@@ -294,8 +296,9 @@ function QuoteTable(props: ShoppingDetailTableProps, ref: Ref) {
const b2bQuoteDraftList = B3LStorage.get('b2bQuoteDraftList') || []
b2bQuoteDraftList.forEach((item: CustomFieldItems) => {
+ const listItem = item
if (item.node.id === optionsProductId) {
- item.node = newProducts[0]?.node || {}
+ listItem.node = newProducts[0]?.node || {}
}
})
diff --git a/apps/storefront/src/pages/registered/RegisterComplete.tsx b/apps/storefront/src/pages/registered/RegisterComplete.tsx
index c189e9c8..320c016d 100644
--- a/apps/storefront/src/pages/registered/RegisterComplete.tsx
+++ b/apps/storefront/src/pages/registered/RegisterComplete.tsx
@@ -1,17 +1,8 @@
-import {
- MouseEvent,
- useContext,
- useEffect,
- useState,
- // useMemo,
-} from 'react'
+import { MouseEvent, useContext, useEffect, useState } from 'react'
import { useForm } from 'react-hook-form'
import { useB3Lang } from '@b3/lang'
import { Alert, Box } from '@mui/material'
-// import {
-// Captcha,
-// } from '@/components/form'
import { B3CustomForm } from '@/components'
import { getContrastColor } from '@/components/outSideComponents/utils/b3CustomStyles'
import { CustomStyleContext } from '@/shared/customStyleButtton'
@@ -233,8 +224,6 @@ export default function RegisterComplete(props: RegisterCompleteProps) {
b2bFields.extraFields = extraFields
}
- // b2bFields.companyEmail = data.email || enterEmail
-
// address Field
const addressBasicInfo =
addressBasicList.filter((list) => !list.custom) || []
@@ -295,12 +284,13 @@ export default function RegisterComplete(props: RegisterCompleteProps) {
)
const fileList = fileResponse.reduce((fileList: any, res: any) => {
+ let list = fileList
if (res.code === 200) {
const newData = {
...res.data,
}
newData.fileSize = newData.fileSize ? `${newData.fileSize}` : ''
- fileList = [...fileList, newData]
+ list = [...fileList, newData]
} else {
throw (
res.data.errMsg ||
@@ -308,7 +298,7 @@ export default function RegisterComplete(props: RegisterCompleteProps) {
b3Lang('intl.global.fileUpload.fileUploadFailure')
)
}
- return fileList
+ return list
}, [])
return fileList
@@ -321,8 +311,9 @@ export default function RegisterComplete(props: RegisterCompleteProps) {
const saveRegisterPassword = (data: CustomFieldItems) => {
const newPasswordInformation = passwordInformation.map(
(field: RegisterFields) => {
+ const registerField = field
if (accountType === '1') {
- field.default = data[field.name] || field.default
+ registerField.default = data[field.name] || field.default
}
return field
}
@@ -330,8 +321,9 @@ export default function RegisterComplete(props: RegisterCompleteProps) {
const newBcPasswordInformation = bcPasswordInformation.map(
(field: RegisterFields) => {
+ const registerField = field
if (accountType === '2') {
- field.default = data[field.name] || field.default
+ registerField.default = data[field.name] || field.default
}
return field
diff --git a/apps/storefront/src/pages/registered/Registered.tsx b/apps/storefront/src/pages/registered/Registered.tsx
index 555e55b7..06be88ac 100644
--- a/apps/storefront/src/pages/registered/Registered.tsx
+++ b/apps/storefront/src/pages/registered/Registered.tsx
@@ -21,7 +21,7 @@ import {
bcLogin,
} from '@/shared/service/bc'
import { themeFrameSelector } from '@/store'
-import { B3SStorage, getCurrentCustomerInfo } from '@/utils'
+import { B3SStorage, getCurrentCustomerInfo, loginjump } from '@/utils'
import { loginCheckout, LoginConfig } from '../login/config'
@@ -105,8 +105,6 @@ function Registered(props: RegisteredProps) {
})
}
- // await getBCRegisterCustomFields()
-
const accountFormAllFields = formType.map((item: number) =>
getB2BAccountFormFields(item)
)
@@ -116,12 +114,13 @@ function Registered(props: RegisteredProps) {
const newB2bAccountFormFields: AccountFormFieldsItems[] = (
accountFormFields[1]?.accountFormFields || []
).map((fields: AccountFormFieldsItems) => {
+ const formFields = fields
if (
b2bAddressRequiredFields.includes(fields?.fieldId || '') &&
fields.groupId === 4
) {
- fields.isRequired = true
- fields.visible = true
+ formFields.isRequired = true
+ formFields.visible = true
}
return fields
@@ -141,9 +140,10 @@ function Registered(props: RegisteredProps) {
(
addressFields: Partial
): Partial => {
+ const fields = addressFields
if (addressFields.name === 'country') {
- addressFields.options = countries
- addressFields.replaceOptions = {
+ fields.options = countries
+ fields.replaceOptions = {
label: 'countryName',
value: 'countryName',
}
@@ -157,8 +157,15 @@ function Registered(props: RegisteredProps) {
(
addressFields: Partial
): Partial => {
+ const addressFormFields = addressFields
if (addressFields.name === 'country') {
- addressFields.options = countries
+ addressFormFields.options = countries
+ const countryDefaultValue = countries.find(
+ (country: CustomFieldItems) =>
+ country.countryName === addressFields.default
+ )
+ addressFormFields.default =
+ countryDefaultValue?.countryCode || addressFields.default
}
return addressFields
}
@@ -294,12 +301,17 @@ function Registered(props: RegisteredProps) {
await getCurrentCustomerInfo(globalDispatch)
+ clearRegisterInfo()
+
+ const isLoginLandLocation = loginjump(navigate)
+
+ if (!isLoginLandLocation) return
+
if (isCloseGotoBCHome) {
window.location.href = '/'
} else {
navigate('/orders')
}
- clearRegisterInfo()
} catch (error) {
console.log(error)
}
diff --git a/apps/storefront/src/pages/registered/RegisteredAccount.tsx b/apps/storefront/src/pages/registered/RegisteredAccount.tsx
index 9d839ee1..213fd28a 100644
--- a/apps/storefront/src/pages/registered/RegisteredAccount.tsx
+++ b/apps/storefront/src/pages/registered/RegisteredAccount.tsx
@@ -82,13 +82,14 @@ export default function RegisteredAccount(props: RegisteredAccountProps) {
: bcAdditionalInformation || []
const newContactInformation = contactInformation?.map(
- (info: CustomFieldItems) => {
- if (info.fieldId === 'field_email' && accountType === '1') {
+ (contactInfo: CustomFieldItems) => {
+ const info = contactInfo
+ if (contactInfo.fieldId === 'field_email' && accountType === '1') {
info.isTip = true
info.tipText = 'This email will be used to sign in to your account'
}
- return info
+ return contactInfo
}
)
@@ -160,12 +161,13 @@ export default function RegisteredAccount(props: RegisteredAccountProps) {
}
const newContactInfo = contactInfo.map((item: RegisterFields) => {
- item.default = data[item.name] || item.default
+ const newContactItem = item
+ newContactItem.default = data[item.name] || item.default
if (
item.fieldId === 'field_email_marketing_newsletter' &&
item.fieldType === 'checkbox'
) {
- item.isChecked = data[item.name].length > 0
+ newContactItem.isChecked = data[item.name].length > 0
}
return item
})
@@ -175,7 +177,8 @@ export default function RegisteredAccount(props: RegisteredAccountProps) {
newAdditionalInformation = (
additionalInfo as Array
).map((item: RegisterFields) => {
- item.default = data[item.name] || item.default
+ const additionalInfoItem = item
+ additionalInfoItem.default = data[item.name] || item.default
return item
})
}
diff --git a/apps/storefront/src/pages/registered/RegisteredBCToB2B.tsx b/apps/storefront/src/pages/registered/RegisteredBCToB2B.tsx
index c6d793cc..41bb4a9c 100644
--- a/apps/storefront/src/pages/registered/RegisteredBCToB2B.tsx
+++ b/apps/storefront/src/pages/registered/RegisteredBCToB2B.tsx
@@ -12,13 +12,14 @@ import type { OpenPageState } from '@b3/hooks'
import { useB3Lang } from '@b3/lang'
import styled from '@emotion/styled'
import { Alert, Box, ImageListItem } from '@mui/material'
+import isEmpty from 'lodash-es/isEmpty'
import { B3Card, B3CustomForm, B3Sping, CustomButton } from '@/components'
import { getContrastColor } from '@/components/outSideComponents/utils/b3CustomStyles'
import { useMobile } from '@/hooks'
import { CustomStyleContext } from '@/shared/customStyleButtton'
import { GlobaledContext } from '@/shared/global'
-import { getCurrentCustomerInfo, storeHash } from '@/utils'
+import { getCurrentCustomerInfo, loginjump, storeHash } from '@/utils'
import {
createB2BCompanyUser,
@@ -158,12 +159,13 @@ export default function RegisteredBCToB2B(props: RegisteredProps) {
const newAccountFormFields: AccountFormFieldsItems[] = (
accountFormAllFields?.accountFormFields || []
).map((fields: AccountFormFieldsItems) => {
+ const accountFields = fields
if (
b2bAddressRequiredFields.includes(fields?.fieldId || '') &&
fields.groupId === 4
) {
- fields.isRequired = true
- fields.visible = true
+ accountFields.isRequired = true
+ accountFields.visible = true
}
return fields
@@ -179,9 +181,10 @@ export default function RegisteredBCToB2B(props: RegisteredProps) {
(
addressFields: Partial
): Partial => {
+ const fields = addressFields
if (addressFields.name === 'country') {
- addressFields.options = countries
- addressFields.replaceOptions = {
+ fields.options = countries
+ fields.replaceOptions = {
label: 'countryName',
value: 'countryName',
}
@@ -202,13 +205,21 @@ export default function RegisteredBCToB2B(props: RegisteredProps) {
(
contactInformationField: Partial
): Partial => {
- contactInformationField.disabled = true
+ const field = contactInformationField
+ field.disabled = true
- contactInformationField.default =
+ field.default =
customerInfo[
deCodeField(contactInformationField.name as string)
] || contactInformationField.default
+ if (
+ contactInformationField.required &&
+ !contactInformationField?.default
+ ) {
+ field.disabled = false
+ }
+
return contactInformationField
}
)
@@ -319,8 +330,13 @@ export default function RegisteredBCToB2B(props: RegisteredProps) {
)
const fileList = fileResponse.reduce((fileList: any, res: any) => {
+ let list = fileList
if (res.code === 200) {
- fileList = [...fileList, res.data]
+ const newData = {
+ ...res.data,
+ }
+ newData.fileSize = newData.fileSize ? `${newData.fileSize}` : ''
+ list = [...fileList, newData]
} else {
throw (
res.data.errMsg ||
@@ -328,7 +344,7 @@ export default function RegisteredBCToB2B(props: RegisteredProps) {
b3Lang('intl.global.fileUpload.fileUploadFailure')
)
}
- return fileList
+ return list
}, [])
return fileList
@@ -372,8 +388,6 @@ export default function RegisteredBCToB2B(props: RegisteredProps) {
b2bFields.extraFields = extraFields
}
- // b2bFields.companyEmail = data.email
-
// address Field
const addressBasicInfo = bcTob2bAddressBasicFields.filter(
(list) => !list.custom
@@ -457,8 +471,35 @@ export default function RegisteredBCToB2B(props: RegisteredProps) {
}
}
+ const handleValidateAttachmentFiles = () => {
+ const formData = getValues()
+ const attachmentsFilesFiled = bcTob2bCompanyInformation.find(
+ (info) => info.fieldId === 'field_attachments'
+ )
+ if (
+ !isEmpty(attachmentsFilesFiled) &&
+ attachmentsFilesFiled.required &&
+ formData[attachmentsFilesFiled.name].length === 0
+ ) {
+ setError(attachmentsFilesFiled.name, {
+ type: 'required',
+ message: b3Lang('global.validate.required', {
+ label: attachmentsFilesFiled.label,
+ }),
+ })
+
+ showLoading(false)
+ return true
+ }
+
+ return false
+ }
+
const handleNext = (event: MouseEvent) => {
+ const hasAttachmentsFilesError = handleValidateAttachmentFiles()
+
handleSubmit(async (data: CustomFieldItems) => {
+ if (hasAttachmentsFilesError) return
showLoading(true)
try {
@@ -471,7 +512,6 @@ export default function RegisteredBCToB2B(props: RegisteredProps) {
(list) => list.fieldType === 'files'
)
const fileList = await getFileUrl(attachmentsList || [], data)
-
await getB2BFieldsValue(data, customerId, fileList)
const isAuto = companyAutoApproval.enabled
@@ -504,6 +544,10 @@ export default function RegisteredBCToB2B(props: RegisteredProps) {
}
const handleFinish = () => {
+ const isLoginLandLocation = loginjump(navigate, true)
+
+ if (!isLoginLandLocation) return
+
if (companyAutoApproval.enabled) {
navigate('/orders')
} else {
@@ -621,6 +665,7 @@ export default function RegisteredBCToB2B(props: RegisteredProps) {
control={control}
getValues={getValues}
setValue={setValue}
+ setError={setError}
/>
diff --git a/apps/storefront/src/pages/registered/RegisteredDetail.tsx b/apps/storefront/src/pages/registered/RegisteredDetail.tsx
index bc3cdadd..0a6958e0 100644
--- a/apps/storefront/src/pages/registered/RegisteredDetail.tsx
+++ b/apps/storefront/src/pages/registered/RegisteredDetail.tsx
@@ -1,6 +1,8 @@
import { MouseEvent, useContext, useEffect, useState } from 'react'
import { useForm } from 'react-hook-form'
+import { useB3Lang } from '@b3/lang'
import { Alert, Box } from '@mui/material'
+import isEmpty from 'lodash-es/isEmpty'
import { B3CustomForm } from '@/components'
import { getContrastColor } from '@/components/outSideComponents/utils/b3CustomStyles'
@@ -19,6 +21,7 @@ interface RegisteredDetailProps {
}
export default function RegisteredDetail(props: RegisteredDetailProps) {
+ const b3Lang = useB3Lang()
const { handleBack, handleNext, activeStep } = props
const { state, dispatch } = useContext(RegisteredContext)
@@ -136,7 +139,8 @@ export default function RegisteredDetail(props: RegisteredDetailProps) {
formData: CustomFieldItems
) =>
formFields.map((field) => {
- field.default = formData[field.name] || field.default
+ const item = field
+ item.default = formData[field.name] || field.default
return field
})
@@ -150,7 +154,8 @@ export default function RegisteredDetail(props: RegisteredDetailProps) {
...companyAttachment,
...addressBasicList,
].reduce((formValues: DetailsFormValues, field: RegisterFields) => {
- formValues[field.name] = getValues(field.name) || field.default
+ const values = formValues
+ values[field.name] = getValues(field.name) || field.default
return formValues
}, {})
@@ -172,8 +177,37 @@ export default function RegisteredDetail(props: RegisteredDetailProps) {
})
}
+ const handleValidateAttachmentFiles = () => {
+ if (accountType === '1') {
+ const formData = getValues()
+ const attachmentsFilesFiled = companyInformation.find(
+ (info) => info.fieldId === 'field_attachments'
+ )
+ if (
+ !isEmpty(attachmentsFilesFiled) &&
+ attachmentsFilesFiled.required &&
+ formData[attachmentsFilesFiled.name].length === 0
+ ) {
+ setError(attachmentsFilesFiled.name, {
+ type: 'required',
+ message: b3Lang('global.validate.required', {
+ label: attachmentsFilesFiled.label,
+ }),
+ })
+
+ showLoading(false)
+ return true
+ }
+ }
+
+ return false
+ }
+
const handleAccountToFinish = (event: MouseEvent) => {
+ const hasAttachmentsFilesError = handleValidateAttachmentFiles()
+
handleSubmit(async (data: CustomFieldItems) => {
+ if (hasAttachmentsFilesError) return
showLoading(true)
try {
@@ -266,6 +300,7 @@ export default function RegisteredDetail(props: RegisteredDetailProps) {
control={control}
getValues={getValues}
setValue={setValue}
+ setError={setError}
/>
) : null}
diff --git a/apps/storefront/src/pages/registered/config.ts b/apps/storefront/src/pages/registered/config.ts
index 321a1ad0..46b1683a 100644
--- a/apps/storefront/src/pages/registered/config.ts
+++ b/apps/storefront/src/pages/registered/config.ts
@@ -5,9 +5,6 @@ import { getLineNumber, validatorRules } from '@/utils'
const inputFormat = 'yyyy-MM-dd'
-// export interface CustomFieldItems {
-// [key: string]: any
-// }
export interface QuoteConfig {
[key: string]: string
}
@@ -183,12 +180,17 @@ const classificationType = (item: CustomFieldItems) => {
if (optionItems?.options) {
optionItems?.options.forEach((option: any) => {
+ const optionValue = option
if (option.value) {
- option.value = option.label
+ optionValue.value = option.label
}
})
}
+ if (item.fieldId === 'field_country') {
+ optionItems.default = item.valueConfigs?.default || optionItems.default
+ }
+
return optionItems
}
@@ -251,8 +253,10 @@ export const conversionSigleItem = (
type: item.type || '',
}
+ const customFieldItem = item
+
if (typeof item.fieldType === 'number') {
- item.fieldType = companyExtraFieldsType[item.fieldType]
+ customFieldItem.fieldType = companyExtraFieldsType[item.fieldType]
requiredItems.fieldType = item.fieldType
}
diff --git a/apps/storefront/src/pages/shoppingListDetails/components/ChooseOptionsDialog.tsx b/apps/storefront/src/pages/shoppingListDetails/components/ChooseOptionsDialog.tsx
index fc3f748f..da46b280 100644
--- a/apps/storefront/src/pages/shoppingListDetails/components/ChooseOptionsDialog.tsx
+++ b/apps/storefront/src/pages/shoppingListDetails/components/ChooseOptionsDialog.tsx
@@ -23,7 +23,6 @@ import {
currencyFormat,
snackbar,
} from '@/utils'
-// import { } from '@/utils/b3Product/b3Product'
import {
getBCPrice,
getProductInfoDisplayPrice,
@@ -383,7 +382,8 @@ export default function ChooseOptionsDialog(props: ChooseOptionsDialogProps) {
if (formFields.length > 0) {
const defaultValues: SimpleObject = formFields.reduce(
(value: SimpleObject, fields) => {
- value[fields.name] = fields.default
+ const formFieldValue = value
+ formFieldValue[fields.name] = fields.default
setValue(fields.name, fields.default)
return value
},
diff --git a/apps/storefront/src/pages/shoppingListDetails/components/QuickAdd.tsx b/apps/storefront/src/pages/shoppingListDetails/components/QuickAdd.tsx
index 8ba2052e..c10a34eb 100644
--- a/apps/storefront/src/pages/shoppingListDetails/components/QuickAdd.tsx
+++ b/apps/storefront/src/pages/shoppingListDetails/components/QuickAdd.tsx
@@ -390,7 +390,7 @@ export default function QuickAdd(props: AddToListContentProps) {
})
snackbar.error(
- b3Lang('shoppingList.quickAdd.skuNotAddable', {
+ b3Lang('shoppingList.quickAdd.skuLimitQuantity', {
numberLimit,
}),
{
diff --git a/apps/storefront/src/pages/shoppingListDetails/components/ReAddToCart.tsx b/apps/storefront/src/pages/shoppingListDetails/components/ReAddToCart.tsx
index 6539d364..360fe902 100644
--- a/apps/storefront/src/pages/shoppingListDetails/components/ReAddToCart.tsx
+++ b/apps/storefront/src/pages/shoppingListDetails/components/ReAddToCart.tsx
@@ -281,6 +281,7 @@ export default function ReAddToCart(props: ShoppingProductsProps) {
)
const requestArr: Promise[] = []
newProduct.forEach((product) => {
+ const item = product
const {
node: { quantity },
minQuantity = 0,
@@ -291,15 +292,15 @@ export default function ReAddToCart(props: ShoppingProductsProps) {
const quantityNumber = parseInt(`${quantity}`, 10) || 0
if (minQuantity !== 0 && quantityNumber < minQuantity) {
- product.node.quantity = minQuantity
+ item.node.quantity = minQuantity
} else if (maxQuantity !== 0 && quantityNumber > maxQuantity) {
- product.node.quantity = maxQuantity
+ item.node.quantity = maxQuantity
}
if (isStock !== '0' && stock && (quantity ? +quantity : 0) > stock) {
- product.node.quantity = stock
+ item.node.quantity = stock
}
- product.isValid = true
+ item.isValid = true
const qty = product?.node?.quantity ? +product.node.quantity : 0
diff --git a/apps/storefront/src/pages/shoppingListDetails/components/ShoppingDetailHeader.tsx b/apps/storefront/src/pages/shoppingListDetails/components/ShoppingDetailHeader.tsx
index 91e72dc0..9b82cd0a 100644
--- a/apps/storefront/src/pages/shoppingListDetails/components/ShoppingDetailHeader.tsx
+++ b/apps/storefront/src/pages/shoppingListDetails/components/ShoppingDetailHeader.tsx
@@ -109,7 +109,7 @@ function ShoppingDetailHeader(props: ShoppingDetailHeaderProps) {
sx={{
margin: 0,
color: customColor,
- m: '16px 0',
+ m: '0',
}}
>
{openAPPParams.shoppingListBtn !== 'add'
diff --git a/apps/storefront/src/pages/shoppingLists/AddEditShoppingLists.tsx b/apps/storefront/src/pages/shoppingLists/AddEditShoppingLists.tsx
index 2ecac09e..05aef5a2 100644
--- a/apps/storefront/src/pages/shoppingLists/AddEditShoppingLists.tsx
+++ b/apps/storefront/src/pages/shoppingLists/AddEditShoppingLists.tsx
@@ -80,8 +80,9 @@ function AddEditShoppingLists(
setAddUpdateLoading(true)
try {
const { description } = data
+ const submitData = data
if (description.indexOf('\n') > -1) {
- data.description = description.split('\n').join('\\n')
+ submitData.description = description.split('\n').join('\\n')
}
const params: Partial = {
...data,
@@ -105,8 +106,6 @@ function AddEditShoppingLists(
fn = isB2BUser ? duplicateB2BShoppingList : duplicateBcShoppingList
params.sampleShoppingListId = editData?.id || 0
successTip = b3Lang('shoppingLists.duplicateSuccess')
-
- // params.status = +role === 2 ? 30 : editData?.status
} else if (type === 'add') {
if (isB2BUser) {
params.status = +role === 2 ? 30 : 0
diff --git a/apps/storefront/src/pages/shoppingLists/ShoppingLists.tsx b/apps/storefront/src/pages/shoppingLists/ShoppingLists.tsx
index cef87a04..92717b89 100644
--- a/apps/storefront/src/pages/shoppingLists/ShoppingLists.tsx
+++ b/apps/storefront/src/pages/shoppingLists/ShoppingLists.tsx
@@ -79,10 +79,12 @@ function ShoppingLists() {
name: string
options: any[]
}) => {
- element.label = b3Lang(element.idLang)
+ const translatedInfo = element
+ translatedInfo.label = b3Lang(element.idLang)
if (element.name === 'status') {
element.options?.map((option) => {
- option.label = b3Lang(option.idLang)
+ const elementOption = option
+ elementOption.label = b3Lang(option.idLang)
return option
})
}
diff --git a/apps/storefront/src/pages/usermanagement/Usermanagement.tsx b/apps/storefront/src/pages/usermanagement/Usermanagement.tsx
index d6f2b882..a5b4941b 100644
--- a/apps/storefront/src/pages/usermanagement/Usermanagement.tsx
+++ b/apps/storefront/src/pages/usermanagement/Usermanagement.tsx
@@ -87,12 +87,14 @@ function Usermanagement() {
const fiterMoreInfo = getFilterMoreList(b3Lang)
const translatedFilterInfo = fiterMoreInfo.map((element) => {
+ const translatedItem = element
const translatedOptions = element.options?.map((option) => {
- option.label = b3Lang(option.idLang)
+ const elementOption = option
+ elementOption.label = b3Lang(option.idLang)
return option
})
- element.options = translatedOptions
+ translatedItem.options = translatedOptions
return element
})
diff --git a/apps/storefront/src/shared/routes/routes.ts b/apps/storefront/src/shared/routes/routes.ts
index 476571fd..2fbfa7fa 100644
--- a/apps/storefront/src/shared/routes/routes.ts
+++ b/apps/storefront/src/shared/routes/routes.ts
@@ -294,6 +294,13 @@ const firstLevelRouting: RouteFirstLevelItem[] = [
isProvider: false,
},
]
+
+const denyInvoiceRoles = [4, 99, 100]
+
+const invoiceFlag = 'invoice?invoiceId'
+
+const { hash, pathname, href } = window.location
+
const getAllowedRoutes = (globalState: GlobalState): RouteItem[] => {
const { isB2BUser, role, isAgenting, storefrontConfig, quoteConfig } =
globalState
@@ -388,7 +395,10 @@ const gotoAllowedAppPage = async (
gotoPage('/login?loginFlag=3&&closeIsLogout=1')
return
}
-
+ if (denyInvoiceRoles.includes(role) && href.includes(invoiceFlag)) {
+ gotoPage('/login?loginFlag=7')
+ return
+ }
try {
const {
data: { customer },
@@ -403,7 +413,6 @@ const gotoAllowedAppPage = async (
console.log(err)
}
- const { hash, pathname } = window.location
let url = hash.split('#')[1] || ''
if (
(!url && role !== 100 && pathname.includes('account.php')) ||
@@ -421,11 +430,12 @@ const gotoAllowedAppPage = async (
break
}
- const flag = routes.some(
- (item: RouteItem) =>
- (matchPath(item.path, url) || url.includes('invoice?')) &&
- item.permissions.includes(role)
- )
+ const flag = routes.some((item: RouteItem) => {
+ if (matchPath(item.path, url) || url.includes(invoiceFlag)) {
+ return item.permissions.includes(role)
+ }
+ return false
+ })
const isFirstLevelFlag = firstLevelRouting.some(
(item: RouteFirstLevelItem) => {
diff --git a/apps/storefront/src/shared/service/b2b/graphql/quote.ts b/apps/storefront/src/shared/service/b2b/graphql/quote.ts
index 840a1745..ab421073 100644
--- a/apps/storefront/src/shared/service/b2b/graphql/quote.ts
+++ b/apps/storefront/src/shared/service/b2b/graphql/quote.ts
@@ -138,7 +138,8 @@ const quoteCreate = (data: CustomFieldItems) => `mutation{
billingAddress: ${convertObjectToGraphql(data.billingAddress)}
contactInfo: ${convertObjectToGraphql(data.contactInfo)}
productList: ${convertArrayToGraphql(data.productList || [])},
- fileList: ${convertArrayToGraphql(data.fileList || [])}
+ fileList: ${convertArrayToGraphql(data.fileList || [])},
+ quoteTitle: "${data.quoteTitle}"
}) {
quote{
id,
diff --git a/apps/storefront/src/store/slices/global.ts b/apps/storefront/src/store/slices/global.ts
index 02089a97..19826a19 100644
--- a/apps/storefront/src/store/slices/global.ts
+++ b/apps/storefront/src/store/slices/global.ts
@@ -64,6 +64,8 @@ export interface GlabolState {
bcUrl?: string
cartNumber?: number
storeInfo?: StoreInfoProps
+ loginLandingLocation?: string
+ recordOpenHash?: string
blockPendingQuoteNonPurchasableOOS?: GlobalBlockPendingQuoteNonPurchasableOOS
}
@@ -101,6 +103,8 @@ const initialState: GlabolState = {
isEnableProduct: false,
isEnableRequest: false,
},
+ loginLandingLocation: '0',
+ recordOpenHash: '',
}
export const glabolSlice = createSlice({
@@ -147,6 +151,9 @@ export const glabolSlice = createSlice({
...(payload as GlobalBlockPendingQuoteNonPurchasableOOS),
}
},
+ setLoginLandingLocation: (state, { payload }: PayloadAction) => {
+ state.loginLandingLocation = payload as Draft
+ },
setHeadLessBcUrl: (state, { payload }: PayloadAction) => {
state.bcUrl = payload as Draft
},
@@ -171,6 +178,7 @@ export const {
setHeadLessBcUrl,
setCartNumber,
setStoreInfo,
+ setLoginLandingLocation,
} = glabolSlice.actions
export default glabolSlice.reducer
diff --git a/apps/storefront/src/utils/b3Login.ts b/apps/storefront/src/utils/b3Login.ts
new file mode 100644
index 00000000..ebba70d1
--- /dev/null
+++ b/apps/storefront/src/utils/b3Login.ts
@@ -0,0 +1,32 @@
+import { NavigateFunction } from 'react-router-dom'
+
+import { store } from '@/store'
+
+export const loginjump = (
+ navigate: NavigateFunction,
+ isClearSesion = false
+) => {
+ const {
+ global: { loginLandingLocation, recordOpenHash, setOpenPageFn },
+ } = store.getState()
+ if (loginLandingLocation === '1' && !recordOpenHash) {
+ navigate('/')
+ setOpenPageFn({
+ isOpen: false,
+ openUrl: '',
+ })
+ if (isClearSesion) window.sessionStorage.clear()
+ window.location.reload()
+ return false
+ }
+ if (loginLandingLocation === '1' && recordOpenHash) {
+ const hash = recordOpenHash.split('#')[1]
+ navigate(hash)
+
+ return false
+ }
+
+ return true
+}
+
+export default loginjump
diff --git a/apps/storefront/src/utils/b3Product/b3Product.ts b/apps/storefront/src/utils/b3Product/b3Product.ts
index 5de6fbf0..2a7da747 100644
--- a/apps/storefront/src/utils/b3Product/b3Product.ts
+++ b/apps/storefront/src/utils/b3Product/b3Product.ts
@@ -1,3 +1,4 @@
+import isEmpty from 'lodash-es/isEmpty'
import { v1 as uuid } from 'uuid'
import {
@@ -278,7 +279,6 @@ const setItemProductPrice = (newListProducts: ListItemProps[]) => {
taxClassId,
},
} = item
-
const rate = getTaxRate(taxClassId)
let singleCurrentPrice = currentProductPrices?.tax_exclusive || 0
@@ -1309,13 +1309,14 @@ export const getVariantInfoOOSAndPurchase = (productInfo: CustomFieldItems) => {
const variantSku = newProductInfo?.variantSku || newProductInfo?.sku
- const variants = newProductInfo?.productsSearch
+ const variants = !isEmpty(newProductInfo?.productsSearch)
? newProductInfo.productsSearch.variants
- : newProductInfo.variants
-
- const variant = variants.find((item: Variant) => item.sku === variantSku)
+ : newProductInfo?.variants || []
- if (variant?.sku) {
+ const variant = variants
+ ? variants.find((item: Variant) => item.sku === variantSku)
+ : {}
+ if (variant && variant?.sku) {
const {
purchasing_disabled: purchasingDisabled,
inventory_level: inventoryLevel,
@@ -1374,12 +1375,15 @@ export const getVariantInfoDisplayPrice = (
const variantSku =
option?.sku || newProductInfo?.variantSku || newProductInfo?.sku
- const newVariants = newProductInfo?.productsSearch
+ const newVariants = !isEmpty(newProductInfo?.productsSearch)
? newProductInfo.productsSearch.variants
- : newProductInfo.variants
+ : newProductInfo?.variants || []
+
+ const variant = newVariants
+ ? newVariants.find((item: Variant) => item.sku === variantSku)
+ : {}
- const variant = newVariants.find((item: Variant) => item.sku === variantSku)
- if (variant?.sku) {
+ if (variant && variant?.sku) {
const {
purchasing_disabled: purchasingDisabled,
inventory_level: inventoryLevel,
diff --git a/apps/storefront/src/utils/b3Product/shared/config.ts b/apps/storefront/src/utils/b3Product/shared/config.ts
index 6880eb67..83bafac7 100644
--- a/apps/storefront/src/utils/b3Product/shared/config.ts
+++ b/apps/storefront/src/utils/b3Product/shared/config.ts
@@ -687,6 +687,7 @@ export const addlineItems = (products: ProductsProps[]) => {
productId: node.productId,
variantId: node.variantId,
optionSelections: optionValue,
+ allOptions,
}
})
diff --git a/apps/storefront/src/utils/cartUtils.ts b/apps/storefront/src/utils/cartUtils.ts
index afb109e8..983d5126 100644
--- a/apps/storefront/src/utils/cartUtils.ts
+++ b/apps/storefront/src/utils/cartUtils.ts
@@ -24,16 +24,27 @@ export const handleSplitOptionId = (id: string | number) => {
const cartLineItems = (products: any) => {
const items = products.map((product: any) => {
- const { newSelectOptionList, quantity, optionSelections } = product
+ const {
+ newSelectOptionList,
+ quantity,
+ optionSelections,
+ allOptions = [],
+ } = product
let options = []
options = newSelectOptionList || optionSelections
-
const selectedOptions = options.reduce(
(a: any, c: any) => {
const optionValue = parseInt(c.optionValue, 10)
const splitOptionId = handleSplitOptionId(c.optionId)
-
- if (Number.isNaN(optionValue)) {
+ const productOption = allOptions.find((option: CustomFieldItems) => {
+ const id = option?.product_option_id || option?.id || ''
+ return id === splitOptionId
+ })
+ if (
+ Number.isNaN(optionValue) ||
+ productOption?.type === 'text' ||
+ productOption?.type === 'Text field'
+ ) {
a.textFields.push({
optionEntityId: splitOptionId,
text: c.optionValue,
@@ -121,7 +132,7 @@ export const callCart = async (
: null
const res =
- cartInfo && cartInfo.data.site.cart
+ cartInfo && cartInfo?.data?.site?.cart
? await updateCart(cartInfo, lineItems, storePlatform)
: await createNewShoppingCart(lineItems)
diff --git a/apps/storefront/src/utils/index.ts b/apps/storefront/src/utils/index.ts
index 016274bf..91ac82d2 100644
--- a/apps/storefront/src/utils/index.ts
+++ b/apps/storefront/src/utils/index.ts
@@ -64,6 +64,8 @@ export {
setStorefrontConfig,
} from './storefrontConfig'
+export { loginjump } from './b3Login'
+
export {
b2bPrintInvoice,
B3LStorage,
diff --git a/apps/storefront/src/utils/storefrontConfig.ts b/apps/storefront/src/utils/storefrontConfig.ts
index a7540b53..57365c87 100644
--- a/apps/storefront/src/utils/storefrontConfig.ts
+++ b/apps/storefront/src/utils/storefrontConfig.ts
@@ -16,6 +16,7 @@ import {
setBlockPendingAccountViewPrice,
setBlockPendingQuoteNonPurchasableOOS,
setEnteredInclusive,
+ setLoginLandingLocation,
setShowInclusiveTaxPrice,
setStoreInfo,
setTaxZoneRates,
@@ -145,6 +146,10 @@ const storeforntKeys: StoreforntKeysProps[] = [
key: 'quote_on_non_purchasable_product_page',
name: 'quoteOnNonPurchasableProductPageBtn',
},
+ {
+ key: 'login_landing_location',
+ name: 'loginLandingLocation',
+ },
]
const getTemPlateConfig = async (
@@ -165,12 +170,13 @@ const getTemPlateConfig = async (
const storeforntKey: StoreforntKeysProps | undefined = storeforntKeys.find(
(option) => option.key === item.key
)
+ const storefrontConfig = item
if (!isEmpty(storeforntKey)) {
if (storeforntKey.key === 'quote_logo') {
logo = item.value
}
if (storeforntKey.key === 'quote_on_product_page') {
- item.extraFields = {
+ storefrontConfig.extraFields = {
...item.extraFields,
locationSelector:
item.extraFields?.locationSelector || '.add-to-cart-buttons',
@@ -180,13 +186,13 @@ const getTemPlateConfig = async (
}
if (storeforntKey.key === 'quote_on_cart_page') {
- item.extraFields = {
+ storefrontConfig.extraFields = {
...item.extraFields,
classSelector: item.extraFields?.classSelector || 'button',
}
}
if (storeforntKey.key === 'masquerade_button') {
- item.extraFields = {
+ storefrontConfig.extraFields = {
...item.extraFields,
color: item.extraFields?.color || '#ED6C02',
location: item.extraFields?.location || ' bottomLeft',
@@ -196,7 +202,7 @@ const getTemPlateConfig = async (
}
if (storeforntKey.key === 'quote_floating_action_button') {
- item.extraFields = {
+ storefrontConfig.extraFields = {
...item.extraFields,
color: item.extraFields?.color || '#E00F36',
location: item.extraFields?.location || ' bottomRight',
@@ -206,7 +212,7 @@ const getTemPlateConfig = async (
}
if (storeforntKey.key === 'shopping_list_on_product_page') {
- item.extraFields = {
+ storefrontConfig.extraFields = {
...item.extraFields,
locationSelector:
item.extraFields?.locationSelector || '.add-to-cart-buttons',
@@ -245,7 +251,7 @@ const getTemPlateConfig = async (
}
if (storeforntKey.key === 'quote_on_non_purchasable_product_page') {
- item.extraFields = {
+ storefrontConfig.extraFields = {
...item.extraFields,
locationSelector:
item.extraFields?.locationSelector || '.add-to-cart-buttons',
@@ -262,6 +268,12 @@ const getTemPlateConfig = async (
)
}
+ if (storeforntKey.key === 'login_landing_location') {
+ store.dispatch(
+ setLoginLandingLocation(item?.extraFields?.location || '0')
+ )
+ }
+
;(obj as CustomFieldItems)[(storeforntKey as StoreforntKeysProps).name] =
{
...item.extraFields,
diff --git a/packages/global-b3/index.ts b/packages/global-b3/index.ts
index 96a1b957..7591a888 100644
--- a/packages/global-b3/index.ts
+++ b/packages/global-b3/index.ts
@@ -79,6 +79,9 @@ const globalB3 = {
setting: {
b2b_url: 'https://staging-v2.bundleb2b.net',
b2b_socket_url: 'https://staging-v2.bundleb2b.net',
+ store_hash: `1l3zp8c753`,
+ channel_id: 1,
+ b2b_client_id: 'r2x8j3tn54wduq47b4efct5tqxio5z2',
},
...localConfig(),
...(window?.B3CustomConfig || {}),
diff --git a/packages/lang/locales/en.json b/packages/lang/locales/en.json
index 474bdf97..3447a20a 100644
--- a/packages/lang/locales/en.json
+++ b/packages/lang/locales/en.json
@@ -518,6 +518,8 @@
"quoteDraft.contactInfo.email": "Email",
"quoteDraft.contactInfo.companyName": "Company name",
"quoteDraft.contactInfo.phone": "Phone",
+ "quoteDraft.contactInfo.quoteTitle": "Quote Title",
+ "quoteDraft.quoteInfo.quoteTitle": "Quote title:",
"quoteDraft.contactInfo.emailExists": "Email already exists",
"quoteDraft.contactInfo.contact": "Contact",
"quoteDraft.quoteAddress.chooseFromSaved": "Choose from saved",
@@ -641,6 +643,7 @@
"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.",
"login.loginTipInfo.accountPrelaunch": "You can not login to the Buyer Portal while the store is in prelaunch or maintenance mode. Please set the store live, or login inside the customer admin panel.",
"login.loginText.forgotPasswordText": "Forgot your password?",
+ "login.loginText.invoiceErrorTip": "Please log in to view your invoice",
"login.loginText.deviceCrowdingLogIn": "The user has logged out, please log in again",
"login.loginText.password": "Password",
"login.registerLogo": "register Logo",