-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Simplified Collect][Workflows] Select workspace approver #37391
Changes from all commits
646b689
08b1ed2
52d78fa
e30eb65
b45c106
c335676
7028951
15704d4
90beac6
e254f56
bcd89b7
c814c51
d4e4de1
84d4d35
f215f03
353d350
c65636b
3f524ba
1d325d1
1e4e5cb
4e0d42b
97dd3a2
54f846e
f5a0592
76d03fa
06399aa
eeaae97
8a058da
4c629b6
a9fc405
73b8245
89e42af
2f46ead
5a2ed40
ec63de4
a88070c
56ffb47
30655d8
b894a34
138edad
af43199
324c529
c0e4981
35e5d18
a01f23c
bd29662
d619c70
eb09ea1
9ad56cc
e917162
aca798a
f2e6310
a3cacc2
0a3c7f9
e8b63b3
722ae40
f69f94f
3e66027
34aa039
9fc8ee7
baf34f5
4cf6d18
b86d2f0
b935378
caec3f6
b4728c9
6dd7fee
5852f2e
538cacf
eceef70
c5daa84
461d38b
b965a33
4f02a5a
3f0f86b
784ef76
91f5369
8582f29
75e1946
e717fea
2201a27
da72467
b00f2c7
fd23672
e08f217
4d63fe5
01b12a5
675d514
98f1b1a
4b2ccaf
740c93f
511a5ff
86e3c77
73cb58c
07e69d1
66e81e6
449e7a6
74591dc
8c74efc
d4b0344
5c6423c
1c98660
25a5b0f
e2495bf
47738f6
e60a960
8b0d417
45585e8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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]) => { | ||
luacmartins marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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}`); | ||
luacmartins marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line caused a regression here Custom name user searched with email id shows no results found |
||
}); | ||
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm... Do we want this fallback? IMO, if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On main, when approver toggle is enabled, policy owner is set as the default approver. So I don't think we should change that |
||
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 comment was marked as duplicate.
Sorry, something went wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you please link the TS check. i can't find failing check
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry that was mistake 🙇♂️