Skip to content

Commit

Permalink
feat: quote add extra fields (#153)
Browse files Browse the repository at this point in the history
* feat: quote add extra fields

* fix: optimize and fix bugs

* fix: quote extra fields fix bugs

* fix: submit quote info

* feat: quote add cc email

* fix: quoteExtraFieldsConfig graphql add channelId
  • Loading branch information
BrianJiang2021 authored Jan 6, 2025
1 parent 4c610c7 commit 4428b81
Show file tree
Hide file tree
Showing 16 changed files with 844 additions and 71 deletions.
11 changes: 11 additions & 0 deletions apps/storefront/src/components/B3CustomForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Grid } from '@mui/material';

import B3UI from './form/ui';
import {
B2BControlMultiTextField,
B3ControlAutocomplete,
B3ControlCheckbox,
B3ControlFileUpload,
Expand Down Expand Up @@ -91,6 +92,16 @@ export default function B3CustomForm(props: B3UI.B3CustomFormProps) {
getValues={getValues}
/>
)}
{['multiInputText'].includes(fieldType) && (
<B2BControlMultiTextField
{...props}
{...field}
errors={errors}
control={control}
setValue={setValue}
getValues={getValues}
/>
)}
</>
</Grid>
);
Expand Down
222 changes: 222 additions & 0 deletions apps/storefront/src/components/form/B2BControlMultiTextField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
import { KeyboardEvent } from 'react';
import { Controller } from 'react-hook-form';
import { useB3Lang } from '@b3/lang';
import { Add, Clear } from '@mui/icons-material';
import { Box, TextField, Typography } from '@mui/material';
import { concat, debounce, uniq } from 'lodash-es';
import isEmpty from 'lodash-es/isEmpty';

import Form from './ui';

export default function B2BControlMultiTextField({ control, errors, ...rest }: Form.B3UIProps) {
const {
fieldType,
isAutoComplete = false,
name,
default: defaultValue,
required,
label,
validate,
variant,
rows,
min,
max,
minLength,
maxLength,
fullWidth = true,
disabled,
labelName,
size,
readOnly,
sx = {},
isTip = false,
tipText = '',
extraPadding,
isEnterTrigger,
handleSave,
getValues,
InputProps = {},
existValue,
setError,
setValue,
} = rest;
const b3Lang = useB3Lang();

const requiredText = b3Lang('global.validate.required', {
label: labelName || label,
});

const fieldsProps = {
type: fieldType,
name,
defaultValue,
rules: {
required: required && requiredText,
validate: validate && ((v: string) => validate(v, b3Lang)),
},
control,
};

const textField = {
type: fieldType,
name,
label,
rows,
disabled,
variant,
fullWidth,
required,
size,
};

const otherProps = {
inputProps: {
min,
max,
maxLength,
minLength,
readOnly,
},
};

const handleAddNewItem = () => {
const currentValue = getValues(name).trim();
const isValidValue = validate(currentValue, b3Lang);
if (isValidValue) {
setError(name, {
type: 'custom',
message: isValidValue,
});
} else {
const newItems = uniq(concat(existValue, currentValue.length ? [currentValue] : []));

setValue(name, '');
if (handleSave) handleSave(newItems);
}
};

const handleDelete = (currentItem: string) => {
const newItems = existValue.filter((item: string) => item !== currentItem);
if (handleSave) handleSave(newItems);
};

const handleKeyDown = debounce((event: KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'Enter') {
handleAddNewItem();
} else {
event.preventDefault();
}
}, 300);

const autoCompleteFn = () => {
if (!isAutoComplete) {
return {
autoComplete: 'off',
};
}
return {};
};

return ['multiInputText'].includes(fieldType) ? (
<Box>
{labelName && (
<Box
sx={{
mb: 1,
}}
>
{`${labelName} :`}
</Box>
)}
<Controller
key={fieldsProps.name}
{...fieldsProps}
render={({ field: { ...rest } }) => (
<TextField
key={textField.name}
{...textField}
{...rest}
{...otherProps}
sx={{
color: disabled ? 'rgba(0, 0, 0, 0.38)' : 'rgba(0, 0, 0, 0.6)',
...sx,
'& input': {
...extraPadding,
},
}}
error={!!errors[name]}
helperText={(errors as any)[name] ? (errors as any)[name].message : null}
onKeyDown={isEnterTrigger ? handleKeyDown : () => {}}
InputProps={
!isEmpty(InputProps)
? { ...InputProps }
: {
endAdornment: (
<Add
onClick={handleAddNewItem}
sx={{
cursor: 'pointer',
}}
/>
),
}
}
{...autoCompleteFn()}
/>
)}
/>
{existValue.length ? (
<Box
sx={{
display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap',

'& div': {
marginRight: '10px',
},
}}
>
{existValue.map((item: string) => (
<Typography
key={item}
sx={{
display: 'flex',
alignItems: 'center',
border: '1px solid #E0E0E0',
borderRadius: '4px',
padding: '4px',
marginRight: '10px',
marginTop: '10px',
wordBreak: 'break-all',

'& svg': {
marginLeft: '10px',
cursor: 'pointer',
},
}}
>
{item}
<Clear
onClick={() => {
handleDelete(item);
}}
/>
</Typography>
))}
</Box>
) : null}
{isTip && (
<Box
sx={{
fontSize: '12px',
color: 'rgba(0, 0, 0, 0.6)',
marginTop: '0.5rem',
}}
>
{tipText}
</Box>
)}
</Box>
) : null;
}
1 change: 1 addition & 0 deletions apps/storefront/src/components/form/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export { default as B3ControlSelect } from './B3ControlSelect';
export { default as B3ControlSwatchRadio } from './B3ControlSwatchRadio';
export { default as B3ControlTextField } from './B3ControlTextField';
export { default as B3ControlAutocomplete } from './B3ControlAutocomplete';
export { default as B2BControlMultiTextField } from './B2BControlMultiTextField';
48 changes: 46 additions & 2 deletions apps/storefront/src/pages/QuoteDetail/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
useAppSelector,
} from '@/store';
import { Currency } from '@/types';
import { QuoteExtraFieldsData } from '@/types/quotes';
import { snackbar } from '@/utils';
import { getVariantInfoOOSAndPurchase } from '@/utils/b3Product/b3Product';
import { conversionProductsList } from '@/utils/b3Product/shared/config';
Expand All @@ -40,6 +41,7 @@ import QuoteInfo from '../quote/components/QuoteInfo';
import QuoteNote from '../quote/components/QuoteNote';
import QuoteTermsAndConditions from '../quote/components/QuoteTermsAndConditions';
import { ProductInfoProps } from '../quote/shared/config';
import getB2BQuoteExtraFields from '../quote/utils/getQuoteExtraFields';
import { handleQuoteCheckout } from '../quote/utils/quoteCheckout';

function QuoteDetail() {
Expand Down Expand Up @@ -237,6 +239,26 @@ function QuoteDetail() {
return undefined;
};

const getQuoteExtraFields = async (currentExtraFields: QuoteExtraFieldsData[]) => {
const extraFieldsInfo = await getB2BQuoteExtraFields();
const quoteCurrentExtraFields: QuoteExtraFieldsData[] = [];
if (extraFieldsInfo.length) {
extraFieldsInfo.forEach((item) => {
const extraField = item;
const currentExtraField = currentExtraFields.find(
(field: QuoteExtraFieldsData) => field.fieldName === extraField.name,
);

quoteCurrentExtraFields.push({
fieldName: extraField.name || '',
fieldValue: currentExtraField?.fieldValue || extraField.default,
});
});
}

return quoteCurrentExtraFields;
};

const getQuoteDetail = async () => {
setIsRequestLoading(true);
setIsShowFooter(false);
Expand All @@ -254,8 +276,12 @@ function QuoteDetail() {

const { quote } = await fn(data);
const productsWithMoreInfo = await handleGetProductsById(quote.productsList);
const quoteExtraFieldInfos = await getQuoteExtraFields(quote.extraFields);

setQuoteDetail(quote);
setQuoteDetail({
...quote,
extraFields: quoteExtraFieldInfos,
});
setQuoteSummary({
originalSubtotal: quote.subtotal,
discount: quote.discount,
Expand Down Expand Up @@ -512,6 +538,24 @@ function QuoteDetail() {
return true;
};

const quoteAndExtraFieldsInfo = useMemo(() => {
const currentExtraFields = quoteDetail?.extraFields?.map(
(field: { fieldName: string; fieldValue: string | number }) => ({
fieldName: field.fieldName,
value: field.fieldValue,
}),
);

return {
info: {
quoteTitle: quoteDetail?.quoteTitle || '',
referenceNumber: quoteDetail?.referenceNumber || '',
},
extraFields: currentExtraFields || [],
recipients: quoteDetail?.recipients || [],
};
}, [quoteDetail]);

useScrollBar(false);

return (
Expand All @@ -531,7 +575,6 @@ function QuoteDetail() {
exportPdf={exportPdf}
printQuote={printQuote}
role={role}
quoteTitle={quoteDetail.quoteTitle}
salesRepInfo={quoteDetail.salesRepInfo}
/>

Expand All @@ -541,6 +584,7 @@ function QuoteDetail() {
}}
>
<QuoteInfo
quoteAndExtraFieldsInfo={quoteAndExtraFieldsInfo}
contactInfo={quoteDetail.contactInfo}
shippingAddress={quoteDetail.shippingAddress}
billingAddress={quoteDetail.billingAddress}
Expand Down
Loading

0 comments on commit 4428b81

Please sign in to comment.