From 3801674816c21a3194ac5d2dd57188ad1f1ca247 Mon Sep 17 00:00:00 2001 From: Gustav Larsson Date: Wed, 26 Feb 2025 15:19:50 +0100 Subject: [PATCH] Lock expense payer and remove "Who is paying?" when submitting invite and coming from a Collective page --- components/AccountHoverCard.tsx | 10 +++++ .../dashboard/filters/HostedAccountFilter.tsx | 14 +----- .../submit-expense/SubmitExpenseFlow.tsx | 44 ++++++++++++++++--- .../submit-expense/SubmitExpenseFlowSteps.tsx | 3 ++ .../form/SubmitExpenseFlowForm.tsx | 4 +- .../form/WhoIsPayingSection.tsx | 2 +- components/submit-expense/useExpenseForm.ts | 13 ++++-- lang/ca.json | 1 + lang/cs.json | 1 + lang/de.json | 1 + lang/en.json | 1 + lang/es.json | 1 + lang/fr.json | 1 + lang/he.json | 1 + lang/it.json | 1 + lang/ja.json | 1 + lang/ko.json | 1 + lang/nl.json | 1 + lang/pl.json | 1 + lang/pt-BR.json | 1 + lang/pt.json | 1 + lang/ru.json | 1 + lang/sk-SK.json | 1 + lang/sv-SE.json | 1 + lang/uk.json | 1 + lang/zh.json | 1 + 26 files changed, 86 insertions(+), 23 deletions(-) diff --git a/components/AccountHoverCard.tsx b/components/AccountHoverCard.tsx index 94f7a04bfaf..ccf09d078dc 100644 --- a/components/AccountHoverCard.tsx +++ b/components/AccountHoverCard.tsx @@ -52,6 +52,16 @@ export const accountHoverCardFields = gql` } `; +export const accountHoverCardQuery = gql` + query Account($slug: String!) { + account(slug: $slug) { + id + ...AccountHoverCardFields + } + } + ${accountHoverCardFields} +`; + type AccountHoverCardProps = { trigger: React.ReactNode; account: { diff --git a/components/dashboard/filters/HostedAccountFilter.tsx b/components/dashboard/filters/HostedAccountFilter.tsx index 963b0978dc4..2cf29a0c40a 100644 --- a/components/dashboard/filters/HostedAccountFilter.tsx +++ b/components/dashboard/filters/HostedAccountFilter.tsx @@ -8,21 +8,11 @@ import { API_V2_CONTEXT, gql } from '../../../lib/graphql/helpers'; import type { AccountHoverCardFieldsFragment, AccountQuery } from '../../../lib/graphql/types/v2/graphql'; import type { Account } from '../../../lib/graphql/types/v2/schema'; -import { AccountHoverCard, accountHoverCardFields } from '../../AccountHoverCard'; +import { AccountHoverCard, accountHoverCardFields, accountHoverCardQuery } from '../../AccountHoverCard'; import Avatar from '../../Avatar'; import ComboSelectFilter from './ComboSelectFilter'; -const accountQuery = gql` - query Account($slug: String!) { - account(slug: $slug) { - id - ...AccountHoverCardFields - } - } - ${accountHoverCardFields} -`; - const hostedAccountFilterSearchQuery = gql` query HostedAccountFilterSearch($searchTerm: String, $hostSlug: String, $orderBy: OrderByInput) { accounts(searchTerm: $searchTerm, host: { slug: $hostSlug }, orderBy: $orderBy) { @@ -44,7 +34,7 @@ export const AccountRenderer = ({ }; inOptionsList?: boolean; // For positioning the HoverCard to the right to prevent blocking options list }) => { - const { data } = useQuery(accountQuery, { + const { data } = useQuery(accountHoverCardQuery, { variables: { slug: account.slug }, fetchPolicy: 'cache-first', context: API_V2_CONTEXT, diff --git a/components/submit-expense/SubmitExpenseFlow.tsx b/components/submit-expense/SubmitExpenseFlow.tsx index cd1c15e190d..8ce76314f73 100644 --- a/components/submit-expense/SubmitExpenseFlow.tsx +++ b/components/submit-expense/SubmitExpenseFlow.tsx @@ -1,6 +1,6 @@ import React from 'react'; import type { FetchResult } from '@apollo/client'; -import { gql, useMutation } from '@apollo/client'; +import { gql, useMutation, useQuery } from '@apollo/client'; import dayjs from 'dayjs'; import { FormikProvider } from 'formik'; import { pick } from 'lodash'; @@ -13,6 +13,7 @@ import { CollectiveType } from '../../lib/constants/collectives'; import { i18nGraphqlException } from '../../lib/errors'; import { API_V2_CONTEXT } from '../../lib/graphql/helpers'; import type { + AccountQuery, CreateExpenseFromDashboardMutation, CreateExpenseFromDashboardMutationVariables, CurrencyExchangeRateInput, @@ -21,10 +22,12 @@ import type { InviteExpenseFromDashboardMutation, InviteExpenseFromDashboardMutationVariables, } from '../../lib/graphql/types/v2/graphql'; -import type { Currency, RecurringExpenseInterval } from '../../lib/graphql/types/v2/schema'; +import type { Account, Currency, RecurringExpenseInterval } from '../../lib/graphql/types/v2/schema'; import { ExpenseStatus, ExpenseType, PayoutMethodType } from '../../lib/graphql/types/v2/schema'; import useLoggedInUser from '../../lib/hooks/useLoggedInUser'; +import { AccountHoverCard, accountHoverCardQuery } from '../AccountHoverCard'; +import Avatar from '../Avatar'; import { Survey, SURVEY_KEY } from '../Survey'; import { Button } from '../ui/Button'; import { Dialog, DialogContent, DialogFooter } from '../ui/Dialog'; @@ -419,9 +422,17 @@ export function SubmitExpenseFlow(props: SubmitExpenseFlowProps) { >
- - - +
+ {props.submitExpenseTo ? ( + }} + /> + ) : ( + + )} +
+ } + /> + ); +}; + function ExpenseFormikContainer(props: { submitExpenseTo?: string; draftKey?: string; @@ -480,6 +513,7 @@ function ExpenseFormikContainer(props: { draftKey: props.draftKey, duplicateExpense: props.duplicateExpense, expenseId: props.expenseId, + canChangeAccount: !props.submitExpenseTo, }); const [activeStep, setActiveStep] = React.useState(Step.WHO_IS_PAYING); diff --git a/components/submit-expense/SubmitExpenseFlowSteps.tsx b/components/submit-expense/SubmitExpenseFlowSteps.tsx index d081ac98d59..c9866d0766b 100644 --- a/components/submit-expense/SubmitExpenseFlowSteps.tsx +++ b/components/submit-expense/SubmitExpenseFlowSteps.tsx @@ -150,6 +150,9 @@ export function SubmitExpenseFlowSteps(props: SubmitExpenseFlowStepsProps) { if (step === Step.EXPENSE_CATEGORY) { return form.options.isAccountingCategoryRequired && form.options.accountingCategories?.length; } + if (step === Step.WHO_IS_PAYING) { + return form.options.canChangeAccount; + } return true; }); diff --git a/components/submit-expense/form/SubmitExpenseFlowForm.tsx b/components/submit-expense/form/SubmitExpenseFlowForm.tsx index 6c2e72a2261..00b6234ee29 100644 --- a/components/submit-expense/form/SubmitExpenseFlowForm.tsx +++ b/components/submit-expense/form/SubmitExpenseFlowForm.tsx @@ -50,7 +50,9 @@ export function SubmitExpenseFlowForm(props: SubmitExpenseFlowFormProps) { onExpenseInviteDeclined={props.onExpenseInviteDeclined} /> )} - + {form.options.canChangeAccount && ( + + )} diff --git a/components/submit-expense/form/WhoIsPayingSection.tsx b/components/submit-expense/form/WhoIsPayingSection.tsx index f5a9df1a122..aa70c0ad377 100644 --- a/components/submit-expense/form/WhoIsPayingSection.tsx +++ b/components/submit-expense/form/WhoIsPayingSection.tsx @@ -17,6 +17,7 @@ import { memoWithGetFormProps } from './helper'; type WhoIsPayingSectionProps = { inViewChange: (inView: boolean, entry: IntersectionObserverEntry) => void; + lockPayee?: boolean; } & ReturnType; function getFormProps(form: ExpenseForm) { @@ -36,7 +37,6 @@ export const WhoIsPayingSection = memoWithGetFormProps(function WhoIsPayingSecti const [isLoading, setIsLoading] = React.useState(true); const lastSubmittedExpense = props.recentlySubmittedExpenses?.nodes?.at?.(0); const lastSubmittedAccount = lastSubmittedExpense && lastSubmittedExpense.account; - const recentlySubmittedAccounts = React.useMemo( () => uniqBy((props.recentlySubmittedExpenses?.nodes || []).map(e => e?.account).filter(Boolean), 'slug'), [props.recentlySubmittedExpenses], diff --git a/components/submit-expense/useExpenseForm.ts b/components/submit-expense/useExpenseForm.ts index 306ba98dbfa..48b65ebd43e 100644 --- a/components/submit-expense/useExpenseForm.ts +++ b/components/submit-expense/useExpenseForm.ts @@ -6,7 +6,7 @@ import dayjs from 'dayjs'; import type { Path, PathValue } from 'dot-path-value'; import type { FieldInputProps, FormikErrors, FormikHelpers } from 'formik'; import { useFormik } from 'formik'; -import { isEmpty, isEqual, isNull, omit, pick, set, uniqBy } from 'lodash'; +import { isEmpty, isEqual, isNull, isUndefined, omit, pick, set, uniqBy } from 'lodash'; import memoizeOne from 'memoize-one'; import type { IntlShape } from 'react-intl'; import { useIntl } from 'react-intl'; @@ -1272,10 +1272,14 @@ async function buildFormOptions( const payeeHost = payee && 'host' in payee ? payee.host : null; const submitter = options.expense?.submitter || query.data?.submitter; - if (account && options.expense?.status === ExpenseStatus.DRAFT) { - options.canChangeAccount = false; + if (isUndefined(startOptions.canChangeAccount)) { + if (account && options.expense?.status === ExpenseStatus.DRAFT) { + options.canChangeAccount = false; + } else { + options.canChangeAccount = true; + } } else { - options.canChangeAccount = true; + options.canChangeAccount = startOptions.canChangeAccount; } if (recentlySubmittedExpenses) { @@ -1450,6 +1454,7 @@ type ExpenseFormStartOptions = { draftKey?: string; isInlineEdit?: boolean; pickSchemaFields?: Record; + canChangeAccount?: boolean; }; export function useExpenseForm(opts: { diff --git a/lang/ca.json b/lang/ca.json index 5a32753a11a..dafdc933047 100644 --- a/lang/ca.json +++ b/lang/ca.json @@ -1534,6 +1534,7 @@ "ExpenseForm.StepPayeeInvoice": "Payee information", "ExpenseForm.Submit": "Submit expense", "ExpenseForm.SubmitRequest": "Envia la sol·licitud", + "ExpenseForm.SubmitTo": "Submit expense to {account}", "ExpenseForm.Type.Request": "Request Grant", "ExpenseFormPayeeStep.PrivateInfo": "This information is private", "ExpenseFormPayeeStep.PrivateInfoDetails": "The payout method details are private and can only be viewed by the Payee and the Host admins.", diff --git a/lang/cs.json b/lang/cs.json index 21cc33460a4..d8b4bc79bff 100644 --- a/lang/cs.json +++ b/lang/cs.json @@ -1534,6 +1534,7 @@ "ExpenseForm.StepPayeeInvoice": "Payee information", "ExpenseForm.Submit": "Odeslat výdaj", "ExpenseForm.SubmitRequest": "Odeslat žádost", + "ExpenseForm.SubmitTo": "Submit expense to {account}", "ExpenseForm.Type.Request": "Request Grant", "ExpenseFormPayeeStep.PrivateInfo": "This information is private", "ExpenseFormPayeeStep.PrivateInfoDetails": "The payout method details are private and can only be viewed by the Payee and the Host admins.", diff --git a/lang/de.json b/lang/de.json index 409cdb54d94..df71d594117 100644 --- a/lang/de.json +++ b/lang/de.json @@ -1534,6 +1534,7 @@ "ExpenseForm.StepPayeeInvoice": "Angaben zum Zahlungsempfänger", "ExpenseForm.Submit": "Ausgaben einreichen", "ExpenseForm.SubmitRequest": "Anfrage absenden", + "ExpenseForm.SubmitTo": "Submit expense to {account}", "ExpenseForm.Type.Request": "Zuwendung anfordern", "ExpenseFormPayeeStep.PrivateInfo": "This information is private", "ExpenseFormPayeeStep.PrivateInfoDetails": "The payout method details are private and can only be viewed by the Payee and the Host admins.", diff --git a/lang/en.json b/lang/en.json index ee68f5d1dcd..7207198981a 100644 --- a/lang/en.json +++ b/lang/en.json @@ -1534,6 +1534,7 @@ "ExpenseForm.StepPayeeInvoice": "Payee information", "ExpenseForm.Submit": "Submit expense", "ExpenseForm.SubmitRequest": "Submit request", + "ExpenseForm.SubmitTo": "Submit expense to {account}", "ExpenseForm.Type.Request": "Request Grant", "ExpenseFormPayeeStep.PrivateInfo": "This information is private", "ExpenseFormPayeeStep.PrivateInfoDetails": "The payout method details are private and can only be viewed by the Payee and the Host admins.", diff --git a/lang/es.json b/lang/es.json index 8fb2eec6012..6a13a455164 100644 --- a/lang/es.json +++ b/lang/es.json @@ -1534,6 +1534,7 @@ "ExpenseForm.StepPayeeInvoice": "Información del beneficiario", "ExpenseForm.Submit": "Enviar gasto", "ExpenseForm.SubmitRequest": "Enviar solicitud", + "ExpenseForm.SubmitTo": "Submit expense to {account}", "ExpenseForm.Type.Request": "Solicitar Subvención", "ExpenseFormPayeeStep.PrivateInfo": "Esta información es privada", "ExpenseFormPayeeStep.PrivateInfoDetails": "Los detalles de los métodos de pago son privados y solo pueden ser visualizados por el beneficiario y los administradores anfitriones.", diff --git a/lang/fr.json b/lang/fr.json index f3479c93138..8305ef73647 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -1534,6 +1534,7 @@ "ExpenseForm.StepPayeeInvoice": "Informations sur le bénéficiaire", "ExpenseForm.Submit": "Envoyer une dépense", "ExpenseForm.SubmitRequest": "Soumettre une requête", + "ExpenseForm.SubmitTo": "Submit expense to {account}", "ExpenseForm.Type.Request": "Demande de subvention", "ExpenseFormPayeeStep.PrivateInfo": "Cette information est privée", "ExpenseFormPayeeStep.PrivateInfoDetails": "Les détails de la méthode de paiement sont privés et ne peuvent être consultés que par le bénéficiaire et les administrateurs de l'hôte.", diff --git a/lang/he.json b/lang/he.json index 0fc9e792d24..c620793093d 100644 --- a/lang/he.json +++ b/lang/he.json @@ -1534,6 +1534,7 @@ "ExpenseForm.StepPayeeInvoice": "פרטים על הספק", "ExpenseForm.Submit": "עדכון הוצאה", "ExpenseForm.SubmitRequest": "סיום ושליחת הבקשה", + "ExpenseForm.SubmitTo": "Submit expense to {account}", "ExpenseForm.Type.Request": "בקשת מענק", "ExpenseFormPayeeStep.PrivateInfo": "This information is private", "ExpenseFormPayeeStep.PrivateInfoDetails": "The payout method details are private and can only be viewed by the Payee and the Host admins.", diff --git a/lang/it.json b/lang/it.json index 5aefa21043f..8aa7b384c14 100644 --- a/lang/it.json +++ b/lang/it.json @@ -1534,6 +1534,7 @@ "ExpenseForm.StepPayeeInvoice": "Payee information", "ExpenseForm.Submit": "Invia spese", "ExpenseForm.SubmitRequest": "Invia richiesta", + "ExpenseForm.SubmitTo": "Submit expense to {account}", "ExpenseForm.Type.Request": "Request Grant", "ExpenseFormPayeeStep.PrivateInfo": "This information is private", "ExpenseFormPayeeStep.PrivateInfoDetails": "The payout method details are private and can only be viewed by the Payee and the Host admins.", diff --git a/lang/ja.json b/lang/ja.json index f21b8da412d..3cabebcd23f 100644 --- a/lang/ja.json +++ b/lang/ja.json @@ -1534,6 +1534,7 @@ "ExpenseForm.StepPayeeInvoice": "支払先情報", "ExpenseForm.Submit": "経費を申請", "ExpenseForm.SubmitRequest": "Submit request", + "ExpenseForm.SubmitTo": "Submit expense to {account}", "ExpenseForm.Type.Request": "Request Grant", "ExpenseFormPayeeStep.PrivateInfo": "This information is private", "ExpenseFormPayeeStep.PrivateInfoDetails": "The payout method details are private and can only be viewed by the Payee and the Host admins.", diff --git a/lang/ko.json b/lang/ko.json index ad0d24c9e55..174e3cb3852 100644 --- a/lang/ko.json +++ b/lang/ko.json @@ -1534,6 +1534,7 @@ "ExpenseForm.StepPayeeInvoice": "수취인 정보", "ExpenseForm.Submit": "경비 제출", "ExpenseForm.SubmitRequest": "요청 제출", + "ExpenseForm.SubmitTo": "Submit expense to {account}", "ExpenseForm.Type.Request": "Request Grant", "ExpenseFormPayeeStep.PrivateInfo": "This information is private", "ExpenseFormPayeeStep.PrivateInfoDetails": "The payout method details are private and can only be viewed by the Payee and the Host admins.", diff --git a/lang/nl.json b/lang/nl.json index ee8d53a0be1..18268cf50f1 100644 --- a/lang/nl.json +++ b/lang/nl.json @@ -1534,6 +1534,7 @@ "ExpenseForm.StepPayeeInvoice": "Payee information", "ExpenseForm.Submit": "Submit expense", "ExpenseForm.SubmitRequest": "Aanvraag verzenden", + "ExpenseForm.SubmitTo": "Submit expense to {account}", "ExpenseForm.Type.Request": "Request Grant", "ExpenseFormPayeeStep.PrivateInfo": "This information is private", "ExpenseFormPayeeStep.PrivateInfoDetails": "The payout method details are private and can only be viewed by the Payee and the Host admins.", diff --git a/lang/pl.json b/lang/pl.json index 5a1fb865e3e..4a1e64ebea6 100644 --- a/lang/pl.json +++ b/lang/pl.json @@ -1534,6 +1534,7 @@ "ExpenseForm.StepPayeeInvoice": "Informacje o odbiorcy", "ExpenseForm.Submit": "Prześlij wydatki", "ExpenseForm.SubmitRequest": "Wyślij wniosek", + "ExpenseForm.SubmitTo": "Submit expense to {account}", "ExpenseForm.Type.Request": "Wniosek o dotację", "ExpenseFormPayeeStep.PrivateInfo": "This information is private", "ExpenseFormPayeeStep.PrivateInfoDetails": "The payout method details are private and can only be viewed by the Payee and the Host admins.", diff --git a/lang/pt-BR.json b/lang/pt-BR.json index ed78986a838..ae48a8adb5f 100644 --- a/lang/pt-BR.json +++ b/lang/pt-BR.json @@ -1534,6 +1534,7 @@ "ExpenseForm.StepPayeeInvoice": "Informações do beneficiário", "ExpenseForm.Submit": "Enviar despesa", "ExpenseForm.SubmitRequest": "Enviar solicitação", + "ExpenseForm.SubmitTo": "Submit expense to {account}", "ExpenseForm.Type.Request": "Solicitação de concessão", "ExpenseFormPayeeStep.PrivateInfo": "Essa informação é privada", "ExpenseFormPayeeStep.PrivateInfoDetails": "Os detalhes do método de pagamento são privados e só podem ser vistos pelo beneficiário e os administradores.", diff --git a/lang/pt.json b/lang/pt.json index b134d69a5c5..01993f753ef 100644 --- a/lang/pt.json +++ b/lang/pt.json @@ -1534,6 +1534,7 @@ "ExpenseForm.StepPayeeInvoice": "Payee information", "ExpenseForm.Submit": "Enviar despesa", "ExpenseForm.SubmitRequest": "Submit request", + "ExpenseForm.SubmitTo": "Submit expense to {account}", "ExpenseForm.Type.Request": "Request Grant", "ExpenseFormPayeeStep.PrivateInfo": "This information is private", "ExpenseFormPayeeStep.PrivateInfoDetails": "The payout method details are private and can only be viewed by the Payee and the Host admins.", diff --git a/lang/ru.json b/lang/ru.json index 70f70d22133..3a81b81bf3b 100644 --- a/lang/ru.json +++ b/lang/ru.json @@ -1534,6 +1534,7 @@ "ExpenseForm.StepPayeeInvoice": "Информация о получателе", "ExpenseForm.Submit": "Отправить расходы", "ExpenseForm.SubmitRequest": "Отправить расходы", + "ExpenseForm.SubmitTo": "Submit expense to {account}", "ExpenseForm.Type.Request": "Запросить грант", "ExpenseFormPayeeStep.PrivateInfo": "This information is private", "ExpenseFormPayeeStep.PrivateInfoDetails": "The payout method details are private and can only be viewed by the Payee and the Host admins.", diff --git a/lang/sk-SK.json b/lang/sk-SK.json index a18df976262..5646d469c07 100644 --- a/lang/sk-SK.json +++ b/lang/sk-SK.json @@ -1534,6 +1534,7 @@ "ExpenseForm.StepPayeeInvoice": "Informácie o príjemcovi", "ExpenseForm.Submit": "Predložiť výdavok", "ExpenseForm.SubmitRequest": "Predložiť požiadavku", + "ExpenseForm.SubmitTo": "Submit expense to {account}", "ExpenseForm.Type.Request": "Žiadosť o grant", "ExpenseFormPayeeStep.PrivateInfo": "This information is private", "ExpenseFormPayeeStep.PrivateInfoDetails": "The payout method details are private and can only be viewed by the Payee and the Host admins.", diff --git a/lang/sv-SE.json b/lang/sv-SE.json index 23d144cc190..64ca3c875af 100644 --- a/lang/sv-SE.json +++ b/lang/sv-SE.json @@ -1534,6 +1534,7 @@ "ExpenseForm.StepPayeeInvoice": "Betalningsmottagarens information", "ExpenseForm.Submit": "Skicka in utgift", "ExpenseForm.SubmitRequest": "Skicka begäran", + "ExpenseForm.SubmitTo": "Submit expense to {account}", "ExpenseForm.Type.Request": "Begär bidrag", "ExpenseFormPayeeStep.PrivateInfo": "This information is private", "ExpenseFormPayeeStep.PrivateInfoDetails": "The payout method details are private and can only be viewed by the Payee and the Host admins.", diff --git a/lang/uk.json b/lang/uk.json index b681fac980e..1f5c4927349 100644 --- a/lang/uk.json +++ b/lang/uk.json @@ -1534,6 +1534,7 @@ "ExpenseForm.StepPayeeInvoice": "Дані одержувача", "ExpenseForm.Submit": "Надіслати витрату", "ExpenseForm.SubmitRequest": "Надіслати запит", + "ExpenseForm.SubmitTo": "Submit expense to {account}", "ExpenseForm.Type.Request": "Request Grant", "ExpenseFormPayeeStep.PrivateInfo": "This information is private", "ExpenseFormPayeeStep.PrivateInfoDetails": "The payout method details are private and can only be viewed by the Payee and the Host admins.", diff --git a/lang/zh.json b/lang/zh.json index a7bed0b65bf..62c099704fd 100644 --- a/lang/zh.json +++ b/lang/zh.json @@ -1534,6 +1534,7 @@ "ExpenseForm.StepPayeeInvoice": "收款人信息", "ExpenseForm.Submit": "提交支出", "ExpenseForm.SubmitRequest": "提交请求", + "ExpenseForm.SubmitTo": "Submit expense to {account}", "ExpenseForm.Type.Request": "请求授权", "ExpenseFormPayeeStep.PrivateInfo": "This information is private", "ExpenseFormPayeeStep.PrivateInfoDetails": "The payout method details are private and can only be viewed by the Payee and the Host admins.",