-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #37391 from rushatgabhane/approval-page
[Simplified Collect][Workflows] Select workspace approver
- Loading branch information
Showing
11 changed files
with
236 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
203 changes: 203 additions & 0 deletions
203
src/pages/workspace/workflows/WorkspaceWorkflowsApproverPage.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
import React, {useCallback, useMemo, useState} from 'react'; | ||
import type {SectionListData} from 'react-native'; | ||
import {withOnyx} from 'react-native-onyx'; | ||
import type {OnyxEntry} from 'react-native-onyx'; | ||
import Badge from '@components/Badge'; | ||
import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; | ||
import HeaderWithBackButton from '@components/HeaderWithBackButton'; | ||
import ScreenWrapper from '@components/ScreenWrapper'; | ||
import SelectionList from '@components/SelectionList'; | ||
import type {ListItem, Section} from '@components/SelectionList/types'; | ||
import UserListItem from '@components/SelectionList/UserListItem'; | ||
import useLocalize from '@hooks/useLocalize'; | ||
import useNetwork from '@hooks/useNetwork'; | ||
import useStyleUtils from '@hooks/useStyleUtils'; | ||
import useThemeStyles from '@hooks/useThemeStyles'; | ||
import compose from '@libs/compose'; | ||
import {formatPhoneNumber} from '@libs/LocalePhoneNumber'; | ||
import Log from '@libs/Log'; | ||
import Navigation from '@libs/Navigation/Navigation'; | ||
import * as OptionsListUtils from '@libs/OptionsListUtils'; | ||
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; | ||
import * as PolicyUtils from '@libs/PolicyUtils'; | ||
import * as UserUtils from '@libs/UserUtils'; | ||
import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading'; | ||
import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading'; | ||
import * as Policy from '@userActions/Policy'; | ||
import CONST from '@src/CONST'; | ||
import ONYXKEYS from '@src/ONYXKEYS'; | ||
import type {PersonalDetailsList, PolicyMember} from '@src/types/onyx'; | ||
import {isEmptyObject} from '@src/types/utils/EmptyObject'; | ||
|
||
type WorkspaceWorkflowsApproverPageOnyxProps = { | ||
/** All of the personal details for everyone */ | ||
personalDetails: OnyxEntry<PersonalDetailsList>; | ||
}; | ||
|
||
type WorkspaceWorkflowsApproverPageProps = WorkspaceWorkflowsApproverPageOnyxProps & WithPolicyAndFullscreenLoadingProps; | ||
type MemberOption = Omit<ListItem, 'accountID'> & {accountID: number}; | ||
type MembersSection = SectionListData<MemberOption, Section<MemberOption>>; | ||
|
||
function WorkspaceWorkflowsApproverPage({policy, policyMembers, personalDetails, isLoadingReportData = true}: WorkspaceWorkflowsApproverPageProps) { | ||
const {translate} = useLocalize(); | ||
const policyName = policy?.name ?? ''; | ||
const [searchTerm, setSearchTerm] = useState(''); | ||
const {isOffline} = useNetwork(); | ||
const styles = useThemeStyles(); | ||
const StyleUtils = useStyleUtils(); | ||
|
||
const isDeletedPolicyMember = useCallback( | ||
(policyMember: PolicyMember) => !isOffline && policyMember.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && isEmptyObject(policyMember.errors), | ||
[isOffline], | ||
); | ||
|
||
const [formattedPolicyMembers, formattedApprover] = useMemo(() => { | ||
const policyMemberDetails: MemberOption[] = []; | ||
const approverDetails: MemberOption[] = []; | ||
|
||
Object.entries(policyMembers ?? {}).forEach(([accountIDKey, policyMember]) => { | ||
const accountID = Number(accountIDKey); | ||
if (isDeletedPolicyMember(policyMember)) { | ||
return; | ||
} | ||
|
||
const details = personalDetails?.[accountID]; | ||
if (!details) { | ||
Log.hmmm(`[WorkspaceMembersPage] no personal details found for policy member with accountID: ${accountID}`); | ||
return; | ||
} | ||
|
||
const isOwner = policy?.owner === details.login; | ||
const isAdmin = policyMember.role === CONST.POLICY.ROLE.ADMIN; | ||
|
||
let roleBadge = null; | ||
if (isOwner || isAdmin) { | ||
roleBadge = ( | ||
<Badge | ||
text={isOwner ? translate('common.owner') : translate('common.admin')} | ||
textStyles={styles.textStrong} | ||
badgeStyles={[styles.justifyContentCenter, StyleUtils.getMinimumWidth(60), styles.badgeBordered]} | ||
/> | ||
); | ||
} | ||
|
||
const formattedMember = { | ||
keyForList: accountIDKey, | ||
accountID, | ||
isSelected: policy?.approver === details.login, | ||
isDisabled: policyMember.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || !isEmptyObject(policyMember.errors), | ||
text: formatPhoneNumber(PersonalDetailsUtils.getDisplayNameOrDefault(details)), | ||
alternateText: formatPhoneNumber(details?.login ?? ''), | ||
rightElement: roleBadge, | ||
icons: [ | ||
{ | ||
source: UserUtils.getAvatar(details.avatar, accountID), | ||
name: formatPhoneNumber(details?.login ?? ''), | ||
type: CONST.ICON_TYPE_AVATAR, | ||
id: accountID, | ||
}, | ||
], | ||
errors: policyMember.errors, | ||
pendingAction: policyMember.pendingAction, | ||
}; | ||
|
||
if (policy?.approver === details.login) { | ||
approverDetails.push(formattedMember); | ||
} else { | ||
policyMemberDetails.push(formattedMember); | ||
} | ||
}); | ||
return [policyMemberDetails, approverDetails]; | ||
}, [personalDetails, policyMembers, translate, policy?.approver, StyleUtils, isDeletedPolicyMember, policy?.owner, styles]); | ||
|
||
const sections: MembersSection[] = useMemo(() => { | ||
const sectionsArray: MembersSection[] = []; | ||
|
||
if (searchTerm !== '') { | ||
const filteredOptions = [...formattedApprover, ...formattedPolicyMembers].filter((option) => { | ||
const searchValue = OptionsListUtils.getSearchValueForPhoneOrEmail(searchTerm); | ||
return !!option.text?.toLowerCase().includes(searchValue) || !!option.login?.toLowerCase().includes(searchValue); | ||
}); | ||
return [ | ||
{ | ||
title: undefined, | ||
data: filteredOptions, | ||
shouldShow: true, | ||
}, | ||
]; | ||
} | ||
|
||
sectionsArray.push({ | ||
title: undefined, | ||
data: formattedApprover, | ||
shouldShow: formattedApprover.length > 0, | ||
indexOffset: 0, | ||
}); | ||
|
||
sectionsArray.push({ | ||
title: translate('common.all'), | ||
data: formattedPolicyMembers, | ||
shouldShow: true, | ||
indexOffset: formattedApprover.length, | ||
}); | ||
|
||
return sectionsArray; | ||
}, [formattedPolicyMembers, formattedApprover, searchTerm, translate]); | ||
|
||
const headerMessage = useMemo( | ||
() => (searchTerm && !sections[0].data.length ? translate('common.noResultsFound') : ''), | ||
|
||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
[translate, sections], | ||
); | ||
|
||
const setPolicyApprover = (member: MemberOption) => { | ||
if (!policy?.approvalMode || !personalDetails?.[member.accountID]?.login) { | ||
return; | ||
} | ||
const approver: string = personalDetails?.[member.accountID]?.login ?? policy.approver ?? policy.owner; | ||
Policy.setWorkspaceApprovalMode(policy.id, approver, policy.approvalMode); | ||
Navigation.goBack(); | ||
}; | ||
|
||
return ( | ||
<ScreenWrapper | ||
includeSafeAreaPaddingBottom={false} | ||
testID={WorkspaceWorkflowsApproverPage.displayName} | ||
> | ||
<FullPageNotFoundView | ||
shouldShow={(isEmptyObject(policy) && !isLoadingReportData) || !PolicyUtils.isPolicyAdmin(policy) || PolicyUtils.isPendingDeletePolicy(policy)} | ||
subtitleKey={isEmptyObject(policy) ? undefined : 'workspace.common.notAuthorized'} | ||
onBackButtonPress={PolicyUtils.goBackFromInvalidPolicy} | ||
onLinkPress={PolicyUtils.goBackFromInvalidPolicy} | ||
> | ||
<HeaderWithBackButton | ||
title={translate('workflowsPage.approver')} | ||
subtitle={policyName} | ||
onBackButtonPress={Navigation.goBack} | ||
/> | ||
<SelectionList | ||
sections={sections} | ||
textInputLabel={translate('optionsSelector.findMember')} | ||
textInputValue={searchTerm} | ||
onChangeText={setSearchTerm} | ||
headerMessage={headerMessage} | ||
ListItem={UserListItem} | ||
onSelectRow={setPolicyApprover} | ||
showScrollIndicator | ||
/> | ||
</FullPageNotFoundView> | ||
</ScreenWrapper> | ||
); | ||
} | ||
|
||
WorkspaceWorkflowsApproverPage.displayName = 'WorkspaceWorkflowsApproverPage'; | ||
|
||
export default compose( | ||
withOnyx<WorkspaceWorkflowsApproverPageProps, WorkspaceWorkflowsApproverPageOnyxProps>({ | ||
personalDetails: { | ||
key: ONYXKEYS.PERSONAL_DETAILS_LIST, | ||
}, | ||
}), | ||
withPolicyAndFullscreenLoading, | ||
)(WorkspaceWorkflowsApproverPage); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters