Skip to content

Commit 2ff6900

Browse files
authored
Merge pull request #2825 from Civolilah/feature/2057-update-modal
[Feature] Implementing Update Modal
2 parents 1d76b92 + 965404f commit 2ff6900

File tree

1 file changed

+248
-0
lines changed

1 file changed

+248
-0
lines changed
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
/**
2+
* Invoice Ninja (https://invoiceninja.com).
3+
*
4+
* @link https://github.com/invoiceninja/invoiceninja source repository
5+
*
6+
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
7+
*
8+
* @license https://www.elastic.co/licensing/elastic-license
9+
*/
10+
11+
import { Button, InputField } from '$app/components/forms';
12+
import { AxiosError } from 'axios';
13+
import { endpoint } from '$app/common/helpers';
14+
import { request } from '$app/common/helpers/request';
15+
import { ValidationBag } from '$app/common/interfaces/validation-bag';
16+
import { cloneDeep, set } from 'lodash';
17+
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
18+
import { useTranslation } from 'react-i18next';
19+
import { Spinner } from '$app/components/Spinner';
20+
import { toast } from '$app/common/helpers/toast/toast';
21+
import { Modal } from '$app/components/Modal';
22+
import { TabGroup } from '$app/components/TabGroup';
23+
import { Company } from '$app/common/interfaces/company.interface';
24+
import { useDispatch } from 'react-redux';
25+
import {
26+
resetChanges,
27+
updateRecord,
28+
} from '$app/common/stores/slices/company-users';
29+
import { CountrySelector } from './CountrySelector';
30+
import { useCurrentCompany } from '$app/common/hooks/useCurrentCompany';
31+
32+
interface Props {
33+
visible: boolean;
34+
setVisible: Dispatch<SetStateAction<boolean>>;
35+
}
36+
37+
export function CompanyUpdateModal({ visible, setVisible }: Props) {
38+
const [t] = useTranslation();
39+
40+
const dispatch = useDispatch();
41+
42+
const currentCompany = useCurrentCompany();
43+
44+
const [company, setCompany] = useState<Company>();
45+
const [errors, setErrors] = useState<ValidationBag>();
46+
const [isFormBusy, setIsFormBusy] = useState<boolean>(false);
47+
48+
const handleChange = (property: string, value: string) => {
49+
setErrors(undefined);
50+
51+
setCompany((company) => company && set({ ...company }, property, value));
52+
};
53+
54+
const handleClose = () => {
55+
setErrors(undefined);
56+
setCompany(undefined);
57+
setVisible(false);
58+
};
59+
60+
const onSave = () => {
61+
if (!isFormBusy) {
62+
toast.processing();
63+
64+
setIsFormBusy(true);
65+
setErrors(undefined);
66+
67+
request(
68+
'PUT',
69+
endpoint('/api/v1/companies/:id', { id: company?.id }),
70+
company
71+
)
72+
.then((response) => {
73+
toast.success('updated_settings');
74+
75+
handleClose();
76+
77+
dispatch(resetChanges('company'));
78+
dispatch(
79+
updateRecord({ object: 'company', data: response.data.data })
80+
);
81+
})
82+
.catch((error: AxiosError<ValidationBag>) => {
83+
if (error.response?.status === 422) {
84+
setErrors(error.response.data);
85+
toast.dismiss();
86+
}
87+
})
88+
.finally(() => setIsFormBusy(false));
89+
}
90+
};
91+
92+
useEffect(() => {
93+
if (currentCompany && visible && !company) {
94+
setCompany(cloneDeep(currentCompany));
95+
}
96+
}, [visible, currentCompany]);
97+
98+
useEffect(() => {
99+
if (currentCompany && !visible && company) {
100+
setCompany(cloneDeep(currentCompany));
101+
}
102+
}, [visible, currentCompany]);
103+
104+
return (
105+
<Modal
106+
title={t('company_details')!}
107+
visible={visible}
108+
onClose={() => setVisible(false)}
109+
size="extraSmall"
110+
renderTransitionChildAsFragment
111+
overflowVisible
112+
withoutVerticalMargin
113+
withoutHorizontalPadding
114+
withoutBorderLine
115+
disableClosing={isFormBusy}
116+
>
117+
<div className="flex flex-col">
118+
{company ? (
119+
<TabGroup
120+
tabs={[t('details'), t('address')]}
121+
width="full"
122+
withHorizontalPadding
123+
horizontalPaddingWidth="1.5rem"
124+
>
125+
<div className="flex flex-col space-y-3 px-4 sm:px-6">
126+
<InputField
127+
label={t('name')}
128+
value={company?.settings?.name || ''}
129+
onValueChange={(value) => handleChange('settings.name', value)}
130+
errorMessage={errors?.errors['settings.name']}
131+
/>
132+
133+
<InputField
134+
label={t('id_number')}
135+
value={company?.settings?.id_number || ''}
136+
onValueChange={(value) =>
137+
handleChange('settings.id_number', value.toString())
138+
}
139+
disabled={company?.legal_entity_id !== null}
140+
errorMessage={errors?.errors['settings.id_number']}
141+
/>
142+
143+
<InputField
144+
label={t('vat_number')}
145+
value={company?.settings?.vat_number || ''}
146+
onValueChange={(value) =>
147+
handleChange('settings.vat_number', value.toString())
148+
}
149+
disabled={company?.legal_entity_id !== null}
150+
errorMessage={errors?.errors['settings.vat_number']}
151+
/>
152+
153+
{company?.legal_entity_id ? (
154+
<p className="mt-2">{t('changing_vat_and_id_number_note')}</p>
155+
) : null}
156+
157+
<InputField
158+
label={t('website')}
159+
value={company?.settings?.website || ''}
160+
onValueChange={(value) =>
161+
handleChange('settings.website', value.toString())
162+
}
163+
errorMessage={errors?.errors['settings.website']}
164+
/>
165+
166+
<InputField
167+
label={t('email')}
168+
value={company?.settings?.email || ''}
169+
onValueChange={(value) =>
170+
handleChange('settings.email', value.toString())
171+
}
172+
errorMessage={errors?.errors['settings.email']}
173+
/>
174+
175+
<InputField
176+
label={t('phone')}
177+
value={company?.settings?.phone || ''}
178+
onValueChange={(value) =>
179+
handleChange('settings.phone', value.toString())
180+
}
181+
errorMessage={errors?.errors['settings.phone']}
182+
/>
183+
</div>
184+
185+
<div className="flex flex-col space-y-3 px-4 sm:px-6">
186+
<InputField
187+
label={t('address1')}
188+
value={company?.settings?.address1 || ''}
189+
onValueChange={(value) =>
190+
handleChange('settings.address1', value)
191+
}
192+
errorMessage={errors?.errors['settings.address1']}
193+
/>
194+
195+
<InputField
196+
label={t('address2')}
197+
value={company?.settings?.address2 || ''}
198+
onValueChange={(value) =>
199+
handleChange('settings.address2', value)
200+
}
201+
errorMessage={errors?.errors['settings.address2']}
202+
/>
203+
204+
<InputField
205+
label={t('city')}
206+
value={company?.settings?.city || ''}
207+
onValueChange={(value) => handleChange('settings.city', value)}
208+
errorMessage={errors?.errors['settings.city']}
209+
/>
210+
211+
<InputField
212+
label={t('state')}
213+
value={company?.settings?.state || ''}
214+
onValueChange={(value) => handleChange('settings.state', value)}
215+
errorMessage={errors?.errors['settings.state']}
216+
/>
217+
218+
<InputField
219+
label={t('postal_code')}
220+
value={company?.settings?.postal_code || ''}
221+
onValueChange={(value) =>
222+
handleChange('settings.postal_code', value)
223+
}
224+
errorMessage={errors?.errors['settings.postal_code']}
225+
/>
226+
227+
<CountrySelector
228+
label={t('country')}
229+
value={company?.settings?.country_id || ''}
230+
onChange={(value) => handleChange('settings.country_id', value)}
231+
errorMessage={errors?.errors['settings.country_id']}
232+
dismissable
233+
/>
234+
</div>
235+
</TabGroup>
236+
) : (
237+
<Spinner />
238+
)}
239+
240+
<div className="flex justify-end mt-2 px-4 sm:px-6">
241+
<Button behavior="button" onClick={onSave} disabled={isFormBusy}>
242+
{t('save')}
243+
</Button>
244+
</div>
245+
</div>
246+
</Modal>
247+
);
248+
}

0 commit comments

Comments
 (0)