Skip to content

Commit ec255d8

Browse files
authored
Merge pull request #126 from ameerul-deriv/FEQ-2343-fix-the-issues-on-my-profile-page
Ameerul / FEQ-2343 Fix the issues on My profile page
2 parents adf32fe + 2a8ce4c commit ec255d8

File tree

18 files changed

+255
-136
lines changed

18 files changed

+255
-136
lines changed

src/components/Modals/BlockUnblockUserModal/BlockUnblockUserModal.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const BlockUnblockUserModal = ({
4444
// eslint-disable-next-line react-hooks/exhaustive-deps
4545
}, [isSuccess, onClickBlocked, unblockIsSuccess, unblockError, error, setErrorMessage]);
4646

47+
const textSize = isMobile ? 'md' : 'sm';
4748
const getModalTitle = () => (isBlocked ? `Unblock ${advertiserName}?` : `Block ${advertiserName}?`);
4849

4950
const getModalContent = () =>
@@ -81,7 +82,7 @@ const BlockUnblockUserModal = ({
8182
</Text>
8283
</Modal.Header>
8384
<Modal.Body>
84-
<Text as='p' className='px-[1.6rem] lg:px-[2.4rem]' size={isMobile ? 'md' : 'sm'}>
85+
<Text as='p' className='px-[1.6rem] lg:px-[2.4rem]' size={textSize}>
8586
{getModalContent()}
8687
</Text>
8788
</Modal.Body>
@@ -91,12 +92,12 @@ const BlockUnblockUserModal = ({
9192
color='black'
9293
onClick={onRequestClose}
9394
size='lg'
94-
textSize='sm'
95+
textSize={textSize}
9596
variant='outlined'
9697
>
9798
<Localize i18n_default_text='Cancel' />
9899
</Button>
99-
<Button onClick={onClickBlockUnblock} size='lg' textSize='sm'>
100+
<Button onClick={onClickBlockUnblock} size='lg' textSize={textSize}>
100101
{isBlocked ? <Localize i18n_default_text='Unblock' /> : <Localize i18n_default_text='Block' />}
101102
</Button>
102103
</Modal.Footer>

src/components/PaymentMethodCard/PaymentMethodCard.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const PaymentMethodCard = ({
6363
isEditable={isEditable}
6464
isSelectable={!isEditable && toAdd}
6565
isSelected={isSelected}
66+
medium={medium}
6667
onDeletePaymentMethod={onDeletePaymentMethod}
6768
onEditPaymentMethod={onEditPaymentMethod}
6869
onSelectPaymentMethod={() => onSelectPaymentMethodCard?.(Number(paymentMethod.id))}

src/components/PaymentMethodForm/PaymentMethodForm.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const PaymentMethodForm = ({
3131
}: TPaymentMethodFormProps) => {
3232
const {
3333
control,
34-
formState: { isDirty, isSubmitting, isValid },
34+
formState: { dirtyFields, isDirty, isSubmitting, isValid },
3535
handleSubmit,
3636
reset,
3737
} = useForm({ mode: 'all' });
@@ -63,8 +63,9 @@ const PaymentMethodForm = ({
6363
}));
6464
return listItems || [];
6565
}, [availablePaymentMethods]);
66+
6667
const handleGoBack = () => {
67-
if (isDirty) {
68+
if (Object.keys(dirtyFields).length) {
6869
setIsError(true);
6970
} else {
7071
onResetFormState();

src/components/Search/Search.scss

+13
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22
width: 100%;
33
background-color: #fff;
44

5+
input[type='search']::-webkit-search-cancel-button {
6+
display: none;
7+
}
8+
9+
rect {
10+
fill: #999;
11+
}
12+
513
.deriv-input {
614
padding: 6px 8px;
715
font-size: 1.4rem;
@@ -30,5 +38,10 @@
3038
&__label {
3139
left: 3rem;
3240
}
41+
42+
&__right-content {
43+
display: flex;
44+
align-items: center;
45+
}
3346
}
3447
}

src/components/Search/Search.tsx

+25-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { useCallback } from 'react';
1+
import { useCallback, useState } from 'react';
22
import clsx from 'clsx';
3-
import { LabelPairedSearchMdRegularIcon } from '@deriv/quill-icons';
3+
import { LabelPairedSearchMdRegularIcon, LegacyCloseCircle1pxIcon } from '@deriv/quill-icons';
44
import { Input } from '@deriv-com/ui';
55
import './Search.scss';
66

@@ -14,6 +14,8 @@ type TSearchProps = {
1414

1515
//TODO: replace the component with deriv shared component
1616
const Search = ({ delayTimer = 500, hideBorder = false, name, onSearch, placeholder }: TSearchProps) => {
17+
const [searchValue, setSearchValue] = useState('');
18+
1719
const debounce = (func: (value: string) => void, delay: number) => {
1820
let timer: ReturnType<typeof setTimeout>;
1921
return (value: string) => {
@@ -22,22 +24,37 @@ const Search = ({ delayTimer = 500, hideBorder = false, name, onSearch, placehol
2224
};
2325
};
2426

27+
// eslint-disable-next-line react-hooks/exhaustive-deps
2528
const debouncedOnSearch = useCallback(debounce(onSearch, delayTimer), [onSearch]);
2629

30+
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
31+
const value = event.target.value;
32+
setSearchValue(value);
33+
debouncedOnSearch(value);
34+
};
35+
36+
const clearSearch = () => {
37+
setSearchValue('');
38+
debouncedOnSearch('');
39+
};
40+
2741
return (
28-
<form
29-
className='search'
30-
onChange={event => debouncedOnSearch((event.target as HTMLInputElement).value)}
31-
role='search'
32-
>
42+
<div className='search'>
3343
<Input
3444
className={clsx({ 'border-none': hideBorder })}
3545
label={placeholder}
3646
leftPlaceholder={<LabelPairedSearchMdRegularIcon />}
3747
name={name}
48+
onChange={handleInputChange}
49+
rightPlaceholder={
50+
searchValue && (
51+
<LegacyCloseCircle1pxIcon className='cursor-pointer' iconSize='xs' onClick={clearSearch} />
52+
)
53+
}
3854
type='search'
55+
value={searchValue}
3956
/>
40-
</form>
57+
</div>
4158
);
4259
};
4360

src/pages/my-profile/screens/MyProfile/MyProfile.scss

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
flex-direction: column;
44
padding-top: 2.4rem;
55
overflow-y: auto;
6-
height: calc(100vh - 19rem);
6+
max-height: calc(100vh - 19rem);
77

88
@include mobile {
99
padding: 0;
10+
max-height: calc(100vh - 11rem);
1011
}
1112

1213
&__tabs {

src/pages/my-profile/screens/MyProfileAdDetails/MyProfileAdDetails.scss

+13
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,21 @@
2121

2222
& .deriv-textarea {
2323
&__footer {
24+
justify-content: space-between;
25+
2426
& .deriv-text {
2527
font-size: 1.2rem;
28+
margin-top: -0.5rem;
29+
margin-left: 1rem;
30+
31+
@include mobile {
32+
margin-top: -0.2rem;
33+
line-height: 1.4rem;
34+
}
35+
36+
&:only-child {
37+
margin-left: auto;
38+
}
2639
}
2740
}
2841
}

src/pages/my-profile/screens/MyProfileAdDetails/MyProfileAdDetails.tsx

+81-104
Original file line numberDiff line numberDiff line change
@@ -1,133 +1,110 @@
1-
import { Dispatch, SetStateAction, useEffect, useMemo, useState } from 'react';
1+
import { useEffect } from 'react';
2+
import clsx from 'clsx';
3+
import { debounce } from 'lodash';
4+
import { Control, FieldValues, useForm } from 'react-hook-form';
25
import { FullPageMobileWrapper } from '@/components';
36
import { api } from '@/hooks';
47
import { useQueryString } from '@/hooks/custom-hooks';
58
import { LabelPairedCheckMdFillIcon } from '@deriv/quill-icons';
6-
import { Localize, useTranslations } from '@deriv-com/translations';
7-
import { Button, Loader, Text, TextArea, useDevice } from '@deriv-com/ui';
9+
import { Localize } from '@deriv-com/translations';
10+
import { Button, Loader, Text, useDevice } from '@deriv-com/ui';
11+
import { MyProfileAdDetailsTextArea } from './MyProfileAdDetailsTextArea';
812
import './MyProfileAdDetails.scss';
913

10-
type TMYProfileAdDetailsTextAreaProps = {
11-
advertDescription: string;
12-
contactInfo: string;
13-
setAdvertDescription: Dispatch<SetStateAction<string>>;
14-
setContactInfo: Dispatch<SetStateAction<string>>;
15-
};
16-
17-
const MyProfileAdDetailsTextArea = ({
18-
advertDescription,
19-
contactInfo,
20-
setAdvertDescription,
21-
setContactInfo,
22-
}: TMYProfileAdDetailsTextAreaProps) => {
23-
const { localize } = useTranslations();
24-
const { isMobile } = useDevice();
25-
const textSize = isMobile ? 'md' : 'sm';
26-
return (
27-
<>
28-
<TextArea
29-
data-testid='dt_profile_ad_details_contact'
30-
label={localize('Contact details')}
31-
maxLength={300}
32-
onChange={e => setContactInfo(e.target.value)}
33-
shouldShowCounter
34-
textSize={textSize}
35-
value={contactInfo}
36-
/>
37-
<TextArea
38-
data-testid='dt_profile_ad_details_description'
39-
hint={localize('This information will be visible to everyone.')}
40-
label={localize('Instructions')}
41-
maxLength={300}
42-
onChange={e => setAdvertDescription(e.target.value)}
43-
shouldShowCounter
44-
textSize={textSize}
45-
value={advertDescription}
46-
/>
47-
</>
48-
);
49-
};
50-
5114
const MyProfileAdDetails = () => {
5215
const { data: advertiserInfo, isLoading } = api.advertiser.useGetInfo();
53-
const { isPending, mutate: updateAdvertiser } = api.advertiser.useUpdate();
54-
const [contactInfo, setContactInfo] = useState('');
55-
const [advertDescription, setAdvertDescription] = useState('');
16+
const { isSuccess, mutate: updateAdvertiser, reset } = api.advertiser.useUpdate();
5617
const { isMobile } = useDevice();
5718
const { setQueryString } = useQueryString();
5819

59-
const hasUpdated = useMemo(() => {
60-
return (
61-
contactInfo !== advertiserInfo?.contact_info ||
62-
advertDescription !== advertiserInfo?.default_advert_description
63-
);
64-
}, [advertiserInfo?.contact_info, advertiserInfo?.default_advert_description, contactInfo, advertDescription]);
20+
const {
21+
control,
22+
formState: { isDirty, isValid },
23+
getValues,
24+
handleSubmit,
25+
reset: resetForm,
26+
watch,
27+
} = useForm({
28+
defaultValues: {
29+
ad_details_contact: advertiserInfo?.contact_info,
30+
ad_details_description: advertiserInfo?.default_advert_description,
31+
},
32+
mode: 'onChange',
33+
});
34+
35+
const debouncedReset = debounce(() => {
36+
reset();
37+
resetForm(watch(), { keepDefaultValues: false, keepDirty: false, keepValues: false });
38+
}, 2000);
39+
40+
const isButtonDisabled = !isDirty || !isValid || isSuccess;
41+
const saveButtonClass = clsx({ 'my-profile-ad-details__button--submitting': isSuccess });
42+
const buttonIcon = isSuccess ? <LabelPairedCheckMdFillIcon fill='#fff' /> : null;
6543

6644
useEffect(() => {
67-
setContactInfo(advertiserInfo?.contact_info || '');
68-
setAdvertDescription(advertiserInfo?.default_advert_description || '');
69-
}, [advertiserInfo]);
45+
if (isSuccess) debouncedReset();
46+
}, [debouncedReset, isSuccess]);
7047

7148
const submitAdDetails = () => {
7249
updateAdvertiser({
73-
contact_info: contactInfo,
74-
default_advert_description: advertDescription,
50+
contact_info: getValues('ad_details_contact'),
51+
default_advert_description: getValues('ad_details_description'),
7552
});
7653
};
7754

7855
if (isLoading && !advertiserInfo) return <Loader className='mt-16' />;
7956

8057
if (isMobile) {
8158
return (
82-
<FullPageMobileWrapper
83-
className='my-profile-ad-details__mobile-wrapper'
84-
onBack={() =>
85-
setQueryString({
86-
tab: 'default',
87-
})
88-
}
89-
renderFooter={() => (
90-
<Button disabled={!hasUpdated} isFullWidth onClick={submitAdDetails} size='lg'>
91-
<Localize i18n_default_text='Save' />
92-
</Button>
93-
)}
94-
renderHeader={() => (
95-
<Text size='lg' weight='bold'>
96-
<Localize i18n_default_text='Ad Details ' />
97-
</Text>
98-
)}
99-
>
100-
<div className='my-profile-ad-details'>
101-
<MyProfileAdDetailsTextArea
102-
advertDescription={advertDescription}
103-
contactInfo={contactInfo}
104-
setAdvertDescription={setAdvertDescription}
105-
setContactInfo={setContactInfo}
106-
/>
107-
</div>
108-
</FullPageMobileWrapper>
59+
<form onSubmit={handleSubmit(submitAdDetails)}>
60+
<FullPageMobileWrapper
61+
className='my-profile-ad-details__mobile-wrapper'
62+
onBack={() =>
63+
setQueryString({
64+
tab: 'default',
65+
})
66+
}
67+
renderFooter={() => (
68+
<Button
69+
className={saveButtonClass}
70+
disabled={!isDirty || !isValid || isSuccess}
71+
icon={buttonIcon}
72+
isFullWidth
73+
size='lg'
74+
>
75+
<Localize i18n_default_text='Save' />
76+
</Button>
77+
)}
78+
renderHeader={() => (
79+
<Text size='lg' weight='bold'>
80+
<Localize i18n_default_text='Ad Details ' />
81+
</Text>
82+
)}
83+
>
84+
<div className='my-profile-ad-details'>
85+
<MyProfileAdDetailsTextArea control={control as unknown as Control<FieldValues>} />
86+
</div>
87+
</FullPageMobileWrapper>
88+
</form>
10989
);
11090
}
91+
11192
return (
112-
<div className='my-profile-ad-details'>
113-
<MyProfileAdDetailsTextArea
114-
advertDescription={advertDescription}
115-
contactInfo={contactInfo}
116-
setAdvertDescription={setAdvertDescription}
117-
setContactInfo={setContactInfo}
118-
/>
119-
<div className='my-profile-ad-details__border' />
120-
<Button
121-
className={isPending ? 'my-profile-ad-details__button--submitting' : ''}
122-
disabled={!hasUpdated || isPending}
123-
icon={isPending ? <LabelPairedCheckMdFillIcon fill='#fff' /> : null}
124-
onClick={submitAdDetails}
125-
size='lg'
126-
textSize='sm'
127-
>
128-
<Localize i18n_default_text='Save' />
129-
</Button>
130-
</div>
93+
<form onSubmit={handleSubmit(submitAdDetails)}>
94+
<div className='my-profile-ad-details'>
95+
<MyProfileAdDetailsTextArea control={control as unknown as Control<FieldValues>} />
96+
<div className='my-profile-ad-details__border' />
97+
<Button
98+
className={saveButtonClass}
99+
disabled={isButtonDisabled}
100+
icon={buttonIcon}
101+
size='lg'
102+
textSize='sm'
103+
>
104+
<Localize i18n_default_text='Save' />
105+
</Button>
106+
</div>
107+
</form>
131108
);
132109
};
133110

0 commit comments

Comments
 (0)