@@ -3,6 +3,7 @@ import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
33// eslint-disable-next-line no-restricted-imports
44import { InteractionManager , View } from 'react-native' ;
55import type { ValueOf } from 'type-fest' ;
6+ import ActivityIndicator from '@components/ActivityIndicator' ;
67import Button from '@components/Button' ;
78import ButtonWithDropdownMenu from '@components/ButtonWithDropdownMenu' ;
89import type { DropdownOption , WorkspaceMemberBulkActionType } from '@components/ButtonWithDropdownMenu/types' ;
@@ -21,6 +22,7 @@ import type {ListItem, SelectionListHandle} from '@components/SelectionList/type
2122import SelectionListWithModal from '@components/SelectionListWithModal' ;
2223import CustomListHeader from '@components/SelectionListWithModal/CustomListHeader' ;
2324import Text from '@components/Text' ;
25+ import TextLink from '@components/TextLink' ;
2426import type { BaseTextInputRef } from '@components/TextInput/BaseTextInput/types' ;
2527import useConfirmModal from '@hooks/useConfirmModal' ;
2628import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails' ;
@@ -39,6 +41,7 @@ import useSearchResults from '@hooks/useSearchResults';
3941import useStyleUtils from '@hooks/useStyleUtils' ;
4042import useThemeStyles from '@hooks/useThemeStyles' ;
4143import useWorkspaceDocumentTitle from '@hooks/useWorkspaceDocumentTitle' ;
44+ import { isConnectionInProgress , syncConnection } from '@libs/actions/connections' ;
4245import { turnOffMobileSelectionMode } from '@libs/actions/MobileSelectionMode' ;
4346import {
4447 clearAddMemberError ,
@@ -118,7 +121,7 @@ type WorkspaceMemberFilterOption = SingleSelectItem<WorkspaceMemberFilterValue>;
118121
119122function WorkspaceMembersPage ( { personalDetails, route, policy} : WorkspaceMembersPageProps ) {
120123 useWorkspaceDocumentTitle ( policy ?. name , 'common.members' ) ;
121- const icons = useMemoizedLazyExpensifyIcons ( [ 'Download' , 'FallbackAvatar' , 'MakeAdmin' , 'Plus' , 'RemoveMembers' , 'Table' , 'User' , 'UserEye' ] ) ;
124+ const icons = useMemoizedLazyExpensifyIcons ( [ 'Download' , 'FallbackAvatar' , 'MakeAdmin' , 'Plus' , 'RemoveMembers' , 'Sync' , ' Table', 'User' , 'UserEye' ] ) ;
122125 const policyMemberEmailsToAccountIDs = useMemo ( ( ) => getMemberAccountIDsForWorkspace ( policy ?. employeeList , true ) , [ policy ?. employeeList ] ) ;
123126 const employeeListDetails = useMemo ( ( ) => policy ?. employeeList ?? ( { } as PolicyEmployeeList ) , [ policy ?. employeeList ] ) ;
124127 const currentUserPersonalDetails = useCurrentUserPersonalDetails ( ) ;
@@ -168,6 +171,7 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers
168171 const selectionListRef = useRef < SelectionListHandle < MemberOption > > ( null ) ;
169172 const isFocused = useIsFocused ( ) ;
170173 const policyID = route . params . policyID ;
174+ const [ connectionSyncProgress ] = useOnyx ( `${ ONYXKEYS . COLLECTION . POLICY_CONNECTION_SYNC_PROGRESS } ${ policyID } ` ) ;
171175 const illustrations = useMemoizedLazyIllustrations ( [ 'ReceiptWrangler' , 'EmptyShelves' ] ) ;
172176
173177 const ownerDetails = personalDetails ?. [ policy ?. ownerAccountID ?? CONST . DEFAULT_NUMBER_ID ] ?? ( { } as PersonalDetails ) ;
@@ -623,6 +627,10 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers
623627 } , [ isLoading , policy ?. employeeList , translate , isOfflineAndNoMemberDataAvailable ] ) ;
624628
625629 const memberCount = data . filter ( ( member ) => member . pendingAction !== CONST . RED_BRICK_ROAD_PENDING_ACTION . DELETE ) . length ;
630+ const hasGustoConnection = ! ! policy ?. connections ?. gusto ;
631+ const shouldShowGustoSyncLink = isPolicyAdmin && hasGustoConnection ;
632+ const isGustoSyncInProgress =
633+ hasGustoConnection && connectionSyncProgress ?. connectionName === CONST . POLICY . CONNECTIONS . NAME . GUSTO && isConnectionInProgress ( connectionSyncProgress , policy ) ;
626634 const isPendingAddOrDelete =
627635 isOffline && data ?. some ( ( member ) => member . pendingAction === CONST . RED_BRICK_ROAD_PENDING_ACTION . DELETE || member . pendingAction === CONST . RED_BRICK_ROAD_PENDING_ACTION . ADD ) ;
628636 const shouldShowSearchBar = data . length > CONST . SEARCH_ITEM_LIMIT ;
@@ -667,9 +675,21 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers
667675
668676 const getHeaderContent = ( ) => (
669677 < View style = { shouldUseNarrowLayout ? styles . workspaceSectionMobile : styles . workspaceSection } >
670- < Text style = { [ styles . pl5 , styles . mb5 , styles . mt3 , styles . textSupporting , isPendingAddOrDelete && styles . offlineFeedbackPending ] } >
671- { translate ( 'workspace.people.workspaceMembersCount' , memberCount ) }
672- </ Text >
678+ < View style = { [ styles . pl5 , styles . mb5 , styles . mt3 , styles . flexRow , styles . alignItemsCenter ] } >
679+ < Text style = { [ styles . textSupporting , styles . flexShrink1 , isPendingAddOrDelete && styles . offlineFeedbackPending ] } >
680+ { translate ( 'workspace.people.workspaceMembersCount' , memberCount ) }
681+ { shouldShowGustoSyncLink && ' ' }
682+ { shouldShowGustoSyncLink && (
683+ < TextLink onPress = { ( ) => Navigation . navigate ( ROUTES . WORKSPACE_HR . getRoute ( policyID ) ) } > { translate ( 'workspace.people.configureGustoSync' ) } </ TextLink >
684+ ) }
685+ </ Text >
686+ { shouldShowGustoSyncLink && isGustoSyncInProgress && (
687+ < ActivityIndicator
688+ size = "small"
689+ style = { styles . ml2 }
690+ />
691+ ) }
692+ </ View >
673693 { ! isEmptyObject ( invitedPrimaryToSecondaryLogins ) && (
674694 < MessagesRow
675695 type = "success"
@@ -825,7 +845,7 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers
825845 return [ ] ;
826846 }
827847
828- const menuItems = [
848+ const menuItems : Array < DropdownOption < ValueOf < typeof CONST . POLICY . SECONDARY_ACTIONS > > > = [
829849 {
830850 icon : icons . Table ,
831851 text : translate ( 'spreadsheet.importSpreadsheet' ) ,
@@ -865,8 +885,39 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers
865885 } ,
866886 ] ;
867887
888+ if ( hasGustoConnection ) {
889+ menuItems . push ( {
890+ icon : icons . Sync ,
891+ text : translate ( 'workspace.people.syncWithGusto' ) ,
892+ onSelected : ( ) => {
893+ if ( isOffline ) {
894+ close ( showRequiresInternetModal ) ;
895+ return ;
896+ }
897+
898+ close ( ( ) => syncConnection ( policy , CONST . POLICY . CONNECTIONS . NAME . GUSTO ) ) ;
899+ } ,
900+ value : CONST . POLICY . SECONDARY_ACTIONS . SYNC_WITH_GUSTO ,
901+ disabled : isGustoSyncInProgress ,
902+ } ) ;
903+ }
904+
868905 return menuItems ;
869- } , [ isPolicyAdmin , icons . Table , icons . Download , translate , isAccountLocked , isOffline , policyID , showLockedAccountModal , showRequiresInternetModal ] ) ;
906+ } , [
907+ isPolicyAdmin ,
908+ icons . Table ,
909+ icons . Download ,
910+ icons . Sync ,
911+ translate ,
912+ isAccountLocked ,
913+ isOffline ,
914+ policyID ,
915+ showLockedAccountModal ,
916+ showRequiresInternetModal ,
917+ hasGustoConnection ,
918+ isGustoSyncInProgress ,
919+ policy ,
920+ ] ) ;
870921
871922 const getHeaderButtons = ( ) => {
872923 if ( ! isPolicyAdmin ) {
0 commit comments