Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
112 changes: 112 additions & 0 deletions src/components/GustoSyncResultsModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import React, {useState} from 'react';
import {View} from 'react-native';
import {useMemoizedLazyExpensifyIcons, useMemoizedLazyIllustrations} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import type {GustoSyncResult} from '@libs/API/GustoSyncResult';
import CONST from '@src/CONST';
import Button from './Button';
import FixedFooter from './FixedFooter';
import HeaderWithBackButton from './HeaderWithBackButton';
import Icon from './Icon';
import Modal from './Modal';
import type {ModalProps} from './Modal/Global/ModalContext';
import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback';
import ScrollView from './ScrollView';
import Text from './Text';

type GustoSyncResultsModalProps = ModalProps & {
/** Sync result returned by the completed Gusto sync job */
result: GustoSyncResult;
};

function GustoSyncResultsModal({result, closeModal}: GustoSyncResultsModalProps) {
const {translate} = useLocalize();
const theme = useTheme();
const styles = useThemeStyles();
const icons = useMemoizedLazyExpensifyIcons(['DownArrow']);
const illustrations = useMemoizedLazyIllustrations(['SyncUsers']);
const [isSkippedSectionExpanded, setIsSkippedSectionExpanded] = useState(false);

const addedCount = result.addedEmployeesCount ?? 0;
const removedCount = result.removedEmployeesCount ?? 0;
const skippedCount = result.skippedEmployees?.length ?? 0;
const closeResultsModal = () => closeModal();

const renderResultSummary = (label: string, count: number) => (
<View style={[styles.mb6]}>
<Text style={[styles.textSupporting, styles.mb1]}>{label}</Text>
<Text style={[styles.textNormalThemeText, styles.textStrong]}>{translate('workspace.hr.gusto.syncResults.employeeCount', {count})}</Text>
</View>
);

return (
<Modal
type={CONST.MODAL.MODAL_TYPE.RIGHT_DOCKED}
isVisible
onClose={closeResultsModal}
shouldHandleNavigationBack
enableEdgeToEdgeBottomSafeAreaPadding
>
<View
testID="GustoSyncResultsModal"
style={[styles.flex1, styles.appBG]}
>
<HeaderWithBackButton
title={translate('workspace.hr.gusto.syncResults.title')}
onBackButtonPress={closeResultsModal}
/>
<ScrollView contentContainerStyle={[styles.flexGrow1, styles.ph5, styles.pb8]}>
<View style={[styles.alignItemsCenter, styles.mt4, styles.mb4, styles.pRelative]}>
<Icon
src={illustrations.SyncUsers}
width={68}
height={68}
/>
</View>
<Text style={[styles.textHeadlineH1, styles.mb8]}>{translate('workspace.hr.gusto.syncResults.successTitle')}</Text>
{renderResultSummary(translate('workspace.hr.gusto.syncResults.added'), addedCount)}
{renderResultSummary(translate('workspace.hr.gusto.syncResults.removed'), removedCount)}
<PressableWithoutFeedback
accessibilityLabel={translate('workspace.hr.gusto.syncResults.skipped')}
sentryLabel="GustoSyncResultsModal-SkippedEmployees"
role={CONST.ROLE.BUTTON}
onPress={() => setIsSkippedSectionExpanded((isExpanded) => !isExpanded)}
style={[styles.flexRow, styles.justifyContentBetween, styles.alignItemsCenter]}
>
<View>
<Text style={[styles.textSupporting, styles.mb1]}>{translate('workspace.hr.gusto.syncResults.skipped')}</Text>
<Text style={[styles.textNormalThemeText, styles.textStrong]}>{translate('workspace.hr.gusto.syncResults.employeeCount', {count: skippedCount})}</Text>
</View>
<Icon
src={icons.DownArrow}
fill={theme.icon}
additionalStyles={isSkippedSectionExpanded ? {transform: [{rotate: '180deg'}]} : undefined}
/>
</PressableWithoutFeedback>
{isSkippedSectionExpanded &&
result.skippedEmployees?.map((employee) => (
<View
key={employee.id}
style={[styles.mt4]}
>
<Text style={[styles.textNormalThemeText, styles.textStrong]}>{employee.name}</Text>
<Text style={[styles.textSupporting]}>{employee.reason}</Text>
</View>
))}
</ScrollView>
<FixedFooter addBottomSafeAreaPadding>
<Button
large
success
text={translate('common.buttonConfirm')}
onPress={closeResultsModal}
/>
</FixedFooter>
</View>
</Modal>
);
}

