Skip to content

Commit

Permalink
ExpenseForm: Editing expense should also allow user to search for oth…
Browse files Browse the repository at this point in the history
…er collectives (#10798)

* fix: get rid of the static CollectivePicker when editing expenses

* fix: only collectives user admins can be picked when editing expense
  • Loading branch information
kewitz authored Nov 18, 2024
1 parent 90cd3ad commit aef65be
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 76 deletions.
138 changes: 62 additions & 76 deletions components/expenses/ExpenseFormPayeeStep.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useQuery } from '@apollo/client';
import { InfoCircle } from '@styled-icons/boxicons-regular/InfoCircle';
import { Undo } from '@styled-icons/fa-solid/Undo';
import { FastField, Field } from 'formik';
import { first, get, groupBy, isEmpty, omit, pick } from 'lodash';
import { compact, first, get, groupBy, isEmpty, omit, pick } from 'lodash';
import { createPortal } from 'react-dom';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';

Expand All @@ -21,7 +21,7 @@ import { require2FAForAdmins } from '../../lib/policies';
import { flattenObjectDeep } from '../../lib/utils';
import { checkRequiresAddress } from './lib/utils';

import CollectivePicker, { CUSTOM_OPTIONS_POSITION, FLAG_COLLECTIVE_PICKER_COLLECTIVE } from '../CollectivePicker';
import { CUSTOM_OPTIONS_POSITION, FLAG_COLLECTIVE_PICKER_COLLECTIVE } from '../CollectivePicker';
import CollectivePickerAsync from '../CollectivePickerAsync';
import { Box, Flex } from '../Grid';
import Image from '../Image';
Expand Down Expand Up @@ -277,79 +277,6 @@ const ExpenseFormPayeeStep = ({
);
const requiresAddress = checkRequiresAddress(values);
const requiresPayoutMethod = !isOnBehalf && values.payee?.type !== VENDOR;
const canInvite = !values.status;

const collectivePick = canInvite
? ({ id }) => (
<CollectivePickerAsync
inputId={id}
data-cy="select-expense-payee"
isSearchable
collective={values.payee}
onChange={({ value }) => {
if (value) {
const existingProfile = payoutProfiles.find(p => p.slug === value.slug);
const isVendor = value.type === VENDOR;
const isNewlyCreatedProfile = value.members?.some(
m => m.role === 'ADMIN' && m.member.slug === loggedInAccount.slug,
);

const payee = existingProfile || {
...pick(value, ['id', 'name', 'slug', 'email', 'type', 'payoutMethods']),
isInvite: !isNewlyCreatedProfile && !isVendor,
};

if (isNewlyCreatedProfile && !isVendor) {
payee.payoutMethods = [];
}

formik.setFieldValue('payee', payee);
formik.setFieldValue('payoutMethod', isVendor ? first(payee.payoutMethods) || null : null);
setLocationFromPayee(formik, payee);
onChange(payee);
}
}}
styles={{
menu: {
borderRadius: '16px',
},
menuList: {
padding: '8px',
},
}}
emptyCustomOptions={payeeOptions}
customOptionsPosition={CUSTOM_OPTIONS_POSITION.BOTTOM}
getDefaultOptions={build => values.payee && build(values.payee)}
disabled={disablePayee}
invitable
onInvite={onInvite}
LoggedInUser={loggedInAccount}
includeVendorsForHostId={collective.host?.legacyId || undefined}
addLoggedInUserAsAdmin
excludeAdminFields
searchQuery={expenseFormPayeeStepCollectivePickerSearchQuery}
filterResults={collectives => collectives.filter(c => c.type !== CollectiveType.VENDOR || c.hasPayoutMethod)}
loading={loading}
/>
)
: ({ id }) => (
<CollectivePicker
inputId={id}
customOptions={payeeOptions}
getDefaultOptions={build => values.payee && build(values.payee)}
data-cy="select-expense-payee"
isSearchable
disabled={disablePayee}
collective={values.payee}
onChange={({ value }) => {
formik.setFieldValue('payee', value);
formik.setFieldValue('payoutMethod', null);
setLocationFromPayee(formik, value);
onChange(value);
}}
loading={loading}
/>
);

const actionButtons = (
<Flex flex={1} gridGap={[2, 3]} flexWrap="wrap">
Expand Down Expand Up @@ -422,7 +349,66 @@ const ExpenseFormPayeeStep = ({
flex="1"
mt={3}
>
{collectivePick}
{({ id }) => (
<CollectivePickerAsync
inputId={id}
data-cy="select-expense-payee"
isSearchable
collective={values.payee}
onChange={({ value }) => {
if (value) {
const existingProfile = payoutProfiles.find(p => p.slug === value.slug);
const isVendor = value.type === VENDOR;
const isNewlyCreatedProfile = value.members?.some(
m => m.role === 'ADMIN' && m.member.slug === loggedInAccount.slug,
);

const payee = existingProfile || {
...pick(value, ['id', 'name', 'slug', 'email', 'type', 'payoutMethods']),
isInvite: !isNewlyCreatedProfile && !isVendor,
};

if (isNewlyCreatedProfile && !isVendor) {
payee.payoutMethods = [];
}

formik.setFieldValue('payee', payee);
formik.setFieldValue('payoutMethod', isVendor ? first(payee.payoutMethods) || null : null);
setLocationFromPayee(formik, payee);
onChange(payee);
}
}}
styles={{
menu: {
borderRadius: '16px',
},
menuList: {
padding: '8px',
},
}}
emptyCustomOptions={payeeOptions}
customOptionsPosition={CUSTOM_OPTIONS_POSITION.BOTTOM}
getDefaultOptions={build => values.payee && build(values.payee)}
disabled={disablePayee}
invitable={!editingExpense}
onInvite={onInvite}
LoggedInUser={loggedInAccount}
includeVendorsForHostId={collective.host?.legacyId || undefined}
addLoggedInUserAsAdmin
excludeAdminFields
searchQuery={expenseFormPayeeStepCollectivePickerSearchQuery}
filterResults={collectives => {
if (editingExpense) {
return collectives.filter(c => {
const slugs = compact([c.slug, c.host?.slug]);
return loggedInAccount.adminMemberships?.nodes.some(m => slugs.includes(m.account.slug));
});
}
return collectives.filter(c => c.type !== CollectiveType.VENDOR || c.hasPayoutMethod);
}}
loading={loading}
/>
)}
</StyledInputField>
)}
</Field>
Expand Down
4 changes: 4 additions & 0 deletions lib/graphql/v1/queries.js
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,10 @@ export const expenseFormPayeeStepCollectivePickerSearchQuery = gqlV1/* GraphQL *
isActive
isArchived
isHost
host {
id
slug
}
payoutMethods {
legacyId: id
type
Expand Down

0 comments on commit aef65be

Please sign in to comment.