1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-08 23:46:14 +01:00
fab-manager/app/frontend/src/javascript/components/socials/edit-socials.tsx

92 lines
3.6 KiB
TypeScript

import React, { useState, useReducer } from 'react';
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';
import Icons from '../../../../images/social-icons.svg';
import { FormInput } from '../form/form-input';
import { Trash } from 'phosphor-react';
import { useTranslation } from 'react-i18next';
interface EditSocialsProps<TFieldValues> {
register: UseFormRegister<TFieldValues>,
setValue: UseFormSetValue<User>,
networks: SocialNetwork[],
formState: FormState<TFieldValues>,
disabled: boolean|((id: string) => boolean),
}
/**
* Allow a user to edit its personnal social networks
*/
export const EditSocials = <TFieldValues extends FieldValues>({ register, setValue, networks, formState, disabled }: EditSocialsProps<TFieldValues>) => {
const { t } = useTranslation('shared');
// regular expression to validate the the input fields
const urlRegex = /^(https?:\/\/)([^.]+)\.(.{2,30})(\/.*)*\/?$/;
const initSelectedNetworks = networks.filter(el => !['', null, undefined].includes(el.url));
const [selectedNetworks, setSelectedNetworks] = useState(initSelectedNetworks);
/**
* Callback triggered when the user adds a network, from the list of available networks, to the editable networks.
*/
const selectNetwork = (network) => {
setSelectedNetworks([...selectedNetworks, network]);
};
/**
* Return a derivated state of the selected networks list, depending on the given action.
*/
const reducer = (state, action) => {
switch (action.type) {
case 'delete':
setSelectedNetworks(selectedNetworks.filter(el => el !== action.payload.network));
setValue(action.payload.field, '');
return state.map(el => el === action.payload.network
? { ...el, url: '' }
: el);
case 'update':
return state.map(el => el === action.payload
? { ...el, url: action.payload.url }
: el);
default:
return state;
}
};
const [userNetworks, dispatch] = useReducer(reducer, networks);
return (
<>
<div className='social-icons'>
{userNetworks.map((network, index) =>
!selectedNetworks.includes(network) && <svg key={index} onClick={() => selectNetwork(network)} viewBox="0 0 24 24" >
<use href={`${Icons}#${network.name}`} />
</svg>
)}
</div>
{selectNetwork.length && <div className='social-inputs'>
{userNetworks.map((network, index) =>
selectedNetworks.includes(network) &&
<FormInput key={index}
id={`profile_attributes.${network.name}`}
register={register}
rules= {{
pattern: {
value: urlRegex,
message: t('app.shared.edit_socials.website_invalid')
}
}}
formState={formState}
defaultValue={network.url}
label={network.name}
disabled={disabled}
placeholder={t('app.shared.edit_socials.url_placeholder')}
icon={<svg viewBox="0 0 24 24"><use href={`${Icons}#${network.name}`}/></svg>}
addOn={<Trash size={16} />}
addOnAction={() => dispatch({ type: 'delete', payload: { network, field: `profile_attributes.${network.name}` } })} />
)}
</div>}
</>
);
};