export default GustoSyncResultsModal;
2 changes: 2 additions & 0 deletions src/components/Icon/chunks/illustrations.chunk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ import Stopwatch from '@assets/images/simple-illustrations/simple-illustration__
import SubscriptionAnnual from '@assets/images/simple-illustrations/simple-illustration__subscription-annual.svg';
import SubscriptionPPU from '@assets/images/simple-illustrations/simple-illustration__subscription-ppu.svg';
import Sunglasses from '@assets/images/simple-illustrations/simple-illustration__sunglasses.svg';
import SyncUsers from '@assets/images/simple-illustrations/simple-illustration__syncusers.svg';
import Tag from '@assets/images/simple-illustrations/simple-illustration__tag.svg';
import Target from '@assets/images/simple-illustrations/simple-illustration__target.svg';
import TeachersUnite from '@assets/images/simple-illustrations/simple-illustration__teachers-unite.svg';
Expand Down Expand Up @@ -391,6 +392,7 @@ const Illustrations = {
LockOpen,
Mailbox,
NewUser,
SyncUsers,
ShieldYellow,
Clock,
Members,
Expand Down
45 changes: 45 additions & 0 deletions src/hooks/useGustoSyncResultsModal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {useEffect} from 'react';
import type {OnyxEntry} from 'react-native-onyx';
import GustoSyncResultsModal from '@components/GustoSyncResultsModal';
import {useModal} from '@components/Modal/Global/ModalContext';
import CONST from '@src/CONST';
import type {PolicyConnectionSyncProgress} from '@src/types/onyx/Policy';
import usePrevious from './usePrevious';

function useGustoSyncResultsModal(policyID: string, connectionSyncProgress: OnyxEntry<PolicyConnectionSyncProgress>, isFocused: boolean) {
const modal = useModal();
const previousSyncProgress = usePrevious(connectionSyncProgress);

useEffect(() => {
const syncResult = connectionSyncProgress?.result;
const isGustoSyncDoneWithResult =
connectionSyncProgress?.connectionName === CONST.POLICY.CONNECTIONS.NAME.GUSTO &&
connectionSyncProgress?.stageInProgress === CONST.POLICY.CONNECTIONS.SYNC_STAGE_NAME.JOB_DONE &&
!!syncResult;
const didTransitionToJobDone =
previousSyncProgress?.connectionName === CONST.POLICY.CONNECTIONS.NAME.GUSTO && previousSyncProgress?.stageInProgress !== CONST.POLICY.CONNECTIONS.SYNC_STAGE_NAME.JOB_DONE;
const didGustoSyncComplete = isFocused && isGustoSyncDoneWithResult && didTransitionToJobDone;

if (!didGustoSyncComplete || !syncResult) {
return;
}

modal.showModal({
component: GustoSyncResultsModal,
props: {result: syncResult},
id: `gusto-sync-results-${policyID}`,
});
}, [
connectionSyncProgress?.connectionName,
connectionSyncProgress?.result,
connectionSyncProgress?.stageInProgress,
connectionSyncProgress?.timestamp,
isFocused,
policyID,
previousSyncProgress?.connectionName,
previousSyncProgress?.stageInProgress,
modal,
]);
}

export default useGustoSyncResultsModal;
11 changes: 11 additions & 0 deletions src/languages/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7106,6 +7106,17 @@ Fügen Sie weitere Ausgabelimits hinzu, um den Cashflow Ihres Unternehmens zu sc
syncError: 'Verbindung zu Gusto nicht möglich',
disconnectTitle: 'Gusto trennen',
disconnectPrompt: 'Bist du sicher, dass du Gusto trennen möchtest?',
syncResults: {
title: 'Gusto-Synchronisierungsergebnisse',
successTitle: 'Ihre Gusto-Verbindung wurde erfolgreich synchronisiert!',
added: 'Hinzugefügt',
removed: 'Entfernt',
skipped: 'Übersprungen',
employeeCount: () => ({
one: '1 Mitarbeiter',
other: (count: number) => `${count} Mitarbeitende`,
}),
},
},
zenefits: {
title: 'TriNet',
Expand Down
11 changes: 11 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6465,6 +6465,17 @@ const translations = {
description: 'I’ll manually setup approval workflows in Expensify.',
},
},
syncResults: {
title: 'Gusto sync results',
successTitle: 'Successfully synced your Gusto connection!',
added: 'Added',
removed: 'Removed',
skipped: 'Skipped',
employeeCount: () => ({
one: '1 employee',
other: (count: number) => `${count} employees`,
}),
},
},
zenefits: {
title: 'TriNet',
Expand Down
11 changes: 11 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6287,6 +6287,17 @@ ${amount} para ${merchant} - ${date}`,
description: 'Configuraré manualmente los flujos de aprobación en Expensify.',
},
},
syncResults: {
title: 'Resultados de la sincronización de Gusto',
successTitle: '¡Se sincronizó correctamente tu conexión con Gusto!',
added: 'Añadido',
removed: 'Eliminado',
skipped: 'Omitido',
employeeCount: () => ({
one: '1 empleado',
other: (count: number) => `${count} empleados`,
}),
},
},
zenefits: {
title: 'TriNet',
Expand Down
11 changes: 11 additions & 0 deletions src/languages/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7128,6 +7128,17 @@ Ajoutez davantage de règles de dépenses pour protéger la trésorerie de l’e
syncError: 'Impossible de se connecter à Gusto',
disconnectTitle: 'Déconnecter Gusto',
disconnectPrompt: 'Voulez-vous vraiment déconnecter Gusto ?',
syncResults: {
title: 'Résultats de la synchronisation Gusto',
successTitle: 'Connexion Gusto synchronisée avec succès !',
added: 'Ajouté',
removed: 'Supprimé',
skipped: 'Ignoré',
employeeCount: () => ({
one: '1 employé',
other: (count: number) => `${count} employés`,
}),
},
},
zenefits: {
title: 'TriNet',
Expand Down
11 changes: 11 additions & 0 deletions src/languages/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7092,6 +7092,17 @@ Aggiungi altre regole di spesa per proteggere il flusso di cassa aziendale.`,
syncError: 'Impossibile connettersi a Gusto',
disconnectTitle: 'Disconnetti Gusto',
disconnectPrompt: 'Sei sicuro di voler disconnettere Gusto?',
syncResults: {
title: 'Risultati sincronizzazione Gusto',
successTitle: 'Connessione a Gusto sincronizzata con successo!',
added: 'Aggiunto',
removed: 'Rimosso',
skipped: 'Saltato',
employeeCount: () => ({
one: '1 dipendente',
other: (count: number) => `${count} dipendenti`,
}),
},
},
zenefits: {
title: 'TriNet',
Expand Down
11 changes: 11 additions & 0 deletions src/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7013,6 +7013,17 @@ ${reportName}
syncError: 'Gusto に接続できません',
disconnectTitle: 'Gusto の接続を解除',
disconnectPrompt: 'Gusto との接続を本当に解除しますか?',
syncResults: {
title: 'Gusto 同期結果',
successTitle: 'Gusto との連携が正常に同期されました!',
added: '追加済み',
removed: '削除済み',
skipped: 'スキップ済み',
employeeCount: () => ({
one: '1 従業員',
other: (count: number) => `${count} 従業員`,
}),
},
},
zenefits: {
title: 'TriNet',
Expand Down
11 changes: 11 additions & 0 deletions src/languages/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7068,6 +7068,17 @@ er bestedingsregels toe om de kasstroom van het bedrijf te beschermen.`,
syncError: 'Kan geen verbinding maken met Gusto',
disconnectTitle: 'Verbinding met Gusto verbreken',
disconnectPrompt: 'Weet je zeker dat je de verbinding met Gusto wilt verbreken?',
syncResults: {
title: 'Gusto-synchronisatieresultaten',
successTitle: 'Je Gusto-verbinding is succesvol gesynchroniseerd!',
added: 'Toegevoegd',
removed: 'Verwijderd',
skipped: 'Overgeslagen',
employeeCount: () => ({
one: '1 werknemer',
other: (count: number) => `${count} medewerkers`,
}),
},
},
zenefits: {
title: 'TriNet',
Expand Down
11 changes: 11 additions & 0 deletions src/languages/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7063,6 +7063,17 @@ Dodaj więcej zasad wydatków, żeby chronić płynność finansową firmy.`,
syncError: 'Nie można połączyć się z Gusto',
disconnectTitle: 'Odłącz Gusto',
disconnectPrompt: 'Czy na pewno chcesz odłączyć Gusto?',
syncResults: {
title: 'Wyniki synchronizacji Gusto',
successTitle: 'Pomyślnie zsynchronizowano Twoje połączenie z Gusto!',
added: 'Dodano',
removed: 'Usunięto',
skipped: 'Pominięto',
employeeCount: () => ({
one: '1 pracownik',
other: (count: number) => `${count} pracownicy`,
}),
},
},
zenefits: {
title: 'TriNet',
Expand Down
11 changes: 11 additions & 0 deletions src/languages/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7069,6 +7069,17 @@ Adicione mais regras de gasto para proteger o fluxo de caixa da empresa.`,
syncError: 'Não é possível conectar ao Gusto',
disconnectTitle: 'Desconectar Gusto',
disconnectPrompt: 'Tem certeza de que deseja desconectar o Gusto?',
syncResults: {
title: 'Resultados da sincronização com Gusto',
successTitle: 'Sua conexão com o Gusto foi sincronizada com sucesso!',
added: 'Adicionado',
removed: 'Removido',
skipped: 'Ignorado',
employeeCount: () => ({
one: '1 funcionário',
other: (count: number) => `${count} funcionários`,
}),
},
},
zenefits: {
title: 'TriNet',
Expand Down
11 changes: 11 additions & 0 deletions src/languages/zh-hans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6890,6 +6890,17 @@ ${reportName}
syncError: '无法连接到 Gusto',
disconnectTitle: '断开 Gusto',
disconnectPrompt: '确定要断开与 Gusto 的连接吗?',
syncResults: {
title: 'Gusto 同步结果',
successTitle: '已成功同步你的 Gusto 连接!',
added: '已添加',
removed: '已移除',
skipped: '已跳过',
employeeCount: () => ({
one: '1 员工',
other: (count: number) => `${count} 员工`,
}),
},
},
zenefits: {
title: 'TriNet',
Expand Down
17 changes: 7 additions & 10 deletions src/libs/API/GustoSyncResult.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
type GustoSyncResultUser = {
email: string;
displayName?: string;
};

type GustoSyncSkippedResultUser = GustoSyncResultUser & {
type GustoSyncSkippedEmployee = {
name: string;
id: string;
reason: string;
};

type GustoSyncResult = {
added?: GustoSyncResultUser[];
removed?: GustoSyncResultUser[];
skipped?: GustoSyncSkippedResultUser[];
addedEmployeesCount?: number;
removedEmployeesCount?: number;
skippedEmployees?: GustoSyncSkippedEmployee[];
};

export type {GustoSyncResult, GustoSyncResultUser};
export type {GustoSyncResult, GustoSyncSkippedEmployee};
Loading
Loading