diff --git a/src/hooks/usePersonalDetailsByLogin.ts b/src/hooks/usePersonalDetailsByLogin.ts new file mode 100644 index 0000000000000..4a0a19b4b1e72 --- /dev/null +++ b/src/hooks/usePersonalDetailsByLogin.ts @@ -0,0 +1,27 @@ +import {useMemo} from 'react'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {PersonalDetails} from '@src/types/onyx'; +import useOnyx from './useOnyx'; + +/** + * Hook that returns personal details indexed by login email. + * Enables case-insensitive lookups. + */ +function usePersonalDetailsByLogin(): Record { + const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: true}); + + return useMemo(() => { + if (!personalDetails) { + return {}; + } + + return Object.values(personalDetails).reduce((acc: Record, detail) => { + if (detail?.login) { + acc[detail.login.toLowerCase()] = detail; + } + return acc; + }, {}); + }, [personalDetails]); +} + +export default usePersonalDetailsByLogin; diff --git a/src/languages/de.ts b/src/languages/de.ts index ab753f01a8cad..bb001b9c77186 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -3139,6 +3139,7 @@ ${ whenClearStatus: 'Wann sollen wir deinen Status zurücksetzen?', vacationDelegate: 'Urlaubsvertretung', setVacationDelegate: `Lege eine Vertretung für den Urlaub fest, die Berichte in deiner Abwesenheit in deinem Namen genehmigt.`, + cannotSetVacationDelegate: `Du kannst keinen Urlaubsvertreter festlegen, da du derzeit der Vertreter für die folgenden Mitglieder bist:`, vacationDelegateError: 'Beim Aktualisieren Ihrer Vertretung im Urlaub ist ein Fehler aufgetreten.', asVacationDelegate: ({nameOrEmail}: VacationDelegateParams) => `als Urlaubsvertretung von ${nameOrEmail}`, toAsVacationDelegate: ({submittedToName, vacationDelegateName}: SubmittedToVacationDelegateParams) => `an ${submittedToName} als Urlaubsvertretung für ${vacationDelegateName}`, diff --git a/src/languages/en.ts b/src/languages/en.ts index fee76443fc339..a1092666dc561 100644 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -3165,6 +3165,7 @@ const translations = { whenClearStatus: 'When should we clear your status?', vacationDelegate: 'Vacation delegate', setVacationDelegate: `Set a vacation delegate to approve reports on your behalf while you're out of office.`, + cannotSetVacationDelegate: `You can't set a vacation delegate because you're currently the delegate for the following members:`, vacationDelegateError: 'There was an error updating your vacation delegate.', asVacationDelegate: ({nameOrEmail}: VacationDelegateParams) => `as ${nameOrEmail}'s vacation delegate`, toAsVacationDelegate: ({submittedToName, vacationDelegateName}: SubmittedToVacationDelegateParams) => `to ${submittedToName} as vacation delegate for ${vacationDelegateName}`, diff --git a/src/languages/es.ts b/src/languages/es.ts index 2a5d46ce44c29..9c8ebcefa3c14 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -2904,6 +2904,7 @@ ${amount} para ${merchant} - ${date}`, whenClearStatus: '¿Cuándo deberíamos borrar tu estado?', vacationDelegate: 'Delegado de vacaciones', setVacationDelegate: 'Configura un delegado de vacaciones para aprobar informes en tu nombre mientras estás fuera de la oficina.', + cannotSetVacationDelegate: `No puedes establecer un delegado de vacaciones porque actualmente eres el delegado de los siguientes miembros:`, vacationDelegateError: 'Hubo un error al actualizar tu delegado de vacaciones.', asVacationDelegate: ({nameOrEmail: managerName}) => `como delegado de vacaciones de ${managerName}`, toAsVacationDelegate: ({submittedToName, vacationDelegateName}) => `a ${submittedToName} como delegado de vacaciones de ${vacationDelegateName}`, diff --git a/src/languages/fr.ts b/src/languages/fr.ts index 7c352570058fe..2d5244c5857fd 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -3145,6 +3145,7 @@ ${ whenClearStatus: 'Quand devons-nous effacer votre statut ?', vacationDelegate: 'Délégué de vacances', setVacationDelegate: `Définissez un délégué de vacances pour approuver les notes de frais en votre nom pendant votre absence du bureau.`, + cannotSetVacationDelegate: `Vous ne pouvez pas définir un délégué de vacances car vous êtes actuellement le délégué des membres suivants :`, vacationDelegateError: 'Une erreur s’est produite lors de la mise à jour de votre remplaçant de congés.', asVacationDelegate: ({nameOrEmail}: VacationDelegateParams) => `en tant que délégué de vacances de ${nameOrEmail}`, toAsVacationDelegate: ({submittedToName, vacationDelegateName}: SubmittedToVacationDelegateParams) => diff --git a/src/languages/it.ts b/src/languages/it.ts index 2d2f860bbc69d..38dd3fcba082d 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -3130,6 +3130,7 @@ ${ whenClearStatus: 'Quando dovremmo cancellare il tuo stato?', vacationDelegate: 'Delegato ferie', setVacationDelegate: `Imposta un delegato per le ferie per approvare i report per tuo conto mentre sei fuori ufficio.`, + cannotSetVacationDelegate: `Non puoi impostare un delegato per le ferie perché al momento sei il delegato per i seguenti membri:`, vacationDelegateError: 'Si è verificato un errore durante l’aggiornamento del tuo delegato per le ferie.', asVacationDelegate: ({nameOrEmail}: VacationDelegateParams) => `come delegato per le ferie di ${nameOrEmail}`, toAsVacationDelegate: ({submittedToName, vacationDelegateName}: SubmittedToVacationDelegateParams) => `a ${submittedToName} come delegato ferie per ${vacationDelegateName}`, diff --git a/src/languages/ja.ts b/src/languages/ja.ts index 2ab896f84c1e0..1f8e2cc0e5c6b 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -3116,6 +3116,7 @@ ${ whenClearStatus: 'ステータスをいつクリアしますか?', vacationDelegate: '休暇代理人', setVacationDelegate: `休暇中に不在の間、あなたに代わってレポートを承認する代理人を設定しましょう。`, + cannotSetVacationDelegate: `現在、次のメンバーの代理人になっているため、休暇代理人を設定できません:`, vacationDelegateError: '休暇の代理人を更新中にエラーが発生しました。', asVacationDelegate: ({nameOrEmail}: VacationDelegateParams) => `${nameOrEmail} さんの休暇代理として`, toAsVacationDelegate: ({submittedToName, vacationDelegateName}: SubmittedToVacationDelegateParams) => `${vacationDelegateName} の休暇代理人として ${submittedToName} に`, diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 8c86b34e92948..bffe3b7a53a2b 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -3130,6 +3130,7 @@ ${ whenClearStatus: 'Wanneer moeten we je status wissen?', vacationDelegate: 'Vertegenwoordiger tijdens vakantie', setVacationDelegate: `Stel een vervangende fiatteur in om rapporten namens jou goed te keuren terwijl je afwezig bent.`, + cannotSetVacationDelegate: `Je kunt geen vakantiedelegaat instellen omdat je momenteel de delegaat bent voor de volgende leden:`, vacationDelegateError: 'Er is een fout opgetreden bij het bijwerken van je vervanger tijdens vakantie.', asVacationDelegate: ({nameOrEmail}: VacationDelegateParams) => `als vakantiewaarnemer van ${nameOrEmail}`, toAsVacationDelegate: ({submittedToName, vacationDelegateName}: SubmittedToVacationDelegateParams) => diff --git a/src/languages/pl.ts b/src/languages/pl.ts index 8ad32dbc3ca80..5eb3bb6479a2b 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -3121,6 +3121,7 @@ ${ whenClearStatus: 'Kiedy powinniśmy wyczyścić Twój status?', vacationDelegate: 'Zastępca urlopowy', setVacationDelegate: `Ustaw zastępcę na czas urlopu, aby zatwierdzał raporty w Twoim imieniu, gdy jesteś poza biurem.`, + cannotSetVacationDelegate: `Nie możesz ustawić delegata urlopowego, ponieważ obecnie jesteś delegatem dla następujących członków:`, vacationDelegateError: 'Wystąpił błąd podczas aktualizowania Twojego zastępcy urlopowego.', asVacationDelegate: ({nameOrEmail}: VacationDelegateParams) => `jako osoba zastępująca ${nameOrEmail} podczas urlopu`, toAsVacationDelegate: ({submittedToName, vacationDelegateName}: SubmittedToVacationDelegateParams) => `do ${submittedToName} jako zastępca urlopowy dla ${vacationDelegateName}`, diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index c8f8dd04d4f7d..c9298a079b8a3 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -3123,6 +3123,7 @@ ${ whenClearStatus: 'Quando devemos limpar seu status?', vacationDelegate: 'Delegado de férias', setVacationDelegate: `Defina um delegado de férias para aprovar relatórios em seu nome enquanto você estiver fora do escritório.`, + cannotSetVacationDelegate: `Você não pode definir um delegado de férias porque atualmente é o delegado dos seguintes membros:`, vacationDelegateError: 'Ocorreu um erro ao atualizar seu delegado de férias.', asVacationDelegate: ({nameOrEmail}: VacationDelegateParams) => `como delegado de férias de ${nameOrEmail}`, toAsVacationDelegate: ({submittedToName, vacationDelegateName}: SubmittedToVacationDelegateParams) => `para ${submittedToName} como delegado(a) de férias de ${vacationDelegateName}`, diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index 8d3db872cd1f4..9ccf85164226c 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -3078,6 +3078,7 @@ ${ whenClearStatus: '我们应在何时清除你的状态?', vacationDelegate: '休假代理', setVacationDelegate: `设置一个休假代理人在你不在办公室时代你审批报销报告。`, + cannotSetVacationDelegate: `由于你目前是以下成员的代理人,因此无法设置休假代理人:`, vacationDelegateError: '更新你的休假代理时出错。', asVacationDelegate: ({nameOrEmail}: VacationDelegateParams) => `作为 ${nameOrEmail} 的休假代理`, toAsVacationDelegate: ({submittedToName, vacationDelegateName}: SubmittedToVacationDelegateParams) => `作为 ${vacationDelegateName} 的休假代理人提交给 ${submittedToName}`, diff --git a/src/pages/settings/Profile/CustomStatus/StatusPage.tsx b/src/pages/settings/Profile/CustomStatus/StatusPage.tsx index c2092bfad1ff6..e95d8a1d781d8 100644 --- a/src/pages/settings/Profile/CustomStatus/StatusPage.tsx +++ b/src/pages/settings/Profile/CustomStatus/StatusPage.tsx @@ -1,7 +1,9 @@ import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {InteractionManager, View} from 'react-native'; import type {ValueOf} from 'type-fest'; +import Button from '@components/Button'; import EmojiPickerButtonDropdown from '@components/EmojiPicker/EmojiPickerButtonDropdown'; +import FixedFooter from '@components/FixedFooter'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; import type {FormInputErrors, FormOnyxValues, FormRef} from '@components/Form/types'; @@ -18,6 +20,7 @@ import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails' import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; +import usePersonalDetailsByLogin from '@hooks/usePersonalDetailsByLogin'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; @@ -28,7 +31,6 @@ import focusAfterModalClose from '@libs/focusAfterModalClose'; import focusComposerWithDelay from '@libs/focusComposerWithDelay'; import {formatPhoneNumber} from '@libs/LocalePhoneNumber'; import Navigation from '@libs/Navigation/Navigation'; -import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import {clearCustomStatus, clearDraftCustomStatus, updateCustomStatus, updateDraftCustomStatus} from '@userActions/User'; import {clearVacationDelegateError} from '@userActions/VacationDelegate'; import CONST from '@src/CONST'; @@ -54,14 +56,18 @@ function StatusPage() { const {isSmallScreenWidth} = useResponsiveLayout(); const [draftStatus] = useOnyx(ONYXKEYS.CUSTOM_STATUS_DRAFT, {canBeMissing: true}); + const [formState] = useOnyx(ONYXKEYS.FORMS.SETTINGS_STATUS_SET_FORM, {canBeMissing: true}); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); + const personalDetailsByLogin = usePersonalDetailsByLogin(); const formRef = useRef(null); const [brickRoadIndicator, setBrickRoadIndicator] = useState>(); const [vacationDelegate] = useOnyx(ONYXKEYS.NVP_PRIVATE_VACATION_DELEGATE, {canBeMissing: true}); const hasVacationDelegate = !!vacationDelegate?.delegate; - const vacationDelegatePersonalDetails = getPersonalDetailByEmail(vacationDelegate?.delegate ?? ''); + const hasActiveDelegations = !!vacationDelegate?.delegatorFor?.length; + const vacationDelegatePersonalDetails = personalDetailsByLogin[vacationDelegate?.delegate?.toLowerCase() ?? '']; const formattedDelegateLogin = formatPhoneNumber(vacationDelegatePersonalDetails?.login ?? ''); + const isFormLoading = !!formState?.isLoading; const currentUserEmojiCode = currentUserPersonalDetails?.status?.emojiCode ?? ''; const currentUserStatusText = currentUserPersonalDetails?.status?.text ?? ''; @@ -189,6 +195,28 @@ function StatusPage() { const {inputCallbackRef, inputRef} = useAutoFocusInput(); const fallbackVacationDelegateLogin = formattedDelegateLogin === '' ? vacationDelegate?.delegate : formattedDelegateLogin; + const renderDelegatorList = () => { + return vacationDelegate?.delegatorFor?.map((delegatorEmail) => { + const delegatorDetails = personalDetailsByLogin[delegatorEmail.toLowerCase()]; + const formattedLogin = formatPhoneNumber(delegatorDetails?.login ?? ''); + const displayLogin = formattedLogin || delegatorEmail; + + return ( + + ); + }); + }; + return ( {translate('statusPage.statusExplanation')} @@ -263,39 +290,65 @@ function StatusPage() { )} - {translate('statusPage.setVacationDelegate')} - {hasVacationDelegate && {translate('statusPage.vacationDelegate')}} - {hasVacationDelegate ? ( - clearVacationDelegateError(vacationDelegate?.previousDelegate)} - > - Navigation.navigate(ROUTES.SETTINGS_VACATION_DELEGATE)} - containerStyle={styles.pr2} - /> - + {translate('statusPage.vacationDelegate')} + {hasActiveDelegations ? ( + + {translate('statusPage.cannotSetVacationDelegate')} + {renderDelegatorList()} + ) : ( - - Navigation.navigate(ROUTES.SETTINGS_VACATION_DELEGATE)} - containerStyle={styles.pr2} - /> + + {translate('statusPage.setVacationDelegate')} + + {hasVacationDelegate ? ( + <> + {translate('statusPage.vacationDelegate')} + clearVacationDelegateError(vacationDelegate?.previousDelegate)} + > + Navigation.navigate(ROUTES.SETTINGS_VACATION_DELEGATE)} + containerStyle={styles.pr2} + /> + + + ) : ( + + Navigation.navigate(ROUTES.SETTINGS_VACATION_DELEGATE)} + containerStyle={styles.pr2} + /> + + )} )} + + +