1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-19 08:52:25 +01:00

Add form validation

This commit is contained in:
vincent 2022-05-04 14:57:31 +02:00
parent d2ed4d4bd8
commit 521229b87b
9 changed files with 70 additions and 28 deletions

View File

@ -1,5 +1,5 @@
import React, { useState, useReducer } from 'react';
import { UseFormRegister, UseFormSetValue } from 'react-hook-form';
import { FormState, UseFormRegister, UseFormSetValue } from 'react-hook-form';
import { FieldValues } from 'react-hook-form/dist/types/fields';
import { User } from '../../models/user';
import { SocialNetwork } from '../../models/social-network';
@ -12,10 +12,13 @@ interface EditSocialsProps<TFieldValues> {
register: UseFormRegister<TFieldValues>,
setValue: UseFormSetValue<User>,
networks: SocialNetwork[],
formState: FormState<TFieldValues>
}
export const EditSocials = <TFieldValues extends FieldValues>({ register, setValue, networks }: EditSocialsProps<TFieldValues>) => {
export const EditSocials = <TFieldValues extends FieldValues>({ register, setValue, networks, formState }: EditSocialsProps<TFieldValues>) => {
const { t } = useTranslation('shared');
// regular expression to validate the the input fields
const urlRegex = /^(https?:\/\/)([\da-z.-]+)\.([-a-z\d.]{2,30})([/\w .-]*)*\/?$/;
const initSelectedNetworks = networks.filter(el => el.url !== '');
const [selectedNetworks, setSelectedNetworks] = useState(initSelectedNetworks);
@ -48,12 +51,19 @@ export const EditSocials = <TFieldValues extends FieldValues>({ register, setVal
!selectedNetworks.includes(network) && <img key={index} src={`${Icons}#${network.name}`} onClick={() => selectNetwork(network)}></img>
)}
</div>
<div>
{selectNetwork.length && <div className='social-inputs'>
{userNetworks.map((network, index) =>
selectedNetworks.includes(network) &&
<FormInput key={index}
id={`profile.${network.name}`}
register={register}
rules= {{
pattern: {
value: urlRegex,
message: t('app.shared.user_profile_form.website_invalid')
}
}}
formState={formState}
defaultValue={network.url}
label={network.name}
placeholder={t('app.shared.text_editor.url_placeholder')}
@ -61,7 +71,7 @@ export const EditSocials = <TFieldValues extends FieldValues>({ register, setVal
addOn={<Trash size={16} />}
addOnAction={() => dispatch({ type: 'delete', payload: { network, field: `profile.${network.name}` } })} />
)}
</div>
</div>}
</>
);
};

View File

@ -15,13 +15,17 @@ import { FabButton } from '../base/fab-button';
declare const Application: IApplication;
interface FabSocialsProps {
show: boolean
show: boolean,
onError: (message: string) => void,
onSuccess: (message: string) => void
}
export const FabSocials: React.FC<FabSocialsProps> = ({ show = false }) => {
export const FabSocials: React.FC<FabSocialsProps> = ({ show = false, onError, onSuccess }) => {
const { t } = useTranslation('shared');
// regular expression to validate the the input fields
const urlRegex = /^(https?:\/\/)([\da-z.-]+)\.([-a-z\d.]{2,30})([/\w .-]*)*\/?$/;
const { handleSubmit, register, setValue } = useForm();
const { handleSubmit, register, setValue, formState } = useForm();
const settingsList = supportedNetworks.map(el => el as SettingName);
@ -44,7 +48,9 @@ export const FabSocials: React.FC<FabSocialsProps> = ({ show = false }) => {
SettingAPI.bulkUpdate(updatedNetworks).then(res => {
const errorResults = Array.from(res.values()).filter(item => !item.status);
if (errorResults.length > 0) {
console.error(errorResults.map(item => item.error[0]).join(' '));
onError(t('app.shared.fab_socials.networks_update_error'));
} else {
onSuccess(t('app.shared.fab_socials.networks_update_success'));
}
});
};
@ -70,24 +76,33 @@ export const FabSocials: React.FC<FabSocialsProps> = ({ show = false }) => {
</div>
: <form onSubmit={handleSubmit(onSubmit)}>
<div className="social-icons">
<div className='social-icons'>
{fabNetworks.map((network, index) =>
!selectedNetworks.includes(network) &&
<img key={index} src={`${Icons}#${network.name}`} onClick={() => selectNetwork(network)}></img>
)}
</div>
{selectNetwork.length && <div className='social-inputs'>
{fabNetworks.map((network, index) =>
selectedNetworks.includes(network) &&
<FormInput id={network.name}
key={index}
register={register}
rules={{
pattern: {
value: urlRegex,
message: t('app.shared.user_profile_form.website_invalid')
}
}}
formState={formState}
defaultValue={network.url}
label={network.name}
placeholder={t('app.shared.text_editor.url_placeholder')}
placeholder={t('app.shared.fab_socials.url_placeholder')}
icon={<img src={`${Icons}#${network.name}`}></img>}
addOn={<Trash size={16} />}
addOnAction={() => remove(network)} />
)}
</div>}
<FabButton type='submit'
className='btn-warning'>
{t('app.shared.buttons.save')}
@ -104,4 +119,4 @@ const FabSocialsWrapper: React.FC<FabSocialsProps> = (props) => {
</Loader>
);
};
Application.Components.component('fabSocials', react2angular(FabSocialsWrapper, ['show']));
Application.Components.component('fabSocials', react2angular(FabSocialsWrapper, ['show', 'onError', 'onSuccess']));

View File

@ -137,7 +137,8 @@ export const UserProfileForm: React.FC<UserProfileFormProps> = ({ action, size,
<h4>{t('app.shared.user_profile_form.account_networks')}</h4>
<EditSocials register={register}
networks={userNetworks}
setValue={setValue} />
setValue={setValue}
formState={formState} />
</div>
<div className="organization-data">
<h4>{t('app.shared.user_profile_form.organization_data')}</h4>

View File

@ -466,6 +466,14 @@ Application.Controllers.controller('SettingsController', ['$scope', '$rootScope'
}
};
$scope.onSuccess = function (message) {
growl.success(message);
};
$scope.onError = function (message) {
growl.error(message);
};
/* PRIVATE SCOPE */
/**

View File

@ -27,7 +27,7 @@ export default class UserLib {
getUserSocialNetworks = (customer: User): {name: string, url: string}[] => {
const userNetworks = [];
for (const [name, url] of Object.entries(customer.profile)) {
for (const [name, url] of Object.entries(customer.profile_attributes)) {
supportedNetworks.includes(name) && userNetworks.push({ name, url });
}
return userNetworks;

View File

@ -595,6 +595,11 @@ body.container {
}
}
}
.social-inputs {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
column-gap: 3rem;
}
// public profile view
.profile-top {

View File

@ -57,10 +57,9 @@
<button name="button" class="btn btn-warning" ng-click="save(aboutContactsSetting)" translate>{{ 'app.shared.buttons.save' }}</button>
</div>
<div class="col-md-4 col-md-offset-2 m-t-xl">
<h2 translate>{{ 'app.public.home.follow_us' }}</h2>
<fab-socials></fab-socials>
<h2 translate>{{ 'app.admin.settings.about_follow_us' }}</h2>
<fab-socials on-success="onSuccess" on-error="onError"></fab-socials>
</div>
</div>
</div>
</div>

View File

@ -1310,6 +1310,7 @@ en:
about_body: "\"About\" page content"
about_contacts: "\"About\" page contacts"
about_follow_us: "Follow us"
about_networks: "Social networks"
privacy_draft: "privacy policy draft"
privacy_body: "privacy policy"
privacy_dpo: "data protection officer address"

View File

@ -23,7 +23,6 @@ en:
you_will_lose_any_unsaved_modification_if_you_reload_this_page: "You will lose any unsaved modification if you reload this page"
payment_card_error: "A problem has occurred with your credit card:"
payment_card_declined: "Your card was declined."
#text editor
text_editor:
text_placeholder: "Type something…"
link_placeholder: "Paste link…"
@ -32,6 +31,10 @@ en:
add_link: "Insert a link"
add_video: "Embed a video"
add_image: "Insert an image"
fab_socials:
networks_update_success: "Social networks update successful"
networks_update_error: "Problem trying to update social networks"
url_placeholder: "Paste url…"
#user edition form
user_profile_form:
add_an_avatar: "Add an avatar"