diff --git a/app/frontend/src/javascript/components/user/user-profile-form.tsx b/app/frontend/src/javascript/components/user/user-profile-form.tsx index 6e6357f85..4187f58e5 100644 --- a/app/frontend/src/javascript/components/user/user-profile-form.tsx +++ b/app/frontend/src/javascript/components/user/user-profile-form.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { react2angular } from 'react2angular'; import { SubmitHandler, useForm, useWatch } from 'react-hook-form'; import { isNil as _isNil } from 'lodash'; -import { User } from '../../models/user'; +import { User, UserFieldMapping } from '../../models/user'; import { IApplication } from '../../models/application'; import { Loader } from '../base/loader'; import { FormInput } from '../form/form-input'; @@ -50,6 +50,14 @@ export const UserProfileForm: React.FC = ({ action, size, .catch((error) => { onError(error); }); }; + /** + * Check if the given field path should be disabled because it's mapped to the SSO API. + */ + const isDisabled = function (id: string) { + // TODO: check if AuthenticationProvider is not LocalDatabase + return user.mapped_from_sso?.includes(UserFieldMapping[id]); + }; + const userNetworks = new UserLib(user).getUserSocialNetworks(user); return ( @@ -69,11 +77,13 @@ export const UserProfileForm: React.FC = ({ action, size, @@ -81,6 +91,7 @@ export const UserProfileForm: React.FC = ({ action, size, = ({ action, size, message: t('app.shared.user_profile_form.phone_number_invalid') } }} + disabled={isDisabled} formState={formState} label={t('app.shared.user_profile_form.phone_number')} /> @@ -99,6 +111,7 @@ export const UserProfileForm: React.FC = ({ action, size, type="hidden" /> @@ -107,11 +120,13 @@ export const UserProfileForm: React.FC = ({ action, size, {/* TODO: no password change if sso */} @@ -123,13 +138,6 @@ export const UserProfileForm: React.FC = ({ action, size, currentFormPassword={output.password} formState={formState} />} -
-

{t('app.shared.user_profile_form.account_networks')}

- -

{t('app.shared.user_profile_form.organization_data')}

= ({ action, size, = ({ action, size,
} @@ -169,6 +179,7 @@ export const UserProfileForm: React.FC = ({ action, size, } }} placeholder="https://www.example.com" + disabled={isDisabled} formState={formState} label={t('app.shared.user_profile_form.website')} /> = ({ action, size,
+
+

{t('app.shared.user_profile_form.account_networks')}

+ +

{t('app.shared.user_profile_form.preferences_data')}

diff --git a/app/frontend/src/javascript/models/social-network.ts b/app/frontend/src/javascript/models/social-network.ts index 2b1875e8a..92e13c72d 100644 --- a/app/frontend/src/javascript/models/social-network.ts +++ b/app/frontend/src/javascript/models/social-network.ts @@ -17,4 +17,6 @@ export const supportedNetworks = [ 'pinterest', 'lastfm', 'flickr' -]; +] as const; + +export type SupportedSocialNetwork = typeof supportedNetworks[number]; diff --git a/app/frontend/src/javascript/models/user.ts b/app/frontend/src/javascript/models/user.ts index 7587663f7..99e697eb9 100644 --- a/app/frontend/src/javascript/models/user.ts +++ b/app/frontend/src/javascript/models/user.ts @@ -1,5 +1,6 @@ import { Plan } from './plan'; import { TDateISO, TDateISODate } from '../typings/date-iso'; +import { supportedNetworks, SupportedSocialNetwork } from './social-network'; export enum UserRole { Member = 'member', @@ -7,6 +8,10 @@ export enum UserRole { Admin = 'admin' } +type ProfileAttributesSocial = { + [network in SupportedSocialNetwork]: string +} + export interface User { id: number, username: string, @@ -19,7 +24,7 @@ export interface User { mapped_from_sso?: string[], password?: string, password_confirmation?: string, - profile_attributes: { + profile_attributes: ProfileAttributesSocial & { id: number, first_name: string, last_name: string, @@ -29,20 +34,6 @@ export interface User { website: string, job: string, tours: Array, - facebook: string, - twitter: string, - google_plus: string, - viadeo: string, - linkedin: string, - instagram: string, - youtube: string, - vimeo: string, - dailymotion: string, - github: string, - echosciences: string, - pinterest: string, - lastfm: string, - flickr: string, user_avatar_attributes: { id: number, attachment?: File, @@ -100,3 +91,27 @@ export interface UserIndexFilter { page?: number, size?: number } + +const socialMappings = supportedNetworks.map(network => { + return { [`profile_attributes.${network}`]: `profile.${network}` }; +}); + +export const UserFieldMapping = Object.assign({ + 'profile_attributes.user_avatar_attributes.attachment': 'profile.avatar', + 'statistic_profile_attributes.gender': 'profile.gender', + 'profile_attributes.last_name': 'profile.last_name', + 'profile_attributes.first_name': 'profile.first_name', + 'statistic_profile_attributes.birthday': 'profile.birthday', + 'profile_attributes.phone': 'profile.phone', + username: 'user.username', + email: 'user.email', + 'invoicing_profile_attributes.address_attributes.address': 'profile.address', + 'invoicing_profile_attributes.organization_attributes.name': 'profile.organization_name', + 'invoicing_profile_attributes.organization_attributes.address_attributes.address': 'profile.organization_address', + 'profile_attributes.website': 'profile.website', + 'profile_attributes.job': 'profile.job', + 'profile_attributes.interest': 'profile.interest', + 'profile_attributes.software_mastered': 'profile.software_mastered', + is_allow_contact: 'user.is_allow_contact', + is_allow_newsletter: 'user.is_allow_newsletter' +}, ...socialMappings);