diff --git a/components/AccountingCategorySelect.tsx b/components/AccountingCategorySelect.tsx index 9201c3d971f..697c79c2909 100644 --- a/components/AccountingCategorySelect.tsx +++ b/components/AccountingCategorySelect.tsx @@ -174,11 +174,7 @@ export const getCategoryLabel = ( /** * Returns true if the category is supported for the given expense type. Host admins can select any category. */ -export const isSupportedExpenseCategory = ( - expenseType: ExpenseType, - category: AccountingCategory, - isHostAdmin: boolean, -) => { +const isSupportedExpenseCategory = (expenseType: ExpenseType, category: AccountingCategory, isHostAdmin: boolean) => { return ( category?.kind === AccountingCategoryKind.EXPENSE && (isHostAdmin || !category?.expensesTypes || category.expensesTypes.includes(expenseType)) diff --git a/components/collective-navbar/ActionsMenu.js b/components/collective-navbar/ActionsMenu.js index f960eb1e348..7e6e0ff6388 100644 --- a/components/collective-navbar/ActionsMenu.js +++ b/components/collective-navbar/ActionsMenu.js @@ -7,16 +7,11 @@ import { ChevronDown } from '@styled-icons/feather/ChevronDown/ChevronDown'; import { AttachMoney } from '@styled-icons/material/AttachMoney'; import { Stack } from '@styled-icons/remix-line/Stack'; import { pickBy } from 'lodash'; -import { useRouter } from 'next/router'; import { FormattedMessage } from 'react-intl'; import styled, { css } from 'styled-components'; import { getContributeRoute } from '../../lib/collective'; -import { isSupportedExpenseType } from '../../lib/expenses'; -import { ExpenseType } from '../../lib/graphql/types/v2/graphql'; -import { PREVIEW_FEATURE_KEYS } from '../../lib/preview-features'; import { getCollectivePageRoute, getDashboardRoute } from '../../lib/url-helpers'; -import { parseToBoolean } from '@/lib/utils'; import ActionButton from '../ActionButton'; import AddFundsBtn from '../AddFundsBtn'; @@ -182,16 +177,6 @@ const CollectiveNavbarActionsMenu = ({ const enabledCTAs = Object.keys(pickBy(callsToAction, Boolean)); const isEmpty = enabledCTAs.length < 1; const hasOnlyOneHiddenCTA = enabledCTAs.length === 1 && hiddenActionForNonMobile === enabledCTAs[0]; - const router = useRouter(); - - const newExpenseFlowOptedOut = parseToBoolean(router?.query?.forceLegacyFlow); - const isNewGrantFlowEnabled = - !newExpenseFlowOptedOut && LoggedInUser?.hasPreviewFeatureEnabled(PREVIEW_FEATURE_KEYS.NEW_EXPENSE_FLOW); - - const isNewExpenseFlowEnabled = - !newExpenseFlowOptedOut && - LoggedInUser?.hasPreviewFeatureEnabled(PREVIEW_FEATURE_KEYS.NEW_EXPENSE_FLOW) && - (!isSupportedExpenseType(collective, ExpenseType.GRANT) || isNewGrantFlowEnabled); // Do not render the menu if there are no available CTAs if (isEmpty) { @@ -227,33 +212,17 @@ const CollectiveNavbarActionsMenu = ({ {callsToAction.hasSubmitExpense && ( - {isNewExpenseFlowEnabled ? ( - - - - - - - ) : ( - - - - - - - )} + + + + + + )} {callsToAction.hasRequestGrant && ( - + diff --git a/components/collective-navbar/index.js b/components/collective-navbar/index.js index f7f0971af8b..f5d39d2796f 100644 --- a/components/collective-navbar/index.js +++ b/components/collective-navbar/index.js @@ -18,12 +18,9 @@ import { display } from 'styled-system'; import { expenseSubmissionAllowed, getContributeRoute, isIndividualAccount } from '../../lib/collective'; import { getFilteredSectionsForCollective, isSectionEnabled } from '../../lib/collective-sections'; import { CollectiveType } from '../../lib/constants/collectives'; -import { isSupportedExpenseType } from '../../lib/expenses'; import { gql } from '../../lib/graphql/helpers'; -import { ExpenseType } from '../../lib/graphql/types/v2/graphql'; import useGlobalBlur from '../../lib/hooks/useGlobalBlur'; import useLoggedInUser from '../../lib/hooks/useLoggedInUser'; -import { PREVIEW_FEATURE_KEYS } from '../../lib/preview-features'; import { getCollectivePageRoute, getDashboardRoute } from '../../lib/url-helpers'; import theme from '@/lib/theme'; @@ -326,14 +323,7 @@ const getDefaultCallsToActions = (collective, sections, isAdmin, LoggedInUser, i /** * Returns the main CTA that should be displayed as a button outside of the action menu in this component. */ -const getMainAction = ( - collective, - callsToAction, - LoggedInUser, - isNewExpenseFlowEnabled = false, - isNewGrantFlowEnabled = false, - onOpenSubmitExpenseModalClick = () => {}, -) => { +const getMainAction = (collective, callsToAction, LoggedInUser, onOpenSubmitExpenseModalClick = () => {}) => { if (!collective || !callsToAction) { return null; } @@ -362,7 +352,7 @@ const getMainAction = ( return { type: NAVBAR_ACTION_TYPE.REQUEST_GRANT, component: ( - + @@ -375,22 +365,13 @@ const getMainAction = ( } else if (callsToAction.includes('hasSubmitExpense')) { return { type: NAVBAR_ACTION_TYPE.SUBMIT_EXPENSE, - component: isNewExpenseFlowEnabled ? ( + component: ( - ) : ( - - - - - - - - ), }; } else if (callsToAction.includes('hasManageSubscriptions')) { @@ -482,12 +463,6 @@ const CollectiveNavbar = ({ const loading = isLoading || dataLoading; - const isNewGrantFlowEnabled = LoggedInUser?.hasPreviewFeatureEnabled(PREVIEW_FEATURE_KEYS.NEW_EXPENSE_FLOW); - - const isNewExpenseFlowEnabled = - LoggedInUser?.hasPreviewFeatureEnabled(PREVIEW_FEATURE_KEYS.NEW_EXPENSE_FLOW) && - (!isSupportedExpenseType(collective, ExpenseType.GRANT) || isNewGrantFlowEnabled); - const isAllowedAddFunds = Boolean(data?.account?.permissions?.addFunds?.allowed); const sections = React.useMemo(() => { return sectionsFromParent || getFilteredSectionsForCollective(collective, isAdmin, isHostAdmin); @@ -497,23 +472,11 @@ const CollectiveNavbar = ({ ...callsToAction, }; const actionsArray = Object.keys(pickBy(callsToAction, Boolean)); - const mainAction = getMainAction( - collective, - actionsArray, - LoggedInUser, - isNewExpenseFlowEnabled, - isNewGrantFlowEnabled, - () => setIsSubmitExpenseModalOpen(true), - ); + const mainAction = getMainAction(collective, actionsArray, LoggedInUser, () => setIsSubmitExpenseModalOpen(true)); const secondAction = actionsArray.length === 2 && - getMainAction( - collective, - without(actionsArray, mainAction?.type), - LoggedInUser, - isNewExpenseFlowEnabled, - isNewGrantFlowEnabled, - () => setIsSubmitExpenseModalOpen(true), + getMainAction(collective, without(actionsArray, mainAction?.type), LoggedInUser, () => + setIsSubmitExpenseModalOpen(true), ); const navbarRef = useRef(undefined); const mainContainerRef = useRef(undefined); diff --git a/components/dashboard/sections/expenses/PaymentRequests.tsx b/components/dashboard/sections/expenses/PaymentRequests.tsx index 33490f550ba..6ebb9406a11 100644 --- a/components/dashboard/sections/expenses/PaymentRequests.tsx +++ b/components/dashboard/sections/expenses/PaymentRequests.tsx @@ -13,9 +13,7 @@ import { ExpenseType, PayoutMethodType, } from '../../../../lib/graphql/types/v2/graphql'; -import useLoggedInUser from '../../../../lib/hooks/useLoggedInUser'; import useQueryFilter from '../../../../lib/hooks/useQueryFilter'; -import { PREVIEW_FEATURE_KEYS } from '../../../../lib/preview-features'; import MessageBoxGraphqlError from '@/components/MessageBoxGraphqlError'; @@ -97,7 +95,6 @@ const PaymentRequests = ({ accountSlug }: DashboardSectionProps) => { const router = useRouter(); const intl = useIntl(); const [isExpenseFlowOpen, setIsExpenseFlowOpen] = React.useState(false); - const { LoggedInUser } = useLoggedInUser(); const views: Views = useMemo( () => [ @@ -177,9 +174,6 @@ const PaymentRequests = ({ accountSlug }: DashboardSectionProps) => { }, }); - const hasNewSubmitExpenseFlow = - LoggedInUser?.hasPreviewFeatureEnabled(PREVIEW_FEATURE_KEYS.NEW_EXPENSE_FLOW) || router.query.newExpenseFlowEnabled; - const pageRoute = `/dashboard/${accountSlug}/payment-requests`; return ( @@ -194,11 +188,9 @@ const PaymentRequests = ({ accountSlug }: DashboardSectionProps) => { /> } actions={ - hasNewSubmitExpenseFlow ? ( - - ) : null + } /> {isSelfHosted && ( diff --git a/components/dashboard/sections/expenses/ReceivedExpenses.tsx b/components/dashboard/sections/expenses/ReceivedExpenses.tsx index 05d4dc4b1f5..5f8b10ef561 100644 --- a/components/dashboard/sections/expenses/ReceivedExpenses.tsx +++ b/components/dashboard/sections/expenses/ReceivedExpenses.tsx @@ -13,9 +13,7 @@ import { ExpenseType, PayoutMethodType, } from '../../../../lib/graphql/types/v2/graphql'; -import useLoggedInUser from '../../../../lib/hooks/useLoggedInUser'; import useQueryFilter from '../../../../lib/hooks/useQueryFilter'; -import { PREVIEW_FEATURE_KEYS } from '../../../../lib/preview-features'; import MessageBoxGraphqlError from '@/components/MessageBoxGraphqlError'; @@ -98,7 +96,6 @@ const ROUTE_PARAMS = ['slug', 'section', 'subpath']; const ReceivedExpenses = ({ accountSlug }: DashboardSectionProps) => { const router = useRouter(); const [isExpenseFlowOpen, setIsExpenseFlowOpen] = React.useState(false); - const { LoggedInUser } = useLoggedInUser(); const { data: metadata, @@ -142,9 +139,6 @@ const ReceivedExpenses = ({ accountSlug }: DashboardSectionProps) => { }, }); - const hasNewSubmitExpenseFlow = - LoggedInUser?.hasPreviewFeatureEnabled(PREVIEW_FEATURE_KEYS.NEW_EXPENSE_FLOW) || router.query.newExpenseFlowEnabled; - const pageRoute = `/dashboard/${accountSlug}/expenses`; return ( @@ -154,11 +148,9 @@ const ReceivedExpenses = ({ accountSlug }: DashboardSectionProps) => { title={} description={} actions={ - hasNewSubmitExpenseFlow ? ( - - ) : null + } /> {isSelfHosted && ( diff --git a/components/dashboard/sections/expenses/SubmittedExpenses.tsx b/components/dashboard/sections/expenses/SubmittedExpenses.tsx index 34538bc187e..6ac4179c488 100644 --- a/components/dashboard/sections/expenses/SubmittedExpenses.tsx +++ b/components/dashboard/sections/expenses/SubmittedExpenses.tsx @@ -6,7 +6,6 @@ import { FormattedMessage } from 'react-intl'; import useLoggedInUser from '../../../../lib/hooks/useLoggedInUser'; import useQueryFilter from '../../../../lib/hooks/useQueryFilter'; -import { PREVIEW_FEATURE_KEYS } from '../../../../lib/preview-features'; import type { Currency } from '@/lib/graphql/types/v2/graphql'; import { ExpenseType } from '@/lib/graphql/types/v2/graphql'; @@ -67,9 +66,6 @@ const SubmittedExpenses = ({ accountSlug }: DashboardSectionProps) => { accountingCategoryKinds: ExpenseAccountingCategoryKinds, }; - const hasNewSubmitExpenseFlow = - LoggedInUser?.hasPreviewFeatureEnabled(PREVIEW_FEATURE_KEYS.NEW_EXPENSE_FLOW) || router.query.newExpenseFlowEnabled; - const pageRoute = `/dashboard/${accountSlug}/submitted-expenses`; return ( @@ -81,18 +77,16 @@ const SubmittedExpenses = ({ accountSlug }: DashboardSectionProps) => { } actions={ - hasNewSubmitExpenseFlow ? ( - - ) : null + } /> diff --git a/components/dashboard/sections/expenses/actions.tsx b/components/dashboard/sections/expenses/actions.tsx index 75482e440fb..5ceeba18222 100644 --- a/components/dashboard/sections/expenses/actions.tsx +++ b/components/dashboard/sections/expenses/actions.tsx @@ -31,10 +31,10 @@ import { ExpenseStatus } from '../../../../lib/graphql/types/v2/graphql'; import { useAsyncCall } from '../../../../lib/hooks/useAsyncCall'; import useClipboard from '../../../../lib/hooks/useClipboard'; import useLoggedInUser from '../../../../lib/hooks/useLoggedInUser'; -import { PREVIEW_FEATURE_KEYS } from '../../../../lib/preview-features'; import { getCollectivePageCanonicalURL, getDashboardRoute } from '../../../../lib/url-helpers'; import { collectiveAdminsMustConfirmAccountingCategory } from '@/components/expenses/lib/accounting-categories'; import type LoggedInUser from '@/lib/LoggedInUser'; +import { PREVIEW_FEATURE_KEYS } from '@/lib/preview-features'; import { shouldShowDuplicateExpenseButton } from '@/components/expenses/ExpenseMoreActionsButton'; import { getDisabledMessage } from '@/components/expenses/PayExpenseButton'; @@ -427,11 +427,7 @@ export function useExpenseActions({ } }; - // Check if the new expense flow is enabled (via preview feature or query param) - const hasNewSubmitExpenseFlow = - LoggedInUser?.hasPreviewFeatureEnabled(PREVIEW_FEATURE_KEYS.NEW_EXPENSE_FLOW) || - router.query.newExpenseFlowEnabled; - const canDuplicateExpense = hasNewSubmitExpenseFlow && shouldShowDuplicateExpenseButton(LoggedInUser, expense); + const canDuplicateExpense = shouldShowDuplicateExpenseButton(LoggedInUser, expense); return { primary: compact([ diff --git a/components/expenses/AddNewAttachedFilesButton.tsx b/components/expenses/AddNewAttachedFilesButton.tsx deleted file mode 100644 index 522dcce0471..00000000000 --- a/components/expenses/AddNewAttachedFilesButton.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import React from 'react'; -import type { FileRejection } from 'react-dropzone'; -import ReactDropzone from 'react-dropzone'; -import { FormattedMessage, useIntl } from 'react-intl'; - -import type { OcrParsingOptionsInput, UploadFileResult } from '../../lib/graphql/types/v2/graphql'; -import { useGraphQLFileUploader } from '../../lib/hooks/useGraphQLFileUploader'; -import { getMessageForRejectedDropzoneFiles, useImageUploader } from '../../lib/hooks/useImageUploader'; -import { attachmentDropzoneParams } from './lib/attachments'; - -import StyledButton from '../StyledButton'; -import { toast } from '../ui/useToast'; - -const AddNewAttachedFilesButton = ({ - disabled, - mockImageGenerator, - onSuccess, - parseDocument = false, - parsingOptions = null, - useGraphQL = false, - onGraphQLSuccess = undefined, -}: AddNewAttachedFilesButtonProps) => { - const kind = 'EXPENSE_ATTACHED_FILE'; - const intl = useIntl(); - const { isUploading, uploadFiles } = useImageUploader({ - isMulti: true, - mockImageGenerator, - onSuccess, - kind, - accept: attachmentDropzoneParams.accept, - }); - - const { isUploading: isUploadingWithGraphQL, uploadFile: uploadFileWithGraphQL } = useGraphQLFileUploader({ - mockImageGenerator, - onSuccess: onGraphQLSuccess, - }); - - const onDrop = React.useCallback( - (acceptedFiles: File[], fileRejections: FileRejection[]) => { - if (fileRejections?.length) { - toast({ - variant: 'error', - message: getMessageForRejectedDropzoneFiles(intl, fileRejections, attachmentDropzoneParams.accept), - }); - } else if (useGraphQL) { - uploadFileWithGraphQL(acceptedFiles.map(file => ({ file, kind, parseDocument, parsingOptions }))); - } else { - uploadFiles(acceptedFiles, fileRejections); - } - }, - [onSuccess, uploadFiles], - ); - - return ( - - {({ getRootProps, getInputProps }) => ( -
- - - +  - - -
- )} -
- ); -}; - -type AddNewAttachedFilesButtonProps = { - disabled?: boolean; - onSuccess: (newFiles: Array<{ url: string }>) => void; - mockImageGenerator?: () => string; - useGraphQL?: boolean; - parseDocument?: boolean; - parsingOptions?: OcrParsingOptionsInput; - onGraphQLSuccess?: (uploadResults: UploadFileResult[]) => void; -}; - -export default AddNewAttachedFilesButton; diff --git a/components/expenses/ConfirmOCRValues.tsx b/components/expenses/ConfirmOCRValues.tsx deleted file mode 100644 index bf8a3d81e0b..00000000000 --- a/components/expenses/ConfirmOCRValues.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react'; -import { some } from 'lodash'; -import { FormattedMessage } from 'react-intl'; - -import { compareItemOCRValues } from './lib/ocr'; - -import MessageBox from '../MessageBox'; -import { Checkbox } from '../ui/Checkbox'; - -import type { ExpenseItemFormValues } from './types/FormValues'; - -export const ConfirmOCRValues = ({ - onConfirm, - items, - currency, -}: { - onConfirm: (boolean) => void; - items: ExpenseItemFormValues[]; - currency: string; -}) => { - const hasMismatches = React.useMemo(() => { - return items.some(item => some(compareItemOCRValues(item), { hasMismatch: true })); - }, [items, currency]); - - return ( - -
- - -
-
- ); -}; diff --git a/components/expenses/CreateExpenseDismissibleIntro.js b/components/expenses/CreateExpenseDismissibleIntro.js deleted file mode 100644 index 040737f9a14..00000000000 --- a/components/expenses/CreateExpenseDismissibleIntro.js +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { FormattedMessage } from 'react-intl'; - -import { HELP_MESSAGE } from '../../lib/constants/dismissable-help-message'; - -import DismissibleMessage from '../DismissibleMessage'; -import MessageBox from '../MessageBox'; -import StyledButton from '../StyledButton'; -import { P } from '../Text'; - -const CreateExpenseDismissibleIntro = ({ collectiveName }) => { - return ( - - {({ dismiss }) => ( - -

- {collectiveName} }} - /> -

- - - -
- )} -
- ); -}; - -export default CreateExpenseDismissibleIntro; diff --git a/components/expenses/EditExpenseDialog.tsx b/components/expenses/EditExpenseDialog.tsx index 418a729ab5e..97193b5bfa5 100644 --- a/components/expenses/EditExpenseDialog.tsx +++ b/components/expenses/EditExpenseDialog.tsx @@ -941,6 +941,7 @@ export default function EditExpenseDialog({ dialogContentClassName, triggerClassName, trigger, + showTriggerTooltip = true, }: { expense: Expense; field: @@ -953,12 +954,15 @@ export default function EditExpenseDialog({ | 'type' | 'attachments' | 'invoiceFile' + | 'attachReceipts' | 'privateMessage'; title: string; description?: string; dialogContentClassName?: string; triggerClassName?: string; trigger?: React.ReactNode; + /** Set to true by default because most triggers are icons. Set this to false when using a button with explicit text. */ + showTriggerTooltip?: boolean; }) { const [open, setOpen] = React.useState(false); const intl = useIntl(); @@ -991,20 +995,27 @@ export default function EditExpenseDialog({ }, [editExpense, intl, expense.id], ); + + const dialogTrigger = ( + + {trigger || ( + + )} + + ); + return ( - - - - {trigger || ( - - )} - - - {title} - + {showTriggerTooltip ? ( + + {dialogTrigger} + {title} + + ) : ( + dialogTrigger + )} diff --git a/components/expenses/Expense.tsx b/components/expenses/Expense.tsx index 2ef326c545c..56ec07bd2aa 100644 --- a/components/expenses/Expense.tsx +++ b/components/expenses/Expense.tsx @@ -1,11 +1,9 @@ import React, { Fragment, useEffect, useRef, useState } from 'react'; import type { ApolloClient } from '@apollo/client'; -import { useMutation } from '@apollo/client'; import type { FetchMoreFunction } from '@apollo/client/react/hooks/useSuspenseQuery'; -import { Undo } from '@styled-icons/fa-solid/Undo'; import { themeGet } from '@styled-system/theme-get'; import dayjs from 'dayjs'; -import { cloneDeep, debounce, get, includes, orderBy, uniqBy, update } from 'lodash'; +import { cloneDeep, get, includes, orderBy, uniqBy, update } from 'lodash'; import { useRouter } from 'next/router'; import { createPortal } from 'react-dom'; import { FormattedMessage, useIntl } from 'react-intl'; @@ -14,46 +12,30 @@ import { css, styled } from 'styled-components'; import { getCollectiveTypeForUrl } from '../../lib/collective'; import CommentType from '../../lib/constants/commentTypes'; import expenseTypes from '../../lib/constants/expenseTypes'; -import { formatErrorMessage, getErrorFromGraphqlException } from '../../lib/errors'; -import { getFilesFromExpense, getPayoutProfiles } from '../../lib/expenses'; +import { getFilesFromExpense } from '../../lib/expenses'; import type { Account, AccountWithHost, Expense as ExpenseType } from '../../lib/graphql/types/v2/graphql'; import { ExpenseStatus } from '../../lib/graphql/types/v2/graphql'; -import useKeyboardKey, { E, ESCAPE_KEY } from '../../lib/hooks/useKeyboardKey'; import useLoggedInUser from '../../lib/hooks/useLoggedInUser'; -import { usePrevious } from '../../lib/hooks/usePrevious'; import { useWindowResize, VIEWPORTS } from '../../lib/hooks/useWindowResize'; -import { itemHasOCR } from './lib/ocr'; import { isFeatureEnabled } from '@/lib/allowed-features'; -import { PREVIEW_FEATURE_KEYS } from '@/lib/preview-features'; import { getCollectivePageRoute } from '@/lib/url-helpers'; -import ConfirmationModal from '../ConfirmationModal'; import CommentForm from '../conversations/CommentForm'; import Thread from '../conversations/Thread'; import { useDrawerActionsContainer } from '../Drawer'; import FilesViewerModal from '../FilesViewerModal'; import { Box, Flex } from '../Grid'; -import { WebsiteName } from '../I18nFormatters'; import CommentIcon from '../icons/CommentIcon'; import Link from '../Link'; import LinkCollective from '../LinkCollective'; import LoadingPlaceholder from '../LoadingPlaceholder'; -import MessageBox from '../MessageBox'; -import StyledButton from '../StyledButton'; -import StyledCheckbox from '../StyledCheckbox'; -import StyledLink from '../StyledLink'; import { SubmitExpenseFlow } from '../submit-expense/SubmitExpenseFlow'; -import { H1, Span } from '../Text'; +import { H1 } from '../Text'; -import { editExpenseMutation } from './graphql/mutations'; import { expensePageQuery } from './graphql/queries'; -import { ConfirmOCRValues } from './ConfirmOCRValues'; -import ExpenseForm, { msg as expenseFormMsg, prepareExpenseForSubmit } from './ExpenseForm'; import ExpenseInviteNotificationBanner from './ExpenseInviteNotificationBanner'; import ExpenseInviteWelcome from './ExpenseInviteWelcome'; import ExpenseMissingReceiptNotificationBanner from './ExpenseMissingReceiptNotificationBanner'; -import ExpenseNotesForm from './ExpenseNotesForm'; -import ExpenseRecurringBanner from './ExpenseRecurringBanner'; import ExpenseSummary from './ExpenseSummary'; import PrivateCommentsMessage from './PrivateCommentsMessage'; import TaxFormMessage from './TaxFormMessage'; @@ -94,22 +76,16 @@ const ExpenseHeader = styled(H1)<{ inDrawer?: boolean }>` } `; -const PAGE_STATUS = { VIEW: 1, EDIT: 2, EDIT_SUMMARY: 3 }; -export const EXPENSE_PAGE_POLLING_INTERVAL = 60 * 1000; - interface ExpenseProps { collectiveSlug?: string; parentCollectiveSlug?: string; legacyExpenseId?: number; draftKey?: string; - edit?: string; client?: ApolloClient; loading?: boolean; error?: any; refetch?: () => void; fetchMore?: FetchMoreFunction; - startPolling?: (number) => void; - stopPolling?: () => void; onClose?: () => void; isRefetchingDataForUser?: boolean; drawerActionsContainer?: object; @@ -120,7 +96,6 @@ interface ExpenseProps { expense: React.ComponentProps & React.ComponentProps & React.ComponentProps & - React.ComponentProps['originalExpense'] & Pick< ExpenseType, | 'id' @@ -158,18 +133,10 @@ function Expense(props: ExpenseProps) { enableKeyboardShortcuts, onClose, } = props; - const { LoggedInUser, loadingLoggedInUser } = useLoggedInUser(); + const { loadingLoggedInUser } = useLoggedInUser(); const intl = useIntl(); const router = useRouter(); - const isNewExpenseSubmissionFlow = - (([expenseTypes.INVOICE, expenseTypes.RECEIPT] as string[]).includes(data?.expense?.type) && - ((LoggedInUser && LoggedInUser.hasPreviewFeatureEnabled(PREVIEW_FEATURE_KEYS.NEW_EXPENSE_FLOW)) || - router.query.newExpenseFlowEnabled)) || - (([expenseTypes.GRANT] as string[]).includes(data?.expense?.type) && - ((LoggedInUser && LoggedInUser.hasPreviewFeatureEnabled(PREVIEW_FEATURE_KEYS.NEW_EXPENSE_FLOW)) || - router.query.newGrantFlowEnabled)); - const [isSubmissionFlowOpen, setIsSubmissionFlowOpen] = React.useState(false); const onContinueSubmissionClick = React.useCallback(() => { @@ -188,136 +155,23 @@ function Expense(props: ExpenseProps) { }, [data?.expense?.account, data?.expense?.legacyId, data?.expense?.type, draftKey, router]); const [state, setState] = useState({ - error: error || null, - status: - draftKey && data?.expense?.status === ExpenseStatus.DRAFT - ? isNewExpenseSubmissionFlow - ? PAGE_STATUS.VIEW - : PAGE_STATUS.EDIT - : PAGE_STATUS.VIEW, - editedExpense: null, - isSubmitting: false, - isPollingEnabled: true, - tos: false, - newsletterOptIn: false, - createdUser: null, showFilesViewerModal: false, }); const [openUrl, setOpenUrl] = useState(router.query.attachmentUrl as string); const [replyingToComment, setReplyingToComment] = useState(null); - const [hasConfirmedOCR, setConfirmedOCR] = useState(false); - const hasItemsWithOCR = Boolean(state.editedExpense?.items?.some(itemHasOCR)); - const mustConfirmOCR = hasItemsWithOCR && !hasConfirmedOCR; - let pollingTimeout = null; - let pollingStarted = false; - let pollingPaused = false; - const drawerActionsContainer = useDrawerActionsContainer(); - - useEffect(() => { - const shouldEditDraft = isNewExpenseSubmissionFlow - ? false - : data?.expense?.status === ExpenseStatus.DRAFT && draftKey; - if (shouldEditDraft) { - setState(state => ({ - ...state, - status: PAGE_STATUS.EDIT, - editedExpense: data?.expense, - })); - } - - handlePolling(); - document.addEventListener('mousemove', handlePolling); - - return () => { - if (props.stopPolling) { - props.stopPolling(); - } - document.removeEventListener('mousemove', handlePolling); - }; - }, []); - - // Disable polling while editing - useEffect(() => { - if ([PAGE_STATUS.EDIT, PAGE_STATUS.EDIT_SUMMARY].includes(state.status)) { - if (state.isPollingEnabled) { - setState(state => ({ ...state, isPollingEnabled: false })); - } - } else if (!state.isPollingEnabled) { - setState(state => ({ ...state, isPollingEnabled: true })); - } - }, [state.status, state.isPollingEnabled]); - - // Automatically edit expense if missing receipt - useEffect(() => { - const expense = data?.expense; - const isMissingReceipt = - expense?.status === ExpenseStatus.PAID && - expense?.type === expenseTypes.CHARGE && - expense?.permissions?.canEdit && - expense?.items?.every(item => !item.url); - - if (props.edit && isMissingReceipt && state.status !== PAGE_STATUS.EDIT) { - onEditBtnClick(); - router.replace(document.location.pathname); - } - }, [props.edit, props.data, state.status]); - - // Update status when data or draftKey changes - useEffect(() => { - const status = - draftKey && data?.expense?.status === ExpenseStatus.DRAFT - ? isNewExpenseSubmissionFlow - ? PAGE_STATUS.VIEW - : PAGE_STATUS.EDIT - : PAGE_STATUS.VIEW; - if (status !== state.status) { - setState(state => ({ ...state, status })); - } - }, [props.data, draftKey, isNewExpenseSubmissionFlow]); - // Scroll to expense's top when changing status - const prevState = usePrevious(state); - - useEffect(() => { - if (prevState && prevState.status !== state.status) { - scrollToExpenseTop(); - } - }, [state.status]); + const drawerActionsContainer = useDrawerActionsContainer(); // Update error state when error prop changes (from Expense query) useEffect(() => { setState(state => ({ ...state, error })); }, [error]); - useKeyboardKey({ - keyMatch: E, - callback: e => { - if (props.enableKeyboardShortcuts && state.status !== PAGE_STATUS.EDIT) { - e.preventDefault(); - onEditBtnClick(); - } - }, - }); - - useKeyboardKey({ - keyMatch: ESCAPE_KEY, - callback: e => { - if (props.isDrawer && state.status !== PAGE_STATUS.EDIT) { - e.preventDefault(); - onClose?.(); - } - }, - }); - - const [editExpense] = useMutation(editExpenseMutation); - const expenseTopRef = useRef(null); - const { status, editedExpense } = state; const { viewport } = useWindowResize(null, { useMinWidth: true }); const isDesktop = viewport === VIEWPORTS.LARGE; const expense = data?.expense; - const loggedInAccount = data?.loggedInAccount; const collective = expense?.account; const host = expense?.host ?? collective?.host; const isDraft = expense?.status === ExpenseStatus.DRAFT; @@ -327,47 +181,16 @@ function Expense(props: ExpenseProps) { expense?.permissions?.canEdit && expense?.items?.every(item => !item.url); const isRecurring = expense?.recurringExpense; - const skipSummary = isMissingReceipt && status === PAGE_STATUS.EDIT; - const [showResetModal, setShowResetModal] = useState(false); - const payoutProfiles = getPayoutProfiles(loggedInAccount); - const canEditPayoutMethod = !expense || isDraft || expense.permissions.canSeePayoutMethodPrivateDetails; - const inDrawer = Boolean(drawerActionsContainer); const threadItems = React.useMemo(() => { const comments = expense?.comments?.nodes || []; const activities = expense?.activities || []; return orderBy([...comments, ...activities], 'createdAt', 'asc'); - }, [expense, inDrawer]); + }, [expense]); - const isEditing = status === PAGE_STATUS.EDIT || status === PAGE_STATUS.EDIT_SUMMARY; const showTaxFormMsg = - isFeatureEnabled(expense?.account, 'TAX_FORMS') && - includes(expense?.requiredLegalDocuments, 'US_TAX_FORM') && - !isEditing; - const isEditingExistingExpense = isEditing && expense !== undefined; - - const handlePolling = debounce(() => { - if (state.isPollingEnabled) { - if (!pollingStarted) { - if (pollingPaused) { - // The polling was paused, so we immediately refetch - props.refetch?.(); - pollingPaused = false; - } - props.startPolling?.(EXPENSE_PAGE_POLLING_INTERVAL); - pollingStarted = true; - } - - clearTimeout(pollingTimeout); - pollingTimeout = setTimeout(() => { - // No mouse movement was detected since 60sec, we stop polling - props.stopPolling?.(); - pollingStarted = false; - pollingPaused = true; - }, EXPENSE_PAGE_POLLING_INTERVAL); - } - }, 100); + isFeatureEnabled(expense?.account, 'TAX_FORMS') && includes(expense?.requiredLegalDocuments, 'US_TAX_FORM'); const clonePageQueryCacheData = () => { const { client } = props; @@ -377,16 +200,6 @@ function Expense(props: ExpenseProps) { return [data, query, variables]; }; - const onEditBtnClick = async () => { - props.stopPolling?.(); - return setState(state => ({ ...state, status: PAGE_STATUS.EDIT, editedExpense: data?.expense })); - }; - - const onCancel = () => { - props.startPolling?.(EXPENSE_PAGE_POLLING_INTERVAL); - return setState(state => ({ ...state, status: PAGE_STATUS.VIEW, editedExpense: null })); - }; - const onDelete = async expense => { const collective = expense.account; const parentCollectiveSlugRoute = collective.parent?.slug ? `${collective.parent?.slug}/` : ''; @@ -395,55 +208,6 @@ function Expense(props: ExpenseProps) { return router.replace(`${parentCollectiveSlugRoute}${collectiveTypeRoute}${collective.slug}/expenses`); }; - const onSummarySubmit = async editedExpense => { - try { - setState(state => ({ ...state, isSubmitting: true, error: null })); - - if (!editedExpense.payee.id && state.newsletterOptIn) { - editedExpense.payee.newsletterOptIn = state.newsletterOptIn; - } - const preparedValues = prepareExpenseForSubmit(editedExpense); - if (!canEditPayoutMethod) { - delete preparedValues.payoutMethod; - } - await editExpense({ - variables: { - expense: preparedValues, - draftKey: data?.expense?.status === ExpenseStatus.DRAFT ? draftKey : null, - }, - }); - if (data?.expense?.type === expenseTypes.CHARGE) { - await refetch(); - } - const createdUser = editedExpense.payee; - props.startPolling?.(EXPENSE_PAGE_POLLING_INTERVAL); - setState(state => ({ - ...state, - status: PAGE_STATUS.VIEW, - isSubmitting: false, - editedExpense: undefined, - error: null, - createdUser, - })); - } catch (e) { - setState(state => ({ ...state, error: getErrorFromGraphqlException(e), isSubmitting: false })); - scrollToExpenseTop(); - } - }; - - const scrollToExpenseTop = () => { - if (expenseTopRef?.current) { - expenseTopRef?.current.scrollIntoView({ behavior: 'smooth' }); - } else { - window.scrollTo(0, 0); - } - }; - const onNotesChanges = e => { - const name = e.target.name; - const value = e.target.value; - setState(state => ({ ...state, editedExpense: { ...state.editedExpense, [name]: value } })); - }; - const onCommentAdded = comment => { // Add comment to cache if not already fetched const [data, query, variables] = clonePageQueryCacheData(); @@ -497,74 +261,6 @@ function Expense(props: ExpenseProps) { setOpenUrl(files?.[0]?.url || null); }, [files, isDesktop, isDrawer]); - const confirmSaveButtons = ( - - setState(state => ({ ...state, status: PAGE_STATUS.EDIT }))} - disabled={state.isSubmitting} - > - ← - - onSummarySubmit(state.editedExpense)} - loading={state.isSubmitting} - disabled={isDraft ? !loggedInAccount && !state.tos : mustConfirmOCR} - > - {isDraft && !loggedInAccount ? ( - - ) : ( - - )} - - - {showResetModal ? ( - setShowResetModal(false)} - header={ - isEditingExistingExpense - ? intl.formatMessage(expenseFormMsg.cancelEditExpense) - : intl.formatMessage(expenseFormMsg.clearExpenseForm) - } - body={ - isEditingExistingExpense - ? intl.formatMessage(expenseFormMsg.confirmCancelEditExpense) - : intl.formatMessage(expenseFormMsg.confirmClearExpenseForm) - } - continueHandler={() => { - onCancel(); - setShowResetModal(false); - }} - {...(isEditingExistingExpense && { - continueLabel: intl.formatMessage({ defaultMessage: 'Yes, cancel editing', id: 'b++lom' }), - cancelLabel: intl.formatMessage({ defaultMessage: 'No, continue editing', id: 'fIsGOi' }), - })} - /> - ) : ( - setShowResetModal(true)} - > - - - {intl.formatMessage( - isEditingExistingExpense ? expenseFormMsg.cancelEditExpense : expenseFormMsg.clearExpenseForm, - )} - - - )} - - ); return ( @@ -594,239 +290,101 @@ function Expense(props: ExpenseProps) { )} - {state.error && ( - - {formatErrorMessage(intl, state.error)} - - )} {showTaxFormMsg && } - {status === PAGE_STATUS.VIEW && - ((expense?.status === ExpenseStatus.UNVERIFIED && state.createdUser) || - (isDraft && !isRecurring && !draftKey)) && ( - + {isDraft && !isRecurring && !draftKey && } + {isMissingReceipt && } + + + {(expense?.permissions?.canDeclineExpenseInvite || + (expense?.status === ExpenseStatus.DRAFT && !isRecurring && draftKey && expense?.draft?.recipientNote)) && ( + )} - {isMissingReceipt && ( - file.url === openUrl)?.id} /> - )} - {status !== PAGE_STATUS.EDIT && ( - - {isNewExpenseSubmissionFlow && - (expense?.permissions?.canDeclineExpenseInvite || - (expense?.status === ExpenseStatus.DRAFT && - !isRecurring && - draftKey && - expense?.draft?.recipientNote)) && ( - - )} - file.url === openUrl)?.id} - onCloneModalOpenChange={isOpen => { - if (isOpen) { - props.stopPolling?.(); - } else { - props.startPolling?.(EXPENSE_PAGE_POLLING_INTERVAL); - } - }} + + + + + + - {status === PAGE_STATUS.EDIT_SUMMARY && ( - - {isDraft && !loggedInAccount && ( - - - - - - e.stopPropagation()}> - - - ), - privacylink: ( - e.stopPropagation()}> - - - ), - }} - /> - } - required - onChange={({ checked }) => { - setState({ ...state, tos: checked }); - }} - /> - - - - . - - } - required - onChange={({ checked }) => { - setState(state => ({ ...state, newsletterOptIn: checked })); - }} - /> - - - )} - {!isDraft && } - {hasItemsWithOCR && ( - + + {loading ? ( + + ) : expense ? ( + threadItems.length} + items={threadItems} + fetchMore={fetchMoreComments} + onCommentDeleted={onCommentDeleted} + loading={loading} + getClickedComment={setReplyingToComment} /> - )} - {isRecurring && } - {drawerActionsContainer ? ( - createPortal(confirmSaveButtons, drawerActionsContainer) - ) : ( - - {confirmSaveButtons} - - )} + ) : null} - )} - - )} - {status === PAGE_STATUS.EDIT && ( - - {loading || loadingLoggedInUser ? ( - - ) : ( - setState(state => ({ ...state, status: PAGE_STATUS.VIEW, editedExpense: null }))} - canEditPayoutMethod={canEditPayoutMethod} - onSubmit={editedExpense => { - if (skipSummary) { - setState(state => ({ - ...state, - editedExpense, - })); - onSummarySubmit(editedExpense); - } else { - setState(state => ({ - ...state, - editedExpense, - status: PAGE_STATUS.EDIT_SUMMARY, - })); - } - }} - validateOnChange - drawerActionsContainer={drawerActionsContainer} - /> - )} - - )} - {!(isEditing && inDrawer) && ( - - - - - - {!inDrawer ? ( - - - {loading ? ( - - ) : expense ? ( - threadItems.length} - items={threadItems} - fetchMore={fetchMoreComments} - onCommentDeleted={onCommentDeleted} - loading={loading} - getClickedComment={setReplyingToComment} + + {expense?.permissions.canComment && ( + + + + + + - ) : null} - - - {expense?.permissions.canComment && ( - - - - - - - - - )} - - ) : ( - threadItems.length} - fetchMore={fetchMoreComments} - onCommentDeleted={onCommentDeleted} - loading={loading} - CommentEntity={{ - ExpenseId: expense && expense.id, - }} - onCommentCreated={onCommentAdded} - canComment={expense?.permissions.canComment} - canUsePrivateNote={expense?.permissions?.canUsePrivateNote} - defaultType={expense?.onHold ? CommentType.PRIVATE_NOTE : CommentType.COMMENT} - /> - )} - - )} + + + )} + + ) : ( + threadItems.length} + fetchMore={fetchMoreComments} + onCommentDeleted={onCommentDeleted} + loading={loading} + CommentEntity={{ + ExpenseId: expense && expense.id, + }} + onCommentCreated={onCommentAdded} + canComment={expense?.permissions.canComment} + canUsePrivateNote={expense?.permissions?.canUsePrivateNote} + defaultType={expense?.onHold ? CommentType.PRIVATE_NOTE : CommentType.COMMENT} + /> + )} + {isSubmissionFlowOpen && ( { - const [files, setFiles] = React.useState(uniqBy(defaultValue, 'url')); - - return ( -
- - - - {title} - - ), - }} - /> -   - - - - {files?.length > 0 && !isSingle && ( - { - const uploadedFiles = [...files, ...newFiles]; - setFiles(uploadedFiles); - onChange(uploadedFiles); - }} - /> - )} - -

- {description} -

- {files?.length > 0 ? ( - { - const updatedFiles = [...files]; - updatedFiles.splice(idx, 1); - setFiles(updatedFiles); - onChange(updatedFiles); - }} - /> - ) : ( - { - let value = uploadedFiles; - if (!Array.isArray(uploadedFiles)) { - value = [uploadedFiles]; - } - setFiles(value); - onChange(value); - }} - /> - )} -
- ); -}; - -type ExpenseAttachedFilesFormProps = { - defaultValue?: Array<{ url: string }>; - title: React.ReactNode; - description: React.ReactNode; - disabled?: boolean; - hasOCRFeature?: boolean; - collective?: Account; - fieldName?: string; - isSingle?: boolean; - kind?: UploadedFileKind; - onChange: (attachedFiles: Array<{ url: string }>) => void; -}; - -export default ExpenseAttachedFilesForm; diff --git a/components/expenses/ExpenseDrawer.tsx b/components/expenses/ExpenseDrawer.tsx index 0042a056afe..f8104bbb0b7 100644 --- a/components/expenses/ExpenseDrawer.tsx +++ b/components/expenses/ExpenseDrawer.tsx @@ -37,7 +37,7 @@ export default function ExpenseDrawer({ const prevExpenseId = usePrevious(openExpenseLegacyId); const id = openExpenseLegacyId || prevExpenseId; - const { data, loading, error, refetch, fetchMore, startPolling, stopPolling } = useQuery(expensePageQuery, { + const { data, loading, error, refetch, fetchMore } = useQuery(expensePageQuery, { variables: id ? getVariablesFromQuery({ ExpenseId: id }) : undefined, skip: !id, @@ -74,8 +74,6 @@ export default function ExpenseDrawer({ client={client} fetchMore={fetchMore} legacyExpenseId={id} - startPolling={startPolling} - stopPolling={stopPolling} isDrawer onClose={handleClose} enableKeyboardShortcuts={hasKeyboardShortcutsEnabled} diff --git a/components/expenses/ExpenseForm.tsx b/components/expenses/ExpenseForm.tsx deleted file mode 100644 index 9452da4a381..00000000000 --- a/components/expenses/ExpenseForm.tsx +++ /dev/null @@ -1,1105 +0,0 @@ -import React, { Fragment } from 'react'; -import { Undo } from '@styled-icons/fa-solid/Undo'; -import { Field, FieldArray, Form, Formik } from 'formik'; -import { first, isEmpty, omit, pick, trimStart } from 'lodash'; -import { useRouter } from 'next/router'; -import { createPortal } from 'react-dom'; -import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; -import { styled } from 'styled-components'; - -import { getAccountReferenceInput } from '../../lib/collective'; -import { CollectiveType } from '../../lib/constants/collectives'; -import expenseTypes from '../../lib/constants/expenseTypes'; -import { PayoutMethodType } from '../../lib/constants/payout-method'; -import { formatErrorMessage } from '../../lib/errors'; -import { getSupportedExpenseTypes } from '../../lib/expenses'; -import { requireFields } from '../../lib/form-utils'; -import { ExpenseLockableFields, ExpenseStatus, UploadedFileKind } from '../../lib/graphql/types/v2/graphql'; -import useLoggedInUser from '../../lib/hooks/useLoggedInUser'; -import { usePrevious } from '../../lib/hooks/usePrevious'; -import { require2FAForAdmins } from '../../lib/policies'; -import { flattenObjectDeep, parseToBoolean } from '../../lib/utils'; -import { userMustSetAccountingCategory } from './lib/accounting-categories'; -import { expenseTypeSupportsAttachments } from './lib/attachments'; -import { addNewExpenseItem, newExpenseItem } from './lib/items'; -import { checkExpenseSupportsOCR, updateExpenseFormWithUploadResult } from './lib/ocr'; -import { - checkRequiresAddress, - expenseTypeSupportsItemCurrency, - getSupportedCurrencies, - validateExpenseTaxes, -} from './lib/utils'; -import { OFICO_MEMBER_ORGANIZATIONS } from '@/lib/preview-features'; - -import AccountingCategorySelect, { isSupportedExpenseCategory } from '../AccountingCategorySelect'; -import ConfirmationModal from '../ConfirmationModal'; -import { expenseTagsQuery } from '../dashboard/filters/ExpenseTagsFilter'; -import { AutocompleteEditTags } from '../EditTags'; -import { Box, Flex } from '../Grid'; -import HTMLContent from '../HTMLContent'; -import { serializeAddress } from '../I18nAddressFields'; -import PrivateInfoIcon from '../icons/PrivateInfoIcon'; -import LoadingPlaceholder from '../LoadingPlaceholder'; -import MessageBox from '../MessageBox'; -import StyledButton from '../StyledButton'; -import StyledCard from '../StyledCard'; -import { StyledCurrencyPicker } from '../StyledCurrencyPicker'; -import StyledHr from '../StyledHr'; -import StyledInput from '../StyledInput'; -import StyledInputFormikField from '../StyledInputFormikField'; -import StyledTextarea from '../StyledTextarea'; -import { Label, P, Span } from '../Text'; - -import ExpenseAttachedFilesForm from './ExpenseAttachedFilesForm'; -import ExpenseFormItems from './ExpenseFormItems'; -import ExpenseFormPayeeInviteNewStep, { validateExpenseFormPayeeInviteNewStep } from './ExpenseFormPayeeInviteNewStep'; -import ExpenseFormPayeeSignUpStep from './ExpenseFormPayeeSignUpStep'; -import ExpenseFormPayeeStep, { checkStepOneCompleted } from './ExpenseFormPayeeStep'; -import ExpenseInviteWelcome from './ExpenseInviteWelcome'; -import { prepareExpenseItemForSubmit, validateExpenseItem } from './ExpenseItemForm'; -import ExpenseRecurringBanner from './ExpenseRecurringBanner'; -import ExpenseSummaryAdditionalInformation from './ExpenseSummaryAdditionalInformation'; -import ExpenseTypeRadioSelect from './ExpenseTypeRadioSelect'; -import ExpenseTypeTag from './ExpenseTypeTag'; -import { validatePayoutMethod } from './PayoutMethodForm'; - -export const msg = defineMessages({ - descriptionPlaceholder: { - id: `ExpenseForm.DescriptionPlaceholder`, - defaultMessage: 'Enter expense title here...', - }, - grantSubjectPlaceholder: { - id: `ExpenseForm.GrantSubjectPlaceholder`, - defaultMessage: 'e.g., Research, software development, etc.', - }, - addNewReceipt: { - id: 'ExpenseForm.AddReceipt', - defaultMessage: 'Add new receipt', - }, - addNewItem: { - id: 'ExpenseForm.AddLineItem', - defaultMessage: 'Add new item', - }, - addNewGrantItem: { - id: 'ExpenseForm.AddGrantItem', - defaultMessage: 'Add grant item', - }, - stepReceipt: { - id: 'ExpenseForm.StepExpense', - defaultMessage: 'Upload one or multiple receipt', - }, - stepInvoice: { - id: 'ExpenseForm.StepExpenseInvoice', - defaultMessage: 'Set invoice details', - }, - stepFundingRequest: { - id: 'ExpenseForm.StepExpenseFundingRequest', - defaultMessage: 'Set grant details', - }, - stepPayee: { - id: 'ExpenseForm.StepPayeeInvoice', - defaultMessage: 'Payee information', - }, - cancelEditExpense: { - id: 'ExpenseForm.CancelEditExpense', - defaultMessage: 'Cancel Edit', - }, - confirmCancelEditExpense: { - id: 'ExpenseForm.ConfirmCancelEditExpense', - defaultMessage: 'Are you sure you want to cancel the edits?', - }, - clearExpenseForm: { - id: 'ExpenseForm.ClearExpenseForm', - defaultMessage: 'Clear Form', - }, - confirmClearExpenseForm: { - id: 'ExpenseForm.ConfirmClearExpenseForm', - defaultMessage: 'Are you sure you want to clear the expense form?', - }, -}); - -const getDefaultExpense = (collective, supportedExpenseTypes) => { - const isSingleSupportedExpenseType = supportedExpenseTypes.length === 1; - - return { - description: '', - longDescription: '', - items: [], - attachedFiles: [], - payee: null, - payoutMethod: undefined, - privateMessage: '', - invoiceInfo: '', - currency: collective.currency, - taxes: null, - type: isSingleSupportedExpenseType ? supportedExpenseTypes[0] : undefined, - accountingCategory: undefined, - payeeLocation: { - address: '', - country: null, - }, - }; -}; - -const CREATE_PAYEE_PROFILE_FIELDS = ['name', 'email', 'legalName', 'organization', 'newsletterOptIn']; - -/** - * Take the expense's data as generated by `ExpenseForm` and strips out all optional data - * like URLs for items when the expense is an invoice. - */ -export const prepareExpenseForSubmit = expenseData => { - const keepAttachedFiles = expenseTypeSupportsAttachments(expenseData.type); - - // Prepare payee - let payee; - if (expenseData.payee) { - // Invites use a different format: the payee ID is passed as a number, not a uuid - // See https://github.com/opencollective/opencollective-api/blob/88e9864a716e4a2ad5237a81cee177b781829f42/server/graphql/v2/input/ExpenseInviteDraftInput.ts#L29 - if (expenseData.payee.isInvite) { - payee = pick(expenseData.payee, ['id', 'legacyId', ...CREATE_PAYEE_PROFILE_FIELDS]); - // The collective picker still uses API V1 for when creating a new profile on the fly - if (payee.legacyId) { - payee.id = payee.legacyId; - delete payee.legacyId; - } - } else if (expenseData.payee.isNewUser) { - payee = pick(expenseData.payee, CREATE_PAYEE_PROFILE_FIELDS); - } else { - payee = getAccountReferenceInput(expenseData.payee); - } - } - - const payeeLocation = expenseData.payee?.isInvite - ? expenseData.payeeLocation - : checkRequiresAddress(expenseData) - ? pick(expenseData.payeeLocation, ['address', 'country', 'structured']) - : null; - - const payoutMethod = pick(expenseData.payoutMethod, ['id', 'name', 'data', 'isSaved', 'type']); - if (payoutMethod.id === 'new') { - payoutMethod.id = null; - } - - const attachedFiles = []; - if (keepAttachedFiles) { - attachedFiles.push(...(expenseData.attachedFiles || [])); - } - - return { - ...pick(expenseData, ['id', 'type', 'tags', 'currency']), - payee, - payeeLocation, - payoutMethod, - attachedFiles: attachedFiles.map(file => pick(file, ['id', 'url', 'name'])), - invoiceFile: expenseData.invoiceFile ? pick(expenseData.invoiceFile, 'url') : null, - tax: expenseData.taxes?.filter(tax => !tax.isDisabled).map(tax => pick(tax, ['type', 'rate', 'idNumber'])), - items: expenseData.items.map(item => prepareExpenseItemForSubmit(expenseData, item)), - accountingCategory: !expenseData.accountingCategory ? null : pick(expenseData.accountingCategory, ['id']), - description: expenseData.description?.trim(), - longDescription: expenseData.longDescription?.trim(), - privateMessage: expenseData.privateMessage?.trim(), - invoiceInfo: expenseData.invoiceInfo?.trim(), - reference: expenseData.reference?.trim(), - }; -}; - -/** - * Validate the expense - */ -const validateExpense = (intl, expense, collective, host, LoggedInUser, canEditPayoutMethod) => { - const isCardCharge = expense.type === expenseTypes.CHARGE; - if (expense.payee?.isInvite) { - return expense.payee.id - ? requireFields(expense, ['description', 'payee', 'payee.id']) - : requireFields(expense, ['description', 'payee', 'payee.name', 'payee.email']); - } - - const errors: Record = isCardCharge - ? {} - : expense.payee?.type === CollectiveType.VENDOR - ? requireFields(expense, ['description', 'payee', 'currency']) - : requireFields(expense, ['description', 'payee', 'payoutMethod', 'currency']); - - if (expense.items.length > 0) { - const itemsErrors = expense.items.map(item => validateExpenseItem(expense, item)); - const hasErrors = itemsErrors.some(errors => !isEmpty(errors)); - if (hasErrors) { - errors.items = itemsErrors; - } - } - - if (expense.taxes?.length) { - const taxesErrors = validateExpenseTaxes(intl, expense.taxes); - if (taxesErrors) { - errors['taxes'] = taxesErrors; - } - } - - if ( - canEditPayoutMethod && - expense.payoutMethod && - // CHARGE expenses have VirtualCard and do not have PayoutMethod - isCardCharge - ) { - const payoutMethodErrors = validatePayoutMethod(expense.payoutMethod); - if (!isEmpty(payoutMethodErrors)) { - errors.payoutMethod = payoutMethodErrors; - } - } - - if (checkRequiresAddress(expense)) { - Object.assign(errors, requireFields(expense, ['payeeLocation.country', 'payeeLocation.address'])); - } - - if (userMustSetAccountingCategory(LoggedInUser, collective, host)) { - Object.assign(errors, requireFields(expense, ['accountingCategory'], { allowNull: true })); - } - - return errors; -}; - -const setLocationFromPayee = (formik, payee) => { - formik.setFieldValue('payeeLocation.country', payee.location.country || null); - formik.setFieldValue('payeeLocation.address', payee.location.address || ''); - formik.setFieldValue('payeeLocation.structured', payee.location.structured); -}; - -const HiddenFragment = styled.div<{ - show: boolean; -}>` - display: ${({ show }) => (show ? 'block' : 'none')}; -`; - -export const EXPENSE_FORM_STEPS = { - PAYEE: 'PAYEE', - EXPENSE: 'EXPENSE', -}; - -const getDefaultStep = (defaultStep, stepOneCompleted, isCreditCardCharge) => { - // Card Charges take priority here because they are technically incomplete. - if (isCreditCardCharge) { - return EXPENSE_FORM_STEPS.EXPENSE; - } else if (!stepOneCompleted) { - return EXPENSE_FORM_STEPS.PAYEE; - } else { - return defaultStep || EXPENSE_FORM_STEPS.PAYEE; - } -}; - -const checkOCREnabled = (router, host) => { - const urlFlag = router.query.ocr && parseToBoolean(router.query.ocr); - return urlFlag !== false && OFICO_MEMBER_ORGANIZATIONS.includes(host?.slug); -}; - -const STYLED_CURRENCY_PICKER_STYLE = { menu: { width: '280px' } }; - -const ExpenseFormBody = ({ - formik, - payoutProfiles, - collective, - host, - expense, - autoFocusTitle, - onCancel, - formPersister, - loggedInAccount, - loading, - shouldLoadValuesFromPersister, - isDraft, - defaultStep, - drawerActionsContainer, - supportedExpenseTypes, - canEditPayoutMethod, -}) => { - const intl = useIntl(); - const { formatMessage } = intl; - const router = useRouter(); - const formRef = React.useRef(undefined); - const { LoggedInUser } = useLoggedInUser(); - const { values, handleChange, errors, setValues, dirty, touched, resetForm, setErrors } = formik; - const hasBaseFormFieldsCompleted = values.type && values.description; - const hasOCRPreviewEnabled = checkOCREnabled(router, host); - const hasOCRFeature = hasOCRPreviewEnabled && checkExpenseSupportsOCR(values.type, LoggedInUser); - const isInvite = values.payee?.isInvite; - const isNewUser = !values.payee?.id; - const isHostAdmin = Boolean(LoggedInUser?.isAdminOfCollective(host)); - const isReceipt = values.type === expenseTypes.RECEIPT; - const isGrant = values.type === expenseTypes.GRANT; - const isCreditCardCharge = values.type === expenseTypes.CHARGE; - const isRecurring = expense && expense.recurringExpense !== null; - const [isOnBehalf, setOnBehalf] = React.useState(false); - const isMissing2FA = require2FAForAdmins(values.payee) && !loggedInAccount?.hasTwoFactorAuth; - const stepOneCompleted = - checkStepOneCompleted(values, isOnBehalf, isMissing2FA, canEditPayoutMethod) && - isEmpty(flattenObjectDeep(omit(errors, 'payoutMethod.data.currency'))); - const stepTwoCompleted = isInvite - ? true - : (stepOneCompleted || isCreditCardCharge) && hasBaseFormFieldsCompleted && values.items.length > 0; - const availableCurrencies = getSupportedCurrencies(collective, values); - const [step, setStep] = React.useState(() => getDefaultStep(defaultStep, stepOneCompleted, isCreditCardCharge)); - const [initWithOCR, setInitWithOCR] = React.useState(null); - - // Only true when logged in and drafting the expense - const [showResetModal, setShowResetModal] = React.useState(false); - const editingExpense = expense !== undefined; - - // Scroll to top when step changes - React.useEffect(() => { - const boundingRect = formRef.current?.getBoundingClientRect(); - if (boundingRect) { - const elemTop = boundingRect.top + window.scrollY; - window.scroll({ top: elemTop - 75 }); - } - }, [step]); - - // When user logs in we set its account as the default payout profile if not yet defined - React.useEffect(() => { - const payeePayoutProfile = values?.payee && payoutProfiles?.find(p => p.slug === values.payee.slug); - if (values?.draft?.payee && !loggedInAccount && !isRecurring) { - formik.setFieldValue('payee', { - ...values.draft.payee, - isInvite: false, - isNewUser: true, - }); - } - // If logged in user edits a DRAFT without a key and it's not the payee, we'll presume they only want to edit the draft and not submit the draft - else if ( - !payeePayoutProfile && - loggedInAccount && - isDraft && - values?.payee.type !== CollectiveType.VENDOR && - !router.query?.key && - !isRecurring - ) { - setOnBehalf(true); - } - // If creating a new expense or completing an expense submitted on your behalf, automatically select your default profile. - else if (!isOnBehalf && (isDraft || !values.payee) && loggedInAccount && !isEmpty(payoutProfiles)) { - const defaultProfile = payeePayoutProfile || first(payoutProfiles); - formik.setFieldValue('payee', defaultProfile); - } - // Update the form state with private fields that were refeched after the user was authenticated - if (isDraft && loggedInAccount) { - const privateFields = ['payoutMethod', 'invoiceInfo']; - for (const field of privateFields) { - if (!values[field] && expense[field]) { - formik.setFieldValue(field, expense[field]); - } - } - } - }, [payoutProfiles, loggedInAccount]); - - // Pre-fill with OCR data when the expense type is set - React.useEffect(() => { - if (initWithOCR && values.type) { - updateExpenseFormWithUploadResult(collective, formik, initWithOCR); - setInitWithOCR(null); - } - }, [initWithOCR, values.type]); - - // Pre-fill address based on the payout profile - React.useEffect(() => { - if (!values.payeeLocation?.address && values.payee?.location) { - setLocationFromPayee(formik, values.payee); - } - }, [values.payee]); - - // Return to Payee step if type is changed and reset some values - const previousType = usePrevious(values.type); - React.useEffect(() => { - if (!isCreditCardCharge && previousType && values.type !== previousType) { - setStep(EXPENSE_FORM_STEPS.PAYEE); - setOnBehalf(false); - - if (!isDraft && values.payee?.isInvite) { - formik.setFieldValue('payee', null); - } - - // Only invoices can have taxes - if (values.taxes?.length && !values.taxes[0].isDisabled && values.type !== expenseTypes.INVOICE) { - formik.setFieldValue('taxes', [{ ...values.taxes[0], isDisabled: true }]); - } - } - - // Reset the accounting category (if not supported by the new expense type) - if (values.accountingCategory && !isSupportedExpenseCategory(values.type, values.accountingCategory, isHostAdmin)) { - formik.setFieldValue('accountingCategory', undefined); - } - - // If the new type does not support setting items currency, reset it - if (!expenseTypeSupportsItemCurrency(values.type)) { - const itemHasExpenseCurrency = item => !item.amountV2?.currency || item.amountV2?.currency === values.currency; - const resetItemAmount = item => ({ ...item, amount: null, amountV2: null }); - const updatedItems = values.items.map(item => (itemHasExpenseCurrency(item) ? item : resetItemAmount(item))); - formik.setFieldValue('items', updatedItems); - } - }, [values.type]); - - React.useEffect(() => { - if (values.payeeLocation?.structured) { - formik.setFieldValue('payeeLocation.address', serializeAddress(values.payeeLocation.structured)); - } - }, [values.payeeLocation]); - - // Handle currency updates - React.useEffect(() => { - // Do nothing while loading - if (loading) { - return; - } - - const payoutMethodCurrency = values.payoutMethod?.currency || values.payoutMethod?.data?.currency; - const hasValidPayoutMethodCurrency = payoutMethodCurrency && availableCurrencies.includes(payoutMethodCurrency); - const hasItemsWithAmounts = values.items.some(item => Boolean(item.amountV2?.valueInCents)); - - // If the currency is not supported anymore, we need to do something - if (!values.currency || !availableCurrencies.includes(values.currency)) { - if (!hasItemsWithAmounts) { - // If no items have amounts yet, we can safely set the default currency - const defaultCurrency = hasValidPayoutMethodCurrency ? payoutMethodCurrency : availableCurrencies[0]; - formik.setFieldValue('currency', defaultCurrency); - } else if (values.currency) { - // If there are items with amounts, we need to reset the currency - formik.setFieldValue('currency', null); - } - } else if ( - payoutMethodCurrency && - hasValidPayoutMethodCurrency && - !hasItemsWithAmounts && - values.currency !== payoutMethodCurrency - ) { - // When the payout method changes, if there's no items yet, we set the default currency to the payout method's currency - formik.setFieldValue('currency', payoutMethodCurrency); - } - }, [loading, values.payoutMethod]); - - // Load values from localstorage - React.useEffect(() => { - if (shouldLoadValuesFromPersister && formPersister && !dirty && !isDraft) { - const formValues = formPersister.loadValues(); - if (formValues) { - // Reset payoutMethod if host is no longer connected to TransferWise - if (formValues.payoutMethod?.type === PayoutMethodType.BANK_ACCOUNT && !host?.transferwise) { - formValues.payoutMethod = undefined; - } - setValues( - omit( - formValues, - // Omit deprecated fields, otherwise it will prevent expense submission - ['location', 'privateInfo'], - ), - ); - } - } - }, [formPersister, dirty]); - - // Save values in localstorage - React.useEffect(() => { - if (dirty && formPersister) { - formPersister.saveValues(values); - } - }, [formPersister, dirty, values]); - - const { setFieldValue } = formik; - const onCurrencyPickerChange = React.useCallback(value => setFieldValue('currency', value), [setFieldValue]); - - let payeeForm; - if (loading) { - payeeForm = ; - } else if (isDraft && !loggedInAccount) { - payeeForm = ( - setStep(EXPENSE_FORM_STEPS.EXPENSE)} - expense={expense} - /> - ); - } else if (isOnBehalf === true && isNewUser) { - payeeForm = ( - { - setStep(EXPENSE_FORM_STEPS.PAYEE); - setOnBehalf(false); - formik.setFieldValue('payee', null); - formik.setFieldValue('payoutMethod', null); - formik.setFieldValue('payeeLocation', null); - }} - onNext={() => { - formik.setFieldValue('payee', { ...values.payee, isInvite: true }); - const errors = validateExpenseFormPayeeInviteNewStep(formik.values); - if (!isEmpty(errors)) { - formik.setErrors(errors); - } else { - setStep(EXPENSE_FORM_STEPS.EXPENSE); - } - }} - /> - ); - } else { - payeeForm = ( - setShowResetModal(true)} - payoutProfiles={payoutProfiles} - loggedInAccount={loggedInAccount} - disablePayee={isDraft && isOnBehalf} - canEditPayoutMethod={canEditPayoutMethod} - onChange={payee => { - setOnBehalf(payee.isInvite); - }} - onNext={values => { - const shouldSkipPayoutMethodValidation = - !canEditPayoutMethod || - ((isOnBehalf || values.payee?.type === CollectiveType.VENDOR) && isEmpty(values.payoutMethod)); - const validation = !shouldSkipPayoutMethodValidation && validatePayoutMethod(values.payoutMethod); - if (isEmpty(validation)) { - setStep(EXPENSE_FORM_STEPS.EXPENSE); - } else { - setErrors({ payoutMethod: validation }); - } - }} - editingExpense={editingExpense} - onInvite={isInvite => { - setOnBehalf(isInvite); - formik.setFieldValue('payeeLocation', {}); - formik.setFieldValue('payee', {}); - formik.setFieldValue('payoutMethod', {}); - }} - drawerActionsContainer={drawerActionsContainer} - /> - ); - } - - const actionButtons = ( - - { - if (isCreditCardCharge) { - onCancel(); - } else { - setStep(EXPENSE_FORM_STEPS.PAYEE); - } - }} - > - ←  - - - { - // When used inside the drawer, the submit button is rendered outside the form (with a portal). The form must be manually submitted. - if (drawerActionsContainer && formRef.current) { - formRef.current.requestSubmit(); - } - }} - > - {isInvite && !isDraft ? ( - - ) : isCreditCardCharge ? ( - - ) : ( - - )} -  → - - {errors.payoutMethod?.data?.currency && touched.items?.some?.(i => i.amountV2?.valueInCents) && ( - - {errors.payoutMethod.data.currency.toString()} - - )} - - setShowResetModal(true)} - > - - {formatMessage(editingExpense ? msg.cancelEditExpense : msg.clearExpenseForm)} - - - ); - - return ( -
- {(expense?.permissions?.canDeclineExpenseInvite || - (expense?.status === ExpenseStatus.DRAFT && expense?.draft?.recipientNote)) && ( - - )} - {!isCreditCardCharge && ( - - )} - {isRecurring && } - {values.type && ( - - {step === EXPENSE_FORM_STEPS.PAYEE ? ( -
- - - {formatMessage(msg.stepPayee)} - - - - - - - {payeeForm} -
- ) : step === EXPENSE_FORM_STEPS.EXPENSE ? ( -
- -

- {values.type === expenseTypes.GRANT ? ( - - {msg} - - ); - }, - }} - /> - ) : ( - - {msg} - - ); - }, - }} - /> - )} -

- -
-

- -

- - -
- {/* Tags */} -
- - - - - - { - formik.setFieldValue( - 'tags', - tags.map(t => t.value.toLowerCase()), - ); - }} - value={values.tags} - /> - -
- {/* Currency */} -
- - - -
-
- - {({ field }) => ( - - )} - -
-
-
-
- {userMustSetAccountingCategory(LoggedInUser, collective, host) && ( -
- - - - -
- - {({ meta }) => ( -
- formik.setFieldValue('accountingCategory', value)} - error={Boolean(meta.error)} - allowNone={!isHostAdmin} - showCode={isHostAdmin} - expenseType={values.type} - expenseValues={values} - predictionStyle="full" - selectFirstOptionIfSingle - /> - {meta.error && meta.touched && ( - - {formatErrorMessage(intl, meta.error)} - - )} -
- )} -
-
- {formik.values.accountingCategory?.instructions && ( - -
- , - }} - /> -
- -
- )} -
- )} - {values.type === expenseTypes.INVOICE && ( - -
-
- - - - ), - }} - /> -
-

- -

- { - e.target.value = trimStart(e.target.value).replace(/\s+/g, ' '); - handleChange(e); - }} - /> -
-
- } - description={ - - } - onChange={invoiceFile => - formik.setFieldValue('invoiceFile', invoiceFile?.length ? invoiceFile[0] : null) - } - isSingle - fieldName="invoiceFile" - defaultValue={values.invoiceFile ? [values.invoiceFile] : []} - /> -
-
- } - description={ - - } - onChange={attachedFiles => formik.setFieldValue('attachedFiles', attachedFiles)} - defaultValue={values.attachedFiles} - /> -
-
- )} - - - - {formatMessage(isReceipt ? msg.stepReceipt : isGrant ? msg.stepFundingRequest : msg.stepInvoice)} - - - addNewExpenseItem(formik)} - minWidth={135} - data-cy="expense-add-item-btn" - disabled={isCreditCardCharge || expense?.lockedFields?.includes(ExpenseLockableFields.AMOUNT)} - > - +  - {formatMessage(isReceipt ? msg.addNewReceipt : isGrant ? msg.addNewGrantItem : msg.addNewItem)} - - - - - {fieldsArrayProps => ( - - )} - - - - {values.type === expenseTypes.GRANT && ( - - } - description={ - - } - onChange={attachedFiles => formik.setFieldValue('attachedFiles', attachedFiles)} - defaultValue={values.attachedFiles} - /> - - )} - - {drawerActionsContainer ? ( - createPortal(actionButtons, drawerActionsContainer) - ) : ( - - - {actionButtons} - - )} -
-
- ) : null} -
- )} - {step === EXPENSE_FORM_STEPS.EXPENSE && ( - - - - )} - {showResetModal && ( - setShowResetModal(false)} - header={editingExpense ? formatMessage(msg.cancelEditExpense) : formatMessage(msg.clearExpenseForm)} - body={ - editingExpense ? formatMessage(msg.confirmCancelEditExpense) : formatMessage(msg.confirmClearExpenseForm) - } - continueHandler={() => { - if (editingExpense) { - onCancel(); - } else { - setStep(EXPENSE_FORM_STEPS.PAYEE); - resetForm({ values: getDefaultExpense(collective, supportedExpenseTypes) }); - if (formPersister) { - formPersister.clearValues(); - window.scrollTo(0, 0); - } - } - setShowResetModal(false); - }} - {...(editingExpense && { - continueLabel: formatMessage({ defaultMessage: 'Yes, cancel editing', id: 'b++lom' }), - cancelLabel: formatMessage({ defaultMessage: 'No, continue editing', id: 'fIsGOi' }), - })} - /> - )} - - ); -}; - -/** - * Main create expense form - */ -const ExpenseForm = ({ - onSubmit, - collective, - host, - expense, - originalExpense, - payoutProfiles, - autoFocusTitle = false, - onCancel, - validateOnChange = false, - formPersister = null, - loggedInAccount, - loading, - shouldLoadValuesFromPersister = false, - defaultStep = undefined, - drawerActionsContainer, - canEditPayoutMethod, -}) => { - const isDraft = expense?.status === ExpenseStatus.DRAFT; - const [hasValidate, setValidate] = React.useState(validateOnChange && !isDraft); - const intl = useIntl(); - const { LoggedInUser } = useLoggedInUser(); - const supportedExpenseTypes = React.useMemo(() => getSupportedExpenseTypes(collective), [collective]); - const initialValues = { ...getDefaultExpense(collective, supportedExpenseTypes), ...expense }; - const validate = expenseData => - validateExpense(intl, expenseData, collective, host, LoggedInUser, canEditPayoutMethod); - - if (isDraft) { - initialValues.items = expense.draft.items?.map(newExpenseItem) || []; - initialValues.taxes = expense.draft.taxes; - initialValues.attachedFiles = expense.draft.attachedFiles; - initialValues.reference = expense.draft.reference; - initialValues.payoutMethod = expense.draft.payoutMethod || expense.payoutMethod; - initialValues.payeeLocation = expense.draft.payeeLocation; - initialValues.payee = expense.recurringExpense ? expense.payee : expense.draft.payee; - initialValues.invoiceFile = expense.draft.invoiceFile; - } - - return ( - { - // We initially let the browser do the validation. Then once users try to submit the - // form at least once, we validate on each change to make sure they fix all the errors. - const errors = validate(values); - if (!isEmpty(errors)) { - setValidate(true); - formik.setErrors(errors); - } else { - return onSubmit(values); - } - }} - > - {formik => ( - - )} - - ); -}; - -export default React.memo(ExpenseForm); diff --git a/components/expenses/ExpenseFormItems.js b/components/expenses/ExpenseFormItems.js deleted file mode 100644 index c1bb7aae8cd..00000000000 --- a/components/expenses/ExpenseFormItems.js +++ /dev/null @@ -1,307 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { accountHasGST, accountHasVAT, TaxType } from '@opencollective/taxes'; -import { filter, isEmpty, range, some } from 'lodash'; -import { FormattedMessage, injectIntl } from 'react-intl'; - -import expenseTypes from '../../lib/constants/expenseTypes'; -import { formatErrorMessage } from '../../lib/errors'; -import { ExpenseLockableFields } from '../../lib/graphql/types/v2/graphql'; -import { i18nTaxType } from '../../lib/i18n/taxes'; -import { attachmentDropzoneParams } from './lib/attachments'; -import { expenseItemsMustHaveFiles, newExpenseItem } from './lib/items'; -import { compareItemOCRValues, itemHasOCR, updateExpenseFormWithUploadResult } from './lib/ocr'; -import { expenseTypeSupportsItemCurrency } from './lib/utils'; - -import Dropzone from '../Dropzone'; -import { Box, Flex } from '../Grid'; -import { I18nBold } from '../I18nFormatters'; -import MessageBox from '../MessageBox'; -import StyledCheckbox from '../StyledCheckbox'; -import StyledHr from '../StyledHr'; -import { TaxesFormikFields } from '../taxes/TaxesFormikFields'; -import { P, Span } from '../Text'; -import { toast } from '../ui/useToast'; - -import ExpenseAmountBreakdown from './ExpenseAmountBreakdown'; -import ExpenseItemForm from './ExpenseItemForm'; - -/** Converts a list of filenames to expense item objects */ -const filesListToItems = (files, expenseCurrency) => files.map(({ url }) => newExpenseItem({ url }, expenseCurrency)); - -class ExpenseFormItems extends React.PureComponent { - static propTypes = { - collective: PropTypes.object, - /** @ignore from injectIntl */ - intl: PropTypes.object, - /** Array helper as provided by formik */ - push: PropTypes.func.isRequired, - /** Array helper as provided by formik */ - remove: PropTypes.func.isRequired, - hasOCRFeature: PropTypes.bool, - /** Formik */ - form: PropTypes.shape({ - values: PropTypes.object.isRequired, - touched: PropTypes.object, - errors: PropTypes.object, - setFieldValue: PropTypes.func, - setFieldTouched: PropTypes.func, - }).isRequired, - expense: PropTypes.shape({ - lockedFields: PropTypes.arrayOf(PropTypes.string), - }), - }; - - componentDidMount() { - const { values } = this.props.form; - if ([expenseTypes.INVOICE, expenseTypes.GRANT].includes(values.type)) { - this.addDefaultItem(); - } - } - - componentDidUpdate(oldProps) { - const { values, touched } = this.props.form; - - // Add or remove the default item when changing the expense type - if (oldProps.form.values.type !== values.type) { - if ([expenseTypes.INVOICE, expenseTypes.GRANT].includes(values.type)) { - this.addDefaultItem(); - } else if (!touched.items && values.items?.length === 1) { - const firstItem = values.items[0]; - if (!firstItem.url && !firstItem.description && !firstItem.amountV2?.valueInCents) { - this.props.remove(0); - } - } - } - } - - addDefaultItem() { - const { values } = this.props.form; - if (isEmpty(values.items)) { - this.props.push(newExpenseItem({}, values.currency)); - } - } - - remove = item => { - const idx = this.props.form.values.items.findIndex(a => a.id === item.id); - if (idx !== -1) { - this.props.remove(idx); - } - }; - - reportErrors(errors) { - if (errors?.length) { - const firstMessage = typeof errors[0] === 'string' ? errors[0] : errors[0].message; - toast({ - variant: 'error', - title: ( - - ), - message: formatErrorMessage(this.props.intl, firstMessage), - }); - } - } - - getApplicableTaxType() { - const { collective, form } = this.props; - if (form.values.type === expenseTypes.INVOICE) { - if (accountHasVAT(collective, collective.host)) { - return TaxType.VAT; - } else if (accountHasGST(collective.host || collective)) { - return TaxType.GST; - } - } - } - - hasTaxFields(taxType) { - if (!taxType) { - return false; - } - - const { values } = this.props.form; - if (!values.taxes) { - // If tax is not initialized (create expense) we render the fields by default - return true; - } else { - // If tax is initialized (edit expense) we render the fields only if there are values - return values.taxes[0] && !values.taxes[0].isDisabled; - } - } - - getUploadingItemsIndexes() { - const { items } = this.props.form.values; - return filter(range(items.length), index => items[index].__isUploading); - } - - getItemsOCRComparisons(items) { - return items.reduce((comparisons, item) => { - comparisons[item.id] = compareItemOCRValues(item); - return comparisons; - }, {}); - } - - removeMultiUploadingItems() { - const isMultiUploadingItem = item => item.__isUploading && item.__fromInput === 'multi'; - const otherItems = this.props.form.values.items.filter(item => !isMultiUploadingItem(item)); - this.props.form.setFieldValue('items', otherItems); - } - - render() { - const { hasOCRFeature, collective, expense } = this.props; - const { values, errors, setFieldValue } = this.props.form; - const requireFile = expenseItemsMustHaveFiles(values.type); - const isGrant = values.type === expenseTypes.GRANT; - const isInvoice = values.type === expenseTypes.INVOICE; - const isCreditCardCharge = values.type === expenseTypes.CHARGE; - const itemsHaveCurrencyPicker = expenseTypeSupportsItemCurrency(values.type); - const items = values.items || []; - const hasItems = items.length > 0; - const itemsWithOCR = items.filter(itemHasOCR); - const itemsOCRComparisons = this.getItemsOCRComparisons(itemsWithOCR); - const ocrMismatchWarningFields = ['amountV2', 'incurredAt']; - const hasOCRWarnings = some(itemsOCRComparisons, comparison => - some(comparison, (value, field) => ocrMismatchWarningFields.includes(field) && value.hasMismatch), - ); - const amountIsLocked = expense?.lockedFields?.includes(ExpenseLockableFields.AMOUNT); - - if (!hasItems && requireFile) { - return ( - - filesListToItems(files).map(this.props.push)} - onReject={uploadErrors => { - this.reportErrors(uploadErrors); - this.removeMultiUploadingItems(); - }} - mockImageGenerator={index => `https://loremflickr.com/120/120/invoice?lock=${index}`} - className="mb-4" - useGraphQL={hasOCRFeature} - parseDocument={hasOCRFeature} - parsingOptions={{ currency: values.currency }} - onDrop={files => { - // Insert dummy items to display the loading states when uploading through GraphQL - if (hasOCRFeature) { - this.props.form.setFieldValue( - 'items', - files.map(file => - newExpenseItem({ __isUploading: true, __file: file, __fromInput: 'multi' }, values.currency), - ), - ); - } - }} - onGraphQLSuccess={uploadResults => { - const indexesToUpdate = this.getUploadingItemsIndexes(); - updateExpenseFormWithUploadResult(collective, this.props.form, uploadResults, indexesToUpdate); - }} - > -

- -

-
-
- ); - } - - const onRemove = requireFile || items.length > 1 ? this.remove : null; - const taxType = this.getApplicableTaxType(); - const hasTaxFields = this.hasTaxFields(taxType); - return ( - - {items.map((attachment, index) => ( - this.reportErrors([e])} - isOptional={values.payee?.isInvite} - editOnlyDescriptiveInfo={isCreditCardCharge} - isInvoice={isInvoice} - hasOCRFeature={hasOCRFeature} - collective={collective} - ocrComparison={itemsOCRComparisons[attachment.id]} - hasCurrencyPicker={itemsHaveCurrencyPicker} - amountIsLocked={amountIsLocked} - isSubjectToTax={Boolean(taxType)} - /> - ))} - {/** Do not display OCR warnings for OCR charges since date/amount can't be changed */} - {!isCreditCardCharge && itemsWithOCR.length > 0 && ( - - - - )} - {taxType && ( -
- - - - - - - - { - // Using "isDisabled" flag rather than removing to preserve data when enabled/disabled - if (checked) { - const tax = { ...values.taxes?.[0], type: taxType, isDisabled: false }; - setFieldValue('taxes', [tax]); - } else { - setFieldValue('taxes.0.isDisabled', true); - } - }} - label={ - - } - /> - -
- )} - {taxType && !hasTaxFields && } - - - {hasTaxFields && ( - - )} - - - - - -
- ); - } -} - -export default injectIntl(ExpenseFormItems); diff --git a/components/expenses/ExpenseFormPayeeInviteNewStep.js b/components/expenses/ExpenseFormPayeeInviteNewStep.js deleted file mode 100644 index 8a6ad42ef08..00000000000 --- a/components/expenses/ExpenseFormPayeeInviteNewStep.js +++ /dev/null @@ -1,464 +0,0 @@ -import React, { Fragment } from 'react'; -import { themeGet } from '@styled-system/theme-get'; -import { FastField, Field } from 'formik'; -import { get, isEmpty, omit } from 'lodash'; -import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; -import { styled } from 'styled-components'; - -import { suggestSlug } from '../../lib/collective'; -import { EMPTY_ARRAY } from '../../lib/constants/utils'; -import { ERROR, isErrorType } from '../../lib/errors'; -import { formatFormErrorMessage, requireFields, verifyEmailPattern } from '../../lib/form-utils'; -import { reportValidityHTML5 } from '../../lib/utils'; - -import { Box, Flex, Grid } from '../Grid'; -import MessageBox from '../MessageBox'; -import StyledButton from '../StyledButton'; -import StyledCard from '../StyledCard'; -import StyledHr from '../StyledHr'; -import StyledInput from '../StyledInput'; -import StyledInputField from '../StyledInputField'; -import StyledInputFormikField from '../StyledInputFormikField'; -import StyledInputGroup from '../StyledInputGroup'; -import StyledInputLocation from '../StyledInputLocation'; -import StyledLinkButton from '../StyledLinkButton'; -import StyledTextarea from '../StyledTextarea'; -import { P } from '../Text'; - -import PayoutMethodForm from './PayoutMethodForm'; -import PayoutMethodSelect from './PayoutMethodSelect'; - -const msg = defineMessages({ - accountType: { - id: `ExpenseForm.inviteeLabel`, - defaultMessage: 'Who will receive the money for this expense?', - }, - nameLabel: { - id: `ContactName`, - defaultMessage: 'Contact name', - }, - emailTitle: { - id: 'User.EmailAddress', - defaultMessage: 'Email address', - }, - payoutOptionLabel: { - id: `ExpenseForm.PayoutOptionLabel`, - defaultMessage: 'Payout method', - }, - invoiceInfo: { - id: 'ExpenseForm.InvoiceInfo', - defaultMessage: 'Additional invoice information', - }, - invoiceInfoPlaceholder: { - id: 'ExpenseForm.InvoiceInfoPlaceholder', - defaultMessage: 'Tax ID, VAT number, etc. This information will be printed on your invoice.', - }, - country: { - id: 'ExpenseForm.ChooseCountry', - defaultMessage: 'Choose country', - }, - address: { - id: 'ExpenseForm.AddressLabel', - defaultMessage: 'Physical address', - }, - recipientNoteLabel: { - id: 'ExpenseForm.RecipientNoteLabel', - defaultMessage: 'Add a note for the recipient', - }, - additionalInfo: { - id: 'ExpenseForm.inviteAdditionalInfo', - defaultMessage: 'Want to enter payout details, such as a PayPal address or bank account?', - }, - orgNameLabel: { - id: 'ExpenseForm.inviteeOrgNameLabel', - defaultMessage: "What's the name of the organization?", - }, - orgSlugLabel: { - id: 'createCollective.form.slugLabel', - defaultMessage: 'Set your profile URL', - }, - orgSlugErrorTaken: { - id: 'createCollective.form.error.slug.taken', - defaultMessage: 'Profile URL already taken', - }, - orgWebsiteLabel: { - id: 'createOrg.form.websiteLabel', - defaultMessage: 'Organization website', - }, - orgDescriptionLabel: { - id: 'ExpenseForm.inviteOrgDescriptionLabel', - defaultMessage: 'Organization description', - }, -}); - -const PAYEE_TYPE = { - USER: 'USER', - ORG: 'ORG', -}; - -const Fieldset = styled.fieldset` - border: none; - padding: 0; - margin: 0; -`; - -const RadioOptionContainer = styled.label` - align-items: center; - display: flex; - flex: 1 1 50%; - font-size: 14px; - font-weight: normal; - line-height: 20px; - margin: 0px; - padding: 6px 16px; - cursor: pointer; - - &:not(:last-child) { - @media (max-width: ${themeGet('breakpoints.0')}) { - border-bottom: 1px solid #dcdee0; - } - @media (min-width: ${themeGet('breakpoints.0')}) { - border-right: 1px solid #dcdee0; - } - } -`; - -export const validateExpenseFormPayeeInviteNewStep = values => { - const errors = requireFields(values, ['payee.name', 'payee.email']); - if (!get(errors, 'payee.email')) { - verifyEmailPattern(errors, values, 'payee.email'); - } - return errors; -}; - -const ExpenseFormPayeeInviteNewStep = ({ - payeeFieldName = 'payee', - recipientNoteFieldName = 'recipientNote', - payoutMethodFieldName = 'payoutMethod', - optionalPayoutMethod = false, - formik, - collective = null, - onBack, - onNext, - hidePayoutDetails = false, -}) => { - const intl = useIntl(); - const { formatMessage } = intl; - const { values, touched, errors } = formik; - const payeeValue = get(formik.values, payeeFieldName); - const setPayoutMethod = React.useCallback(({ value }) => formik.setFieldValue(payoutMethodFieldName, value), []); - const payeeType = payeeValue?.organization ? PAYEE_TYPE.ORG : PAYEE_TYPE.USER; - const [showAdditionalInfo, setAdditionalInfo] = React.useState( - !isEmpty(values.payeeLocation) || !isEmpty(get(values, payoutMethodFieldName)), - ); - - React.useEffect(() => { - if (payeeValue?.organization?.name && !touched.payee?.organization?.slug) { - const slug = suggestSlug(payeeValue.organization.name); - if (payeeValue.organization.slug !== slug) { - formik.setFieldValue(`${payeeFieldName}.organization.slug`, suggestSlug(payeeValue.organization.name)); - } - } - }, [payeeValue?.organization?.name]); - - const changePayeeType = e => { - e.stopPropagation(); - const newPayeeType = e.target.value; - if (newPayeeType === PAYEE_TYPE.USER) { - formik.setFieldValue(payeeFieldName, omit(payeeValue, ['organization'])); - } else { - formik.setFieldValue(payeeFieldName, { ...payeeValue, organization: { name: '' } }); - } - }; - - return ( - - - -
- - - - - - Personal Account - - - - - - Organization Account - - -
-
-
- - {payeeType === PAYEE_TYPE.ORG && ( - - - - {({ field }) => ( - - {inputProps => } - - )} - - - {({ field }) => ( - - {inputProps => } - - )} - - - {({ field }) => ( - - {inputProps => } - - )} - - - - {({ field }) => ( - - {inputProps => } - - )} - - - - )} - - - - - {({ field }) => } - - - - - {({ field }) => } - - - - {hidePayoutDetails ? null : !showAdditionalInfo ? ( - - -

{formatMessage(msg.additionalInfo)}

-

- setAdditionalInfo(true)}> - - -

-
-
- ) : ( - - - { - formik.setFieldValue('payeeLocation', values); - }} - location={values.payeeLocation} - errors={errors.payeeLocation} - required={false} - /> - - - - {({ field }) => ( - - {({ id, error }) => ( - - )} - - )} - - {get(values, payoutMethodFieldName) && ( - - {({ field, meta }) => ( - - - - )} - - )} - - - - {({ field }) => ( - - {inputProps => ( - - )} - - )} - - - )} -
- - - {({ field }) => ( - - {inputProps => } - - )} - - - {payeeValue && (onBack || onNext) && ( - - - - {onBack && ( - { - onBack?.(); - }} - > - ←  - - - )} - { - const isFormValid = reportValidityHTML5(e.target.form); - const errors = validateExpenseFormPayeeInviteNewStep(values); - if (!isEmpty(errors)) { - formik.setErrors(errors); - } else if (isFormValid) { - onNext(); - } - }} - > - -  → - - - - )} -
- ); -}; - -export default ExpenseFormPayeeInviteNewStep; diff --git a/components/expenses/ExpenseFormPayeeSignUpStep.js b/components/expenses/ExpenseFormPayeeSignUpStep.js deleted file mode 100644 index f1fb4f60593..00000000000 --- a/components/expenses/ExpenseFormPayeeSignUpStep.js +++ /dev/null @@ -1,467 +0,0 @@ -import React, { Fragment } from 'react'; -import { useLazyQuery } from '@apollo/client'; -import { themeGet } from '@styled-system/theme-get'; -import { FastField, Field } from 'formik'; -import { debounce, isEmpty, omit, pick } from 'lodash'; -import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; -import { styled } from 'styled-components'; - -import { suggestSlug } from '../../lib/collective'; -import expenseTypes from '../../lib/constants/expenseTypes'; -import { EMPTY_ARRAY } from '../../lib/constants/utils'; -import { ERROR, isErrorType } from '../../lib/errors'; -import { formatFormErrorMessage } from '../../lib/form-utils'; -import { gql } from '../../lib/graphql/helpers'; -import { ExpenseLockableFields } from '../../lib/graphql/types/v2/graphql'; -import { flattenObjectDeep } from '../../lib/utils'; - -import { Box, Flex, Grid } from '../Grid'; -import LoginBtn from '../LoginBtn'; -import StyledButton from '../StyledButton'; -import StyledCard from '../StyledCard'; -import StyledHr from '../StyledHr'; -import StyledInput from '../StyledInput'; -import StyledInputField from '../StyledInputField'; -import StyledInputGroup from '../StyledInputGroup'; -import StyledInputLocation from '../StyledInputLocation'; -import StyledTextarea from '../StyledTextarea'; -import { Span } from '../Text'; - -import PayoutMethodForm, { validatePayoutMethod } from './PayoutMethodForm'; -import PayoutMethodSelect from './PayoutMethodSelect'; - -const validateSlugQuery = gql` - query ValidateSlug($slug: String) { - account(slug: $slug, throwIfMissing: false) { - id - slug - } - } -`; - -const msg = defineMessages({ - nameLabel: { - id: `ExpenseForm.inviteeLabel`, - defaultMessage: 'Who will receive the money for this expense?', - }, - legalName: { id: 'LegalName', defaultMessage: 'Legal Name' }, - emailLabel: { - id: 'Form.yourEmail', - defaultMessage: 'Your email address', - }, - orgNameLabel: { - id: 'ExpenseForm.inviteeOrgNameLabel', - defaultMessage: "What's the name of the organization?", - }, - orgSlugLabel: { - id: 'createCollective.form.slugLabel', - defaultMessage: 'Set your profile URL', - }, - orgSlugErrorTaken: { - id: 'createCollective.form.error.slug.taken', - defaultMessage: 'Profile URL already taken', - }, - orgWebsiteLabel: { - id: 'createOrg.form.websiteLabel', - defaultMessage: 'Organization website', - }, - orgDescriptionLabel: { - id: 'ExpenseForm.inviteeOrgDescriptionLabel', - defaultMessage: 'What does your organization do?', - }, - payoutOptionLabel: { - id: `ExpenseForm.PayoutOptionLabel`, - defaultMessage: 'Payout method', - }, - invoiceInfo: { - id: 'ExpenseForm.InvoiceInfo', - defaultMessage: 'Additional invoice information', - }, - invoiceInfoPlaceholder: { - id: 'ExpenseForm.InvoiceInfoPlaceholder', - defaultMessage: 'Tax ID, VAT number, etc. This information will be printed on your invoice.', - }, - country: { - id: 'ExpenseForm.ChooseCountry', - defaultMessage: 'Choose country', - }, - address: { - id: 'ExpenseForm.AddressLabel', - defaultMessage: 'Physical address', - }, -}); - -const PAYEE_TYPE = { - USER: 'USER', - ORG: 'ORG', -}; - -const Fieldset = styled.fieldset` - border: none; - padding: 0; - margin: 0; -`; - -const RadioOptionContainer = styled.label` - align-items: center; - display: flex; - flex: 1 1 50%; - font-size: 14px; - font-weight: normal; - line-height: 20px; - margin: 0px; - padding: 6px 16px; - cursor: pointer; - - &:not(:last-child) { - @media (max-width: ${themeGet('breakpoints.0')}) { - border-bottom: 1px solid #dcdee0; - } - @media (min-width: ${themeGet('breakpoints.0')}) { - border-right: 1px solid #dcdee0; - } - } -`; - -const throttledSearch = debounce((searchFunc, variables) => { - return searchFunc({ variables }); -}, 750); - -const ExpenseFormPayeeSignUpStep = ({ formik, collective, onCancel, onNext, expense }) => { - const intl = useIntl(); - const { formatMessage } = intl; - const { values, touched, errors } = formik; - const stepOneCompleted = - isEmpty(flattenObjectDeep(validatePayoutMethod(values.payoutMethod))) && - (values.type === expenseTypes.RECEIPT || - (values.payoutMethod && values.payeeLocation?.country && values.payeeLocation?.address)); - - const setPayoutMethod = React.useCallback(({ value }) => formik.setFieldValue('payoutMethod', value), []); - const [payeeType, setPayeeType] = React.useState(values.payee?.organization ? PAYEE_TYPE.ORG : PAYEE_TYPE.USER); - const [validateSlug, { data: existingSlugAccount }] = useLazyQuery(validateSlugQuery); - - const changePayeeType = e => { - e.stopPropagation(); - setPayeeType(e.target.value); - }; - - React.useEffect(() => { - if (values.payee?.organization?.name && !touched.payee?.organization?.slug) { - const slug = suggestSlug(values.payee.organization.name); - if (values.payee.organization.slug !== slug) { - formik.setFieldValue('payee.organization.slug', suggestSlug(values.payee.organization.name)); - } - } - }, [values.payee?.organization?.name]); - React.useEffect(() => { - if (payeeType === PAYEE_TYPE.USER) { - formik.setFieldValue('payee', omit(values.payee, ['organization'])); - } else if (payeeType === PAYEE_TYPE.ORG && values.draft?.payee?.organization) { - formik.setFieldValue('payee', { ...values.payee, organization: values.draft.payee.organization }); - } - }, [payeeType]); - // Slug Validation - React.useEffect(() => { - if (values.payee?.organization?.slug) { - throttledSearch(validateSlug, { slug: values.payee.organization.slug }); - } - }, [values.payee?.organization?.slug]); - - const handleSlugValidation = async value => { - if (value === existingSlugAccount?.account?.slug) { - return formatMessage(msg.orgSlugErrorTaken); - } - }; - - return ( - - - -
- - - - - - Personal Account - - - - - - Organization Account - - -
-
-
- - {payeeType === PAYEE_TYPE.ORG && ( - - - - {({ field }) => ( - - {inputProps => } - - )} - - - {({ field }) => ( - - {inputProps => } - - )} - - - {({ field }) => ( - - {inputProps => } - - )} - - - - {({ field }) => ( - - {inputProps => } - - )} - - - - )} - - - - - {({ field }) => ( - - {inputProps => } - - )} - - {payeeType === PAYEE_TYPE.ORG && ( - - - - )} - - - - {({ field }) => ( - - {inputProps => } - - )} - - - }} - /> - - - - - - {({ field }) => ( - - {inputProps => } - - )} - - - - - - - { - formik.setFieldValue('payeeLocation', values); - }} - location={values.payeeLocation} - errors={errors.payeeLocation} - required - /> - - - - - {({ field }) => ( - - {({ id, error }) => ( - - )} - - )} - - {values.payoutMethod && ( - - {({ field, meta }) => ( - - - - )} - - )} - - - - {({ field }) => ( - - {inputProps => ( - - )} - - )} - - - {values.payee && ( - - - - {onCancel && ( - { - onCancel?.(); - }} - > - - - )} - { - const allErrors = await formik.validateForm(); - const errors = omit(pick(allErrors, ['payee', 'payoutMethod', 'payeeLocation']), [ - 'payoutMethod.data.currency', - ]); - if (isEmpty(flattenObjectDeep(errors))) { - onNext?.(); - } else { - // We use set touched here to display errors on fields that are not dirty. - formik.setTouched(errors); - formik.setErrors(errors); - } - }} - > - -  → - - - - )} -
- ); -}; - -export default ExpenseFormPayeeSignUpStep; diff --git a/components/expenses/ExpenseFormPayeeStep.js b/components/expenses/ExpenseFormPayeeStep.js deleted file mode 100644 index 4a2317f32ea..00000000000 --- a/components/expenses/ExpenseFormPayeeStep.js +++ /dev/null @@ -1,593 +0,0 @@ -import React, { Fragment } from 'react'; -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 { compact, first, get, groupBy, isEmpty, omit, pick } from 'lodash'; -import { createPortal } from 'react-dom'; -import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; - -import { compareNames } from '../../lib/collective'; -import { AccountTypesWithHost, CollectiveType } from '../../lib/constants/collectives'; -import expenseTypes from '../../lib/constants/expenseTypes'; -import { PayoutMethodType } from '../../lib/constants/payout-method'; -import { EMPTY_ARRAY } from '../../lib/constants/utils'; -import { ERROR, isErrorType } from '../../lib/errors'; -import { formatFormErrorMessage } from '../../lib/form-utils'; -import { gql } from '../../lib/graphql/helpers'; -import { expenseFormPayeeStepCollectivePickerSearchQuery } from '../../lib/graphql/v1/queries'; -import { require2FAForAdmins } from '../../lib/policies'; -import { flattenObjectDeep } from '../../lib/utils'; -import { checkRequiresAddress } from './lib/utils'; - -import { CUSTOM_OPTIONS_POSITION, FLAG_COLLECTIVE_PICKER_COLLECTIVE } from '../CollectivePicker'; -import CollectivePickerAsync from '../CollectivePickerAsync'; -import { Box, Flex } from '../Grid'; -import Image from '../Image'; -import MessageBox from '../MessageBox'; -import StyledButton from '../StyledButton'; -import StyledHr from '../StyledHr'; -import StyledInput from '../StyledInput'; -import StyledInputField from '../StyledInputField'; -import StyledInputLocation from '../StyledInputLocation'; -import StyledTextarea from '../StyledTextarea'; -import StyledTooltip from '../StyledTooltip'; -import { Span } from '../Text'; -import { TwoFactorAuthRequiredMessage } from '../TwoFactorAuthRequiredMessage'; - -import PayoutMethodForm, { validatePayoutMethod } from './PayoutMethodForm'; -import PayoutMethodSelect from './PayoutMethodSelect'; - -const { INDIVIDUAL, ORGANIZATION, COLLECTIVE, FUND, EVENT, PROJECT, VENDOR } = CollectiveType; - -const msg = defineMessages({ - payeeLabel: { - id: `ExpenseForm.payeeLabel`, - defaultMessage: 'Who is being paid for this expense?', - }, - payoutOptionLabel: { - id: `ExpenseForm.PayoutOptionLabel`, - defaultMessage: 'Payout method', - }, - invoiceInfo: { - id: 'ExpenseForm.InvoiceInfo', - defaultMessage: 'Additional invoice information', - }, - invoiceInfoPlaceholder: { - id: 'ExpenseForm.InvoiceInfoPlaceholder', - defaultMessage: 'Tax ID, VAT number, etc. This information will be printed on your invoice.', - }, - country: { - id: 'ExpenseForm.ChooseCountry', - defaultMessage: 'Choose country', - }, - address: { - id: 'ExpenseForm.AddressLabel', - defaultMessage: 'Physical address', - }, - cancelEditExpense: { - id: 'ExpenseForm.CancelEditExpense', - defaultMessage: 'Cancel Edit', - }, - confirmCancelEditExpense: { - id: 'ExpenseForm.ConfirmCancelEditExpense', - defaultMessage: 'Are you sure you want to cancel the edits?', - }, - clearExpenseForm: { - id: 'ExpenseForm.ClearExpenseForm', - defaultMessage: 'Clear Form', - }, - confirmClearExpenseForm: { - id: 'ExpenseForm.ConfirmClearExpenseForm', - defaultMessage: 'Are you sure you want to clear the expense form?', - }, -}); - -const setLocationFromPayee = (formik, payee) => { - formik.setFieldValue('payeeLocation.country', payee?.location?.country || null); - formik.setFieldValue('payeeLocation.address', payee?.location?.address || ''); - formik.setFieldValue('payeeLocation.structured', payee?.location?.structured); -}; - -const getPayoutMethodsFromPayee = payee => { - const payoutMethods = (get(payee, 'payoutMethods') || EMPTY_ARRAY).filter(({ isSaved }) => isSaved); - - // If the Payee is active (can manage a budget and has a balance). This is usually: - // - a "Collective" family (Collective, Fund, Event, Project) with an host or Self Hosted - // - an "Host" Organization with budget activated (new default) - if (payee?.isActive) { - if (!payoutMethods.find(pm => pm.type === PayoutMethodType.ACCOUNT_BALANCE)) { - payoutMethods.push({ - id: 'new', - data: {}, - type: PayoutMethodType.ACCOUNT_BALANCE, - isSaved: true, - }); - } - } - - // If the Payee is in the "Collective" family (Collective, Fund, Event, Project) - // But not the Host itself (Self Hosted) - // Then we should add BANK_ACCOUNT and PAYPAL of the Host as an option - if (payee && AccountTypesWithHost.includes(payee.type) && payee.id !== payee.host?.id) { - const hostPayoutMethods = get(payee, 'host.payoutMethods') || EMPTY_ARRAY; - let hostSuitablePayoutMethods = hostPayoutMethods - .filter(payoutMethod => payoutMethod.type === PayoutMethodType.BANK_ACCOUNT) - .filter( - payoutMethod => - !payoutMethod.name || - payoutMethod.name.includes('Collectives account') || - payoutMethod.name.includes('Main account'), - ); - if (hostSuitablePayoutMethods.length === 0) { - hostSuitablePayoutMethods = hostPayoutMethods.filter( - payoutMethod => payoutMethod.type === PayoutMethodType.PAYPAL, - ); - } - payoutMethods.push(...hostSuitablePayoutMethods.map(payoutMethod => ({ ...payoutMethod, isDeletable: false }))); - } - - return payoutMethods.length > 0 ? payoutMethods : EMPTY_ARRAY; -}; - -const refreshPayoutProfile = (formik, payoutProfiles) => { - const payee = formik.values.payee - ? payoutProfiles.find(profile => profile.id === formik.values.payee.id) - : first(payoutProfiles); - - formik.setValues({ ...formik.values, draft: omit(formik.values.draft, ['payee']), payee }); -}; - -const sortProfiles = profiles => { - return profiles?.sort((a, b) => a.slug.localeCompare(b.slug)) || []; -}; - -const getPayeeOptions = (intl, payoutProfiles) => { - const profilesByType = groupBy(payoutProfiles, p => p.type); - const getOption = profile => ({ value: profile, label: profile.name, [FLAG_COLLECTIVE_PICKER_COLLECTIVE]: true }); - const getProfileOptions = type => sortProfiles(profilesByType[type]).map(getOption); - - const payeeOptions = [ - { - label: intl.formatMessage({ defaultMessage: 'Myself', id: 'YjO/0+' }), - options: getProfileOptions(INDIVIDUAL), - }, - { - label: intl.formatMessage({ defaultMessage: 'Vendors', id: 'RilevA' }), - options: getProfileOptions(VENDOR), - }, - { - label: intl.formatMessage({ id: 'organization', defaultMessage: 'My Organizations' }), - options: getProfileOptions(ORGANIZATION), - }, - ]; - - if (profilesByType[COLLECTIVE]?.length) { - payeeOptions.push({ - options: getProfileOptions(COLLECTIVE), - label: intl.formatMessage({ id: 'collective', defaultMessage: 'My Collectives' }), - }); - } - if (profilesByType[FUND]?.length) { - payeeOptions.push({ - options: getProfileOptions(FUND), - label: intl.formatMessage({ id: 'funds', defaultMessage: 'My Funds' }), - }); - } - if (profilesByType[PROJECT]?.length) { - payeeOptions.push({ - options: getProfileOptions(PROJECT), - label: intl.formatMessage({ defaultMessage: 'My Projects', id: 'FVO2wx' }), - }); - } - if (profilesByType[EVENT]?.length) { - payeeOptions.push({ - options: getProfileOptions(EVENT), - label: intl.formatMessage({ id: 'events', defaultMessage: 'My Events' }), - }); - } - - return payeeOptions; -}; - -const hostVendorsQuery = gql` - query HostVendors($hostId: String!, $collectiveSlug: String!) { - host(id: $hostId, throwIfMissing: false) { - id - slug - legacyId - vendors(forAccount: { slug: $collectiveSlug }, limit: 5) { - nodes { - id - slug - name - type - description - imageUrl(height: 64) - hasPayoutMethod - payoutMethods { - id - type - name - data - isSaved - } - } - } - } - } -`; - -export const checkStepOneCompleted = (values, isOnBehalf, isMissing2FA, canEditPayoutMethod) => { - if (isMissing2FA) { - return false; - } else if (isOnBehalf || values.payee?.type === VENDOR) { - return Boolean(values.payee); - } else if (canEditPayoutMethod) { - if (!isEmpty(flattenObjectDeep(validatePayoutMethod(values.payoutMethod)))) { - return false; // There are some errors in the form - } else if (checkRequiresAddress(values)) { - // Require an address for non-receipt expenses - return Boolean(values.payoutMethod && values.payeeLocation?.country && values.payeeLocation?.address); - } - } - - return true; -}; - -const ExpenseFormPayeeStep = ({ - formik, - payoutProfiles, - collective, - onCancel, - onNext, - onInvite, - onChange, - isOnBehalf, - canEditPayoutMethod, - loggedInAccount, - editingExpense, - handleClearPayeeStep, - drawerActionsContainer, - disablePayee, -}) => { - const intl = useIntl(); - const { formatMessage } = intl; - const { values, errors } = formik; - const { data, loading } = useQuery(hostVendorsQuery, { - variables: { hostId: collective.host?.id, collectiveSlug: collective.slug }, - skip: !collective.host?.id, - }); - const isMissing2FA = require2FAForAdmins(values.payee) && !loggedInAccount?.hasTwoFactorAuth; - const stepOneCompleted = checkStepOneCompleted(values, isOnBehalf, isMissing2FA, canEditPayoutMethod); - const allPayoutMethods = React.useMemo( - () => getPayoutMethodsFromPayee(values.payee), - [values.payee, loggedInAccount], - ); - - const onPayoutMethodRemove = React.useCallback(() => refreshPayoutProfile(formik, payoutProfiles), [payoutProfiles]); - const setPayoutMethod = React.useCallback(({ value }) => formik.setFieldValue('payoutMethod', value), []); - - const vendors = get(data, 'host.vendors.nodes', []).filter(v => v.hasPayoutMethod); - const payeeOptions = React.useMemo( - () => getPayeeOptions(intl, [...payoutProfiles, ...vendors]), - [payoutProfiles, vendors], - ); - const requiresAddress = checkRequiresAddress(values); - const requiresPayoutMethod = !isOnBehalf && values.payee?.type !== VENDOR; - - const actionButtons = ( - - {onCancel && ( - { - onCancel?.(); - }} - > - - - )} - { - const allErrors = await formik.validateForm(); - // Get the relevant errors for the payee step, ignores data.currency in the because it is related to expense amount. - const errors = omit(pick(allErrors, ['payee', 'payoutMethod', 'payeeLocation']), [ - 'payoutMethod.data.currency', - ]); - if (isEmpty(flattenObjectDeep(errors))) { - onNext?.(formik.values); - } else { - // We use set touched here to display errors on fields that are not dirty. - // eslint-disable-next-line no-console - console.log('ExpenseFormPayeeStep > Validation failed', errors); - formik.setTouched(errors); - formik.setErrors(errors); - } - }} - > - -  → - - - - - {formatMessage(editingExpense ? msg.cancelEditExpense : msg.clearExpenseForm)} - - - ); - - return ( - - - - - {({ field }) => ( - - {({ id }) => ( - { - 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} - vendorVisibleToAccountIds={[collective.legacyId]} - 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} - /> - )} - - )} - - {!isMissing2FA && ( - - {values.payee?.legalName && ( - - {({ field }) => ( - - -   - ( - - )} - > - - - - } - labelFontSize="13px" - flex="1" - mt={3} - > - - {values.payoutMethod?.data?.accountHolderName && - values.payee.legalName && - !compareNames(values.payoutMethod.data.accountHolderName, values.payee.legalName) && ( - - - - )} - - )} - - )} - {requiresAddress && ( - - { - formik.setFieldValue('payeeLocation', values); - }} - location={values.payeeLocation} - errors={errors.payeeLocation} - /> - - )} - {values.type === expenseTypes.INVOICE && ( - - {({ field }) => ( - - {inputProps => ( - - )} - - )} - - )} - - )} - - {requiresPayoutMethod && ( - - {canEditPayoutMethod ? ( - - - {({ field }) => ( - - {({ id, error }) => ( - - )} - - )} - - - {values.payoutMethod && ( - - {({ field, meta }) => ( - - - - )} - - )} - - ) : ( -
-

- -

- - -
- -
-
-

- -

-

- -

-
-
-
-
- )} -
- )} -
- - {isMissing2FA && } - - {values.payee && - !isMissing2FA && - (drawerActionsContainer ? ( - createPortal(actionButtons, drawerActionsContainer) - ) : ( - - - {actionButtons} - - ))} -
- ); -}; - -export default ExpenseFormPayeeStep; diff --git a/components/expenses/ExpenseInviteNotificationBanner.tsx b/components/expenses/ExpenseInviteNotificationBanner.tsx index 85957839b34..cd6916884aa 100644 --- a/components/expenses/ExpenseInviteNotificationBanner.tsx +++ b/components/expenses/ExpenseInviteNotificationBanner.tsx @@ -103,30 +103,16 @@ const ExpenseInviteNotificationBanner = props => {

- {props.createdUser ? ( - - ) : ( - - )} +

- {props.createdUser ? ( - - ) : ( - - )} +

{canResendEmail && ( diff --git a/components/expenses/ExpenseItemForm.js b/components/expenses/ExpenseItemForm.js deleted file mode 100644 index 62cdd4329d7..00000000000 --- a/components/expenses/ExpenseItemForm.js +++ /dev/null @@ -1,590 +0,0 @@ -import React from 'react'; -import { gql, useQuery } from '@apollo/client'; -import dayjs from 'dayjs'; -import { Field, useFormikContext } from 'formik'; -import { escape, get, isEmpty, omit, pick, unescape } from 'lodash'; -import Lottie from 'lottie-react'; -import { AlertTriangle } from 'lucide-react'; -import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; - -import expenseTypes from '../../lib/constants/expenseTypes'; -import { formatCurrency, formatValueAsCurrency, getDefaultCurrencyPrecision } from '../../lib/currency-utils'; -import { createError, ERROR } from '../../lib/errors'; -import { standardizeExpenseItemIncurredAt } from '../../lib/expenses'; -import { formatFormErrorMessage, requireFields } from '../../lib/form-utils'; -import { cn, isValidUrl } from '../../lib/utils'; -import { attachmentDropzoneParams } from './lib/attachments'; -import { expenseItemsMustHaveFiles } from './lib/items'; -import { updateExpenseFormWithUploadResult } from './lib/ocr'; -import { FX_RATE_ERROR_THRESHOLD, getExpenseExchangeRateWarningOrError } from './lib/utils'; - -import * as ScanningAnimationJSON from '../../public/static/animations/scanning.json'; -import Container from '../Container'; -import Dropzone from '../Dropzone'; -import { ExchangeRate } from '../ExchangeRate'; -import { Box, Flex } from '../Grid'; -import PrivateInfoIcon from '../icons/PrivateInfoIcon'; -import RichTextEditor from '../RichTextEditor'; -import StyledButton from '../StyledButton'; -import StyledHr from '../StyledHr'; -import StyledInput from '../StyledInput'; -import StyledInputAmount from '../StyledInputAmount'; -import StyledInputField from '../StyledInputField'; -import { P, Span } from '../Text'; -import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/Tooltip'; - -import { ExpenseAccountingCategoryPill } from './ExpenseAccountingCategoryPill'; -import { ExpenseItemDescriptionHint } from './ItemDescriptionHint'; - -const msg = defineMessages({ - previewImgAlt: { - id: 'ExpenseReceiptImagePreview.Alt', - defaultMessage: 'Expense receipt preview', - }, - descriptionLabel: { - id: 'Fields.description', - defaultMessage: 'Description', - }, - grossAmountLabel: { - id: 'bwZInO', - defaultMessage: 'Gross Amount', - }, - amountLabel: { - id: 'Fields.amount', - defaultMessage: 'Amount', - }, - dateLabel: { - id: 'expense.incurredAt', - defaultMessage: 'Date', - }, - removeReceipt: { - id: 'expense.RemoveReceipt', - defaultMessage: 'Remove receipt', - }, - removeItem: { - id: 'expense.RemoveItem', - defaultMessage: 'Remove item', - }, - receiptRequired: { - id: 'expense.ReceiptRequired', - defaultMessage: 'Receipt required', - }, -}); - -/** Validates a single expense item, one field at a time (doesn't return multiple errors) */ -export const validateExpenseItem = (expense, item) => { - const requiredFields = ['description']; - if (expense.type !== expenseTypes.GRANT) { - requiredFields.push('incurredAt'); - } - const errors = requireFields(item, requiredFields); - - if (!item.amountV2?.valueInCents) { - errors.amountV2 = createError(ERROR.FORM_FIELD_REQUIRED); - } else if (isNaN(item.amountV2.valueInCents)) { - errors.amountV2 = createError(ERROR.FORM_FIELD_PATTERN); - } - - if (!isEmpty(errors)) { - return errors; - } - - // Attachment URL - if (expenseItemsMustHaveFiles(expense.type)) { - if (!item.url) { - errors.url = createError(ERROR.FORM_FIELD_REQUIRED); - } else if (!isValidUrl(item.url)) { - errors.url = createError(ERROR.FORM_FIELD_PATTERN); - } else if (item.__isUploading) { - errors.url = createError(ERROR.FORM_FILE_UPLOADING); - } - } - - // Show the expense currency errors on the amount field, since it's displayed next to it - if (!expense.currency) { - errors.amountV2 = createError(ERROR.FORM_FIELD_REQUIRED); - } - - return errors; -}; - -export const prepareExpenseItemForSubmit = (expenseData, item) => { - // The frontend currently ignores the time part of the date, we default to midnight UTC - const incurredAtFullDate = item.incurredAt || new Date().toISOString().split('T')[0]; - const incurredAt = standardizeExpenseItemIncurredAt(incurredAtFullDate); - return { - id: item.__isNew ? undefined : item.id, // Omit item's ids that were created for keying purposes - incurredAt, - description: item.description, - url: expenseItemsMustHaveFiles(expenseData.type) ? item.url : null, // never submit URLs for invoices or requests - amountV2: { - ...pick(item.amountV2, ['valueInCents', 'currency']), - exchangeRate: item.amountV2.exchangeRate && { - ...omit(item.amountV2.exchangeRate, ['__typename', 'isApproximate']), - date: item.amountV2.exchangeRate.date || incurredAt, - }, - }, - }; -}; - -const AttachmentLabel = () => ( - - -    - - -); - -const WithOCRComparisonWarning = ({ comparison, formatValue, children, mrClass = 'mr-10' }) => ( -
- {children} - {Boolean(comparison?.hasMismatch) && ( -
- - - - - - {comparison.hasCurrencyMismatch ? ( - - ) : comparison.hasAmountMismatch ? ( - - ) : ( - - )} - - -
- )} -
-); - -const currencyExchangeRateQuery = gql` - query ExpenseFormCurrencyExchangeRate($requests: [CurrencyExchangeRateRequest!]!) { - currencyExchangeRate(requests: $requests) { - value - source - fromCurrency - toCurrency - date - isApproximate - } - } -`; - -const getDefaultExchangeRate = (itemCurrency, expenseCurrency) => ({ - value: null, - source: 'USER', - fromCurrency: itemCurrency, - toCurrency: expenseCurrency, - date: null, -}); - -const isValidExchangeRate = (exchangeRate, itemCurrency, expenseCurrency) => { - return Boolean( - exchangeRate && - exchangeRate.source === 'USER' && - exchangeRate.fromCurrency === itemCurrency && - exchangeRate.toCurrency === expenseCurrency && - exchangeRate.value, - ); -}; - -const extractFormValues = (form, itemPath) => ({ - expenseCurrency: get(form.values, 'currency'), - item: get(form.values, itemPath) || {}, -}); - -/** - * A hook that queries the exchange rate if needed and updates the form with the result. - */ -const useExpenseItemExchangeRate = (form, itemPath) => { - // We use a ref as `useQuery` callbacks are not guaranteed to be called with the latest values - const formValues = React.useRef(extractFormValues(form, itemPath)); - React.useEffect(() => { - formValues.current = extractFormValues(form, itemPath); - }); - - // Do not query exchange rate... - const shouldSkipExchangeRateQuery = () => { - const { expenseCurrency, item } = formValues.current; - const itemCurrency = get(item, 'amountV2.currency') || expenseCurrency; - // ...if expense currency is not set or if item currency is the same as expense currency - if (!expenseCurrency || !itemCurrency || expenseCurrency === itemCurrency) { - return true; - } - - // ...if we already have a valid exchange rate from Open Collective - const existingExchangeRate = get(item, 'amountV2.exchangeRate'); - return Boolean( - existingExchangeRate && - existingExchangeRate.source === 'OPENCOLLECTIVE' && - existingExchangeRate.fromCurrency === itemCurrency && - existingExchangeRate.toCurrency === expenseCurrency && - existingExchangeRate.value && - dayjs(existingExchangeRate?.date).isSame(dayjs(standardizeExpenseItemIncurredAt(get(item, 'incurredAt')))), - ); - }; - - // If the item exchange rate isn't valid anymore, let's make sure we invalidate it - React.useEffect(() => { - const newItemValues = {}; - const { expenseCurrency, item } = formValues.current; - const existingExchangeRate = get(item, 'amountV2.exchangeRate'); - if (existingExchangeRate && existingExchangeRate.toCurrency !== expenseCurrency) { - newItemValues.amountV2 = { ...item.amountV2, exchangeRate: null }; - } - if (item.referenceExchangeRate && item.referenceExchangeRate.toCurrency !== expenseCurrency) { - newItemValues.referenceExchangeRate = null; - } - - if (!isEmpty(newItemValues)) { - form.setFieldValue(itemPath, { ...item, ...newItemValues }); - } - }, [form, itemPath]); - - const { loading } = useQuery(currencyExchangeRateQuery, { - skip: shouldSkipExchangeRateQuery(), - - variables: { - requests: [ - { - fromCurrency: formValues.current.item.amountV2?.currency, - toCurrency: formValues.current.expenseCurrency, - date: standardizeExpenseItemIncurredAt(formValues.current.item.incurredAt), - }, - ], - }, - onCompleted: data => { - // Re-check condition in case it changed since triggering the query - const { expenseCurrency, item } = formValues.current; - const itemCurrency = get(item, 'amountV2.currency') || expenseCurrency; - const existingExchangeRate = get(item, 'amountV2.exchangeRate'); - if (!shouldSkipExchangeRateQuery() && !isValidExchangeRate(existingExchangeRate, itemCurrency, expenseCurrency)) { - const exchangeRate = get(data, 'currencyExchangeRate[0]'); - if (exchangeRate && exchangeRate.fromCurrency === itemCurrency && exchangeRate.toCurrency === expenseCurrency) { - form.setFieldValue(itemPath, { - ...item, - amountV2: { ...item.amountV2, exchangeRate }, - referenceExchangeRate: exchangeRate, - }); - } else { - // If we're not able to find an exchange rate, we'll ask the user to provide one manually - form.setFieldValue(itemPath, { - ...item, - amountV2: { ...item.amountV2, exchangeRate: getDefaultExchangeRate(itemCurrency, expenseCurrency) }, - referenceExchangeRate: null, - }); - } - } - }, - onError: () => { - // If the API fails (e.g. network error), we'll ask the user to provide an exchange rate manually - const { expenseCurrency, item } = formValues.current; - form.setFieldValue(`${itemPath}.amountV2.exchangeRate`, getDefaultExchangeRate(item.currency, expenseCurrency)); - }, - }); - - // Not returning data as we don't want to encourage using it directly (values are set directly in the form) - return { loading }; -}; - -const UploadAnimation = () => ; - -/** - * Form for a single attachment. Must be used with Formik. - */ -const ExpenseItemForm = ({ - collective, - attachment, - errors, - onRemove, - onUploadError, - requireFile, - requireDate, - isRichText, - itemIdx, - isOptional = false, - editOnlyDescriptiveInfo, - isInvoice, - hasOCRFeature, - ocrComparison, - hasCurrencyPicker, - amountIsLocked, - isSubjectToTax = false, -}) => { - const intl = useIntl(); - const form = useFormikContext(); - const { formatMessage } = intl; - const attachmentKey = `attachment-${attachment.id || attachment.url}`; - const itemPath = `items[${itemIdx}]`; - const getFieldName = field => `${itemPath}.${field}`; - const getError = field => formatFormErrorMessage(intl, get(errors, getFieldName(field))); - const isLoading = Boolean(attachment.__isUploading); - const hasAccountingCategory = Boolean(form.values.accountingCategory); - const expenseCurrency = get(form.values, 'currency'); - const itemCurrency = get(form.values, getFieldName('amountV2.currency')) || expenseCurrency; - const { loading: loadingExchangeRate } = useExpenseItemExchangeRate(form, itemPath); - const exchangeRate = get(form.values, `${itemPath}.amountV2.exchangeRate`); - const referenceExchangeRate = get(form.values, `${itemPath}.referenceExchangeRate`); - - // Store a ref to the form to make sure we can always access the latest values in async callbacks - const formRef = React.useRef(form); - formRef.current = form; - - return ( - - - {requireFile && ( - - {({ field, meta }) => { - const hasValidUrl = field.value && isValidUrl(field.value); - return ( - } - data-cy="attachment-url-field" - required={!isOptional} - error={meta.error?.type !== ERROR.FORM_FIELD_REQUIRED && formatFormErrorMessage(intl, meta.error)} - > - - formRef.current.setFieldValue(itemPath, { ...attachment, url, __isUploading: false }) - } - mockImageGenerator={() => `https://loremflickr.com/120/120/invoice?lock=${attachmentKey}`} - className="size-20 sm:size-28" - value={hasValidUrl && field.value} - onReject={(...args) => { - formRef.current.setFieldValue(itemPath, { ...attachment, __isUploading: false }); - onUploadError(...args); - }} - useGraphQL={hasOCRFeature} - parseDocument={hasOCRFeature} - parsingOptions={{ currency: form.values.currency }} - onGraphQLSuccess={uploadResults => { - updateExpenseFormWithUploadResult(collective, formRef.current, uploadResults, [itemIdx]); - }} - isLoading={isLoading} - UploadingComponent={UploadAnimation} - onDrop={() => form.setFieldValue(itemPath, { ...attachment, __isUploading: true })} - /> - - ); - }} - - )} - - - {({ field, form }) => ( - } - htmlFor={`${attachmentKey}-description`} - label={formatMessage(msg.descriptionLabel)} - labelFontSize="13px" - required={!isOptional} - > - {inputProps => - isRichText ? ( - - ) : ( - form.setFieldValue(field.name, escape(e.target.value))} - placeholder={get(attachment, '__file.name') || get(attachment, '__file.path')} - /> - ) - } - - )} - - - {requireDate && ( - - {inputProps => ( - - {({ field }) => ( - - - - )} - - )} - - )} -
- - {inputProps => ( - - {({ field, form: { setFieldValue } }) => ( - - `${amount?.currency} ${formatValueAsCurrency(amount, { locale: intl.locale })}` - } - > - { - setFieldValue(field.name, { - ...field.value, - exchangeRate, - }); - }} - onChange={valueInCents => { - setFieldValue(field.name, { - ...field.value, - valueInCents, - currency: itemCurrency, // Make sure we encode the currency here (it case it was defaulted from the expense currency) - }); - }} - onCurrencyChange={currency => { - const exchangeRate = field.value?.exchangeRate; - setFieldValue(field.name, { - ...field.value, - exchangeRate: exchangeRate?.fromCurrency === currency ? field.value.exchangeRate : null, // Drop exchange rate when switching currency - currency, - }); - }} - /> - - )} - - )} - - {Boolean(itemCurrency && expenseCurrency !== itemCurrency) && ( - - )} -
- {hasAccountingCategory && ( - -

- -

-
- -
-
- )} -
-
-
- - {onRemove && !editOnlyDescriptiveInfo && !amountIsLocked && ( - onRemove(attachment)} - > - {formatMessage(requireFile ? msg.removeReceipt : msg.removeItem)} - - )} - - -
- ); -}; - -export default React.memo(ExpenseItemForm); diff --git a/components/expenses/ExpenseMissingReceiptNotificationBanner.js b/components/expenses/ExpenseMissingReceiptNotificationBanner.tsx similarity index 54% rename from components/expenses/ExpenseMissingReceiptNotificationBanner.js rename to components/expenses/ExpenseMissingReceiptNotificationBanner.tsx index 748613698b2..d8612e942a5 100644 --- a/components/expenses/ExpenseMissingReceiptNotificationBanner.js +++ b/components/expenses/ExpenseMissingReceiptNotificationBanner.tsx @@ -1,62 +1,46 @@ import React from 'react'; import { FormattedMessage, useIntl } from 'react-intl'; -import useLoggedInUser from '@/lib/hooks/useLoggedInUser'; -import { PREVIEW_FEATURE_KEYS } from '@/lib/preview-features'; - -import { Flex } from '../../components/Grid'; -import MessageBox from '../../components/MessageBox'; -import StyledButton from '../../components/StyledButton'; -import { H4, P } from '../../components/Text'; - +import MessageBox from '../MessageBox'; import { Button } from '../ui/Button'; import EditExpenseDialog from './EditExpenseDialog'; const ExpenseMissingReceiptNotificationBanner = props => { const intl = useIntl(); - const { LoggedInUser } = useLoggedInUser(); - - const { canAttachReceipts } = LoggedInUser?.hasPreviewFeatureEnabled(PREVIEW_FEATURE_KEYS.INLINE_EDIT_EXPENSE) - ? props.expense?.permissions || {} - : {}; + const { canAttachReceipts } = props.expense?.permissions || {}; return ( - - -

+
+
+

-

-

+

+

-

-
- {canAttachReceipts ? ( +

+ {canAttachReceipts && ( +
} /> - ) : ( - props.onEdit && ( - - - - ) - )} -
- - +
+ )} + +
); }; diff --git a/components/expenses/ExpenseMoreActionsButton.js b/components/expenses/ExpenseMoreActionsButton.js index 725cf2040e2..2efdd9bda9e 100644 --- a/components/expenses/ExpenseMoreActionsButton.js +++ b/components/expenses/ExpenseMoreActionsButton.js @@ -4,7 +4,6 @@ import React from 'react'; import { Check } from '@styled-icons/feather/Check'; import { ChevronDown } from '@styled-icons/feather/ChevronDown/ChevronDown'; import { Download as IconDownload } from '@styled-icons/feather/Download'; -import { Edit as IconEdit } from '@styled-icons/feather/Edit'; import { Flag as FlagIcon } from '@styled-icons/feather/Flag'; import { Link as IconLink } from '@styled-icons/feather/Link'; import { MinusCircle } from '@styled-icons/feather/MinusCircle'; @@ -24,7 +23,6 @@ import { ExpenseStatus, ExpenseType } from '../../lib/graphql/types/v2/graphql'; import useClipboard from '../../lib/hooks/useClipboard'; import useKeyboardKey, { H, I } from '../../lib/hooks/useKeyboardKey'; import useLoggedInUser from '../../lib/hooks/useLoggedInUser'; -import { PREVIEW_FEATURE_KEYS } from '../../lib/preview-features'; import { getCollectivePageCanonicalURL, getCollectivePageRoute, getDashboardRoute } from '../../lib/url-helpers'; import { DashboardContext } from '../dashboard/DashboardContext'; @@ -108,7 +106,6 @@ export const shouldShowDuplicateExpenseButton = (LoggedInUser, expense) => { const ExpenseMoreActionsButton = ({ expense, onError, - onEdit, isDisabled, linkAction = 'copy', onModalToggle, @@ -155,9 +152,6 @@ const ExpenseMoreActionsButton = ({ }); const { LoggedInUser } = useLoggedInUser(); - const hasNewSubmitExpenseFlow = - LoggedInUser?.hasPreviewFeatureEnabled(PREVIEW_FEATURE_KEYS.NEW_EXPENSE_FLOW) || router.query.newExpenseFlowEnabled; - const showDeleteConfirmMoreActions = isOpen => { setDeleteConfirm(isOpen); onModalToggle?.(isOpen); @@ -290,12 +284,6 @@ const ExpenseMoreActionsButton = ({ )} - {onEdit && permissions.canEdit && ( - - - - - )} {!props.hasAttachedInvoiceFile && permissions.canSeeInvoiceInfo && [expenseTypes.INVOICE, expenseTypes.SETTLEMENT, expenseTypes.PLATFORM_BILLING].includes( @@ -355,7 +343,7 @@ const ExpenseMoreActionsButton = ({ )} - {hasNewSubmitExpenseFlow && shouldShowDuplicateExpenseButton(LoggedInUser, expense) && ( + {shouldShowDuplicateExpenseButton(LoggedInUser, expense) && ( { setDuplicateExpenseId(expense.legacyId); diff --git a/components/expenses/ExpenseNotesForm.js b/components/expenses/ExpenseNotesForm.js deleted file mode 100644 index 8c360cd2fa7..00000000000 --- a/components/expenses/ExpenseNotesForm.js +++ /dev/null @@ -1,63 +0,0 @@ -import React from 'react'; -import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; - -import { Box } from '../Grid'; -import PrivateInfoIcon from '../icons/PrivateInfoIcon'; -import RichTextEditor from '../RichTextEditor'; -import StyledInputField from '../StyledInputField'; -import { Span } from '../Text'; - -const msg = defineMessages({ - notesPlaceholder: { - id: 'ExpenseSummary.addNotesLabel', - defaultMessage: 'Add notes', - }, -}); - -const PrivateNoteLabel = () => { - return ( - - -    - - - - - ); -}; - -const ExpenseNotesForm = ({ onChange, disabled = false, defaultValue, hideLabel = false }) => { - const { formatMessage } = useIntl(); - return ( - } - labelProps={{ fontWeight: '500', fontSize: '13px' }} - > - {inputProps => ( - - - - )} - - ); -}; - -export default ExpenseNotesForm; diff --git a/components/expenses/ExpenseRecurringBanner.js b/components/expenses/ExpenseRecurringBanner.js deleted file mode 100644 index 101276b59ad..00000000000 --- a/components/expenses/ExpenseRecurringBanner.js +++ /dev/null @@ -1,155 +0,0 @@ -import React from 'react'; -import { useMutation } from '@apollo/client'; -import { pick } from 'lodash'; -import { useRouter } from 'next/router'; -import { FormattedMessage, useIntl } from 'react-intl'; - -import { getDateFromValue, toIsoDateStr } from '../../lib/date-utils'; -import { i18nGraphqlException } from '../../lib/errors'; -import { gql } from '../../lib/graphql/helpers'; -import { RecurringExpenseIntervals, RecurringIntervalOptions } from '../../lib/i18n/expense'; -import { getCollectivePageRoute } from '../../lib/url-helpers'; - -import Container from '../Container'; -import { Box, Flex } from '../Grid'; -import MessageBox from '../MessageBox'; -import StyledButton from '../StyledButton'; -import StyledInput from '../StyledInput'; -import StyledLink from '../StyledLink'; -import StyledModal, { ModalBody, ModalFooter, ModalHeader } from '../StyledModal'; -import StyledSelect from '../StyledSelect'; -import { P } from '../Text'; -import { useToast } from '../ui/useToast'; - -const deleteExpenseMutation = gql` - mutation DeleteExpense($expense: ExpenseReferenceInput!) { - deleteExpense(expense: $expense) { - id - } - } -`; - -const ExpenseRecurringEditModal = ({ onClose, expense }) => { - const { recurringExpense } = expense; - const [deleteExpense, { loading }] = useMutation(deleteExpenseMutation); - const { toast } = useToast(); - const intl = useIntl(); - const router = useRouter(); - - const handleDeletion = async () => { - try { - await deleteExpense({ variables: { expense: pick(expense, ['id']) } }); - toast({ - variant: 'success', - message: intl.formatMessage({ defaultMessage: 'Expense deleted', id: 'KYXMJ6' }), - }); - router.push(getCollectivePageRoute(expense.account)); - onClose(); - } catch (e) { - toast({ variant: 'error', message: i18nGraphqlException(intl, e) }); - } - }; - - return ( - - - - - - -

- -

- -

- -

- option.value === recurringExpense.interval)} - options={RecurringIntervalOptions} - disabled - /> -
- -

- -

- -
-
-
- - - - - - - -
- ); -}; - -const ExpenseRecurringBanner = ({ expense }) => { - const { recurringExpense } = expense; - const [isEditModalOpen, setEditModal] = React.useState(false); - - return ( - - -

- -

-

- -

-

- ({RecurringExpenseIntervals[recurringExpense.interval]} - {recurringExpense.endsAt && ( - - ,  - - - )} - )   - setEditModal(true)}> - - -

-
- {isEditModalOpen && setEditModal(false)} expense={expense} />} -
- ); -}; - -export default ExpenseRecurringBanner; diff --git a/components/expenses/ExpenseRecurringForm.js b/components/expenses/ExpenseRecurringForm.js deleted file mode 100644 index 4361c1530fc..00000000000 --- a/components/expenses/ExpenseRecurringForm.js +++ /dev/null @@ -1,103 +0,0 @@ -import React from 'react'; -import { FormattedMessage } from 'react-intl'; - -import { getDateFromValue, toIsoDateStr } from '../../lib/date-utils'; -import { RecurringIntervalOptions } from '../../lib/i18n/expense'; - -import { Box, Flex } from '../Grid'; -import StyledCheckbox from '../StyledCheckbox'; -import StyledHr from '../StyledHr'; -import StyledInput from '../StyledInput'; -import StyledInputField from '../StyledInputField'; -import StyledSelect from '../StyledSelect'; -import { P, Span } from '../Text'; - -const ExpenseRecurringForm = ({ recurring, onChange }) => { - const [isRecurring, setRecurring] = React.useState(!!recurring); - - const handleSetRecurring = isRecurring => { - if (!isRecurring) { - onChange(null); - } - setRecurring(isRecurring); - }; - - return ( - - - -

- -

- - - - - - } - size="13px" - fontSize="13px" - checked={isRecurring} - onChange={({ checked }) => handleSetRecurring(checked)} - /> - - - {isRecurring && ( - - - } - labelFontSize="13px" - labelFontWeight={600} - required - > - {inputProps => ( - onChange({ ...recurring, interval })} - menuPlacement="auto" - value={RecurringIntervalOptions.find(i => i.value === recurring?.interval)} - isSearchable={false} - /> - )} - - - - } - labelFontSize="13px" - labelFontWeight={600} - required={false} - > - {inputProps => ( - onChange({ ...recurring, endsAt: getDateFromValue(event.target.value) })} - menuPlacement="auto" - height="38px" - width="100%" - value={recurring?.endsAt && toIsoDateStr(recurring.endsAt)} - min={toIsoDateStr(new Date())} - /> - )} - - - - )} -
- ); -}; - -export default ExpenseRecurringForm; diff --git a/components/expenses/ExpenseSummary.js b/components/expenses/ExpenseSummary.js index c92449382c9..5a344d6c3d6 100644 --- a/components/expenses/ExpenseSummary.js +++ b/components/expenses/ExpenseSummary.js @@ -1,18 +1,14 @@ import React, { Fragment } from 'react'; -import { themeGet } from '@styled-system/theme-get'; import { includes } from 'lodash'; import { Download, MessageSquare } from 'lucide-react'; -import { useRouter } from 'next/router'; import { createPortal } from 'react-dom'; import { FormattedDate, FormattedMessage, useIntl } from 'react-intl'; -import { styled } from 'styled-components'; import expenseTypes from '../../lib/constants/expenseTypes'; import { i18nGraphqlException } from '../../lib/errors'; import { ExpenseStatus, ExpenseType } from '../../lib/graphql/types/v2/graphql'; import useLoggedInUser from '../../lib/hooks/useLoggedInUser'; -import { PREVIEW_FEATURE_KEYS } from '../../lib/preview-features'; -import { cn, parseToBoolean } from '../../lib/utils'; +import { cn } from '../../lib/utils'; import { shouldDisplayExpenseCategoryPill } from './lib/accounting-categories'; import { expenseTypeSupportsAttachments } from './lib/attachments'; import { expenseItemsMustHaveFiles, getExpenseItemAmountV2FromNewAttrs } from './lib/items'; @@ -32,7 +28,7 @@ import LoadingPlaceholder from '../LoadingPlaceholder'; import StyledCard from '../StyledCard'; import StyledHr from '../StyledHr'; import Tags from '../Tags'; -import { H1, P, Span } from '../Text'; +import { P, Span } from '../Text'; import TruncatedTextWithTooltip from '../TruncatedTextWithTooltip'; import { Button } from '../ui/Button'; import { useToast } from '../ui/useToast'; @@ -49,17 +45,6 @@ import ExpenseSummaryAdditionalInformation from './ExpenseSummaryAdditionalInfor import ExpenseTypeTag from './ExpenseTypeTag'; import ProcessExpenseButtons, { hasProcessButtons } from './ProcessExpenseButtons'; -export const SummaryHeader = styled(H1)` - > a { - color: inherit; - text-decoration: underline; - - &:hover { - color: ${themeGet('colors.black.600')}; - } - } -`; - const CreatedByUserLink = ({ account }) => { return ( @@ -93,13 +78,11 @@ const ExpenseSummary = ({ host, isLoading, isLoadingLoggedInUser, - isEditing, borderless = undefined, canEditTags, showProcessButtons, onClose = undefined, onDelete, - onEdit, drawerActionsContainer, openFileViewer, enableKeyboardShortcuts, @@ -109,7 +92,6 @@ const ExpenseSummary = ({ }) => { const intl = useIntl(); const { toast } = useToast(); - const router = useRouter(); const isReceipt = expense?.type === expenseTypes.RECEIPT; const isCreditCardCharge = expense?.type === expenseTypes.CHARGE; const isGrant = expense?.type === expenseTypes.GRANT; @@ -123,12 +105,11 @@ const ExpenseSummary = ({ const expenseTaxes = expense?.taxes?.length > 0 ? expense.taxes : expense?.draft?.taxes || []; const isMultiCurrency = expense?.amountInAccountCurrency && expense.amountInAccountCurrency.currency !== expense.currency; - const { LoggedInUser } = useLoggedInUser(); + const LoggedInUser = useLoggedInUser().LoggedInUser; const isLoggedInUserExpenseHostAdmin = LoggedInUser?.isHostAdmin(expense?.account); const isLoggedInUserExpenseAdmin = LoggedInUser?.isAdminOfCollective(expense?.account); const isViewingExpenseInHostContext = isLoggedInUserExpenseHostAdmin && !isLoggedInUserExpenseAdmin; - const { canEditTitle, canEditType, canEditItems, canUsePrivateNote, canAttachReceipts } = - LoggedInUser?.hasPreviewFeatureEnabled(PREVIEW_FEATURE_KEYS.INLINE_EDIT_EXPENSE) ? expense?.permissions || {} : {}; + const { canEditTitle, canEditType, canEditItems, canUsePrivateNote, canAttachReceipts } = expense?.permissions || {}; const invoiceFile = React.useMemo( () => expense?.invoiceFile || expense?.draft?.invoiceFile, [expense?.invoiceFile, expense?.draft?.invoiceFile], @@ -137,10 +118,6 @@ const ExpenseSummary = ({ () => (expense?.attachedFiles?.length ? expense.attachedFiles : (expense?.draft?.attachedFiles ?? [])), [expense?.attachedFiles, expense?.draft?.attachedFiles], ); - const hasNewEditFlow = - LoggedInUser?.hasPreviewFeatureEnabled(PREVIEW_FEATURE_KEYS.NEW_EXPENSE_FLOW) && - !parseToBoolean(router.query.forceLegacyFlow); - const processButtons = ( )} @@ -687,17 +663,16 @@ const ExpenseSummary = ({ host={host} expense={expense} collective={collective} - isDraft={!isEditing && expense?.status === ExpenseStatus.DRAFT} + isDraft={expense?.status === ExpenseStatus.DRAFT} /> - {!isEditing && - (drawerActionsContainer ? ( - createPortal(processButtons, drawerActionsContainer) - ) : showProcessButtons ? ( - - - {processButtons} - - ) : null)} + {drawerActionsContainer ? ( + createPortal(processButtons, drawerActionsContainer) + ) : showProcessButtons ? ( + + + {processButtons} + + ) : null} ); }; diff --git a/components/expenses/ExpenseSummaryAdditionalInformation.js b/components/expenses/ExpenseSummaryAdditionalInformation.js index 7b28c7a675f..1b22ff5db62 100644 --- a/components/expenses/ExpenseSummaryAdditionalInformation.js +++ b/components/expenses/ExpenseSummaryAdditionalInformation.js @@ -10,8 +10,6 @@ import { INVITE, VIRTUAL_CARD } from '../../lib/constants/payout-method'; import { ExpenseStatus } from '../../lib/graphql/types/v2/graphql'; import formatCollectiveType from '../../lib/i18n/collective-type'; import { getDashboardRoute } from '../../lib/url-helpers'; -import useLoggedInUser from '@/lib/hooks/useLoggedInUser'; -import { PREVIEW_FEATURE_KEYS } from '@/lib/preview-features'; import { AccountHoverCard } from '../AccountHoverCard'; import Avatar from '../Avatar'; @@ -107,12 +105,7 @@ const ExpenseSummaryAdditionalInformation = ({ const isInvoice = expense?.type === expenseTypes.INVOICE; const isCharge = expense?.type === expenseTypes.CHARGE; const isPaid = expense?.status === ExpenseStatus.PAID; - const { LoggedInUser } = useLoggedInUser(); - const { canEditPaidBy, canEditPayee, canEditPayoutMethod } = LoggedInUser?.hasPreviewFeatureEnabled( - PREVIEW_FEATURE_KEYS.INLINE_EDIT_EXPENSE, - ) - ? expense?.permissions || {} - : {}; + const { canEditPaidBy, canEditPayee, canEditPayoutMethod } = expense?.permissions || {}; if (isLoading) { return ; diff --git a/components/expenses/ExpenseTypeRadioSelect.tsx b/components/expenses/ExpenseTypeRadioSelect.tsx deleted file mode 100644 index a7cb5c14e0f..00000000000 --- a/components/expenses/ExpenseTypeRadioSelect.tsx +++ /dev/null @@ -1,194 +0,0 @@ -import React from 'react'; -import { defineMessages, useIntl } from 'react-intl'; -import { styled } from 'styled-components'; - -import expenseTypes from '../../lib/constants/expenseTypes'; - -import { Box, Flex } from '../Grid'; -import StyledCard from '../StyledCard'; -import { P } from '../Text'; - -import grantIllustration from '../../public/static/images/grant.gif'; -import grantAnimation from '../../public/static/images/grant-animation.gif'; -import invoiceIllustration from '../../public/static/images/invoice-animation.gif'; -import invoiceIllustrationStatic from '../../public/static/images/invoice-animation-static.jpg'; -import receiptIllustration from '../../public/static/images/receipt-animation.gif'; -import receiptIllustrationStatic from '../../public/static/images/receipt-animation-static.jpg'; - -const ExpenseTypeLabels = defineMessages({ - [expenseTypes.INVOICE]: { - id: 'Expense.Type.Invoice', - defaultMessage: 'Invoice', - }, - [expenseTypes.RECEIPT]: { - id: 'ExpenseForm.ReceiptLabel', - defaultMessage: 'Reimbursement', - }, - [expenseTypes.GRANT]: { - id: 'ExpenseForm.Type.Request', - defaultMessage: 'Request Grant', - }, -}); - -const ExpenseTypeDescription = defineMessages({ - [expenseTypes.RECEIPT]: { - id: 'ExpenseForm.ReceiptDescription', - defaultMessage: 'Get reimbursed for a purchase already made.', - }, - [expenseTypes.INVOICE]: { - id: 'ExpenseForm.InvoiceDescription', - defaultMessage: 'Bill for your time or a service.', - }, - [expenseTypes.GRANT]: { - id: 'ExpenseForm.FundingRequestDescription', - defaultMessage: 'Request a grant for your project or initiative.', - }, -}); - -const TypeIllustration = styled.img.attrs({ alt: '' })` - width: 48px; - height: 48px; -`; - -const StaticTypeIllustration = styled(TypeIllustration)` - position: absolute; - background: white; - width: 48px; - height: 48px; -`; - -const ExpenseTypeOptionContainer = styled.label` - display: flex; - align-items: baseline; - padding: 15px 16px; - margin-bottom: 0; - cursor: pointer; - background: white; - justify-content: flex-start; - flex: 1; - - // The following adds a border on top and left to separate items. Because parent has overflow=hidden, - // only the required one will actually be displayed - border-top: 1px solid #dcdee0; - border-left: 1px solid #dcdee0; - margin-top: -1px; - margin-left: -1px; - - input[type='radio'] { - margin-right: 4px; - } - - // Animate gif on hover by hidding the static illustration - &:hover { - ${StaticTypeIllustration} { - opacity: 0; - } - } -`; - -const illustrations = { - [expenseTypes.INVOICE]: invoiceIllustration, - [expenseTypes.RECEIPT]: receiptIllustration, - [expenseTypes.GRANT]: grantAnimation, -}; - -const staticIllustrations = { - [expenseTypes.INVOICE]: invoiceIllustrationStatic, - [expenseTypes.RECEIPT]: receiptIllustrationStatic, - [expenseTypes.GRANT]: grantIllustration, -}; - -interface ExpenseTypeOptionProps { - name: string; - type: (typeof expenseTypes)[keyof typeof expenseTypes]; - isChecked?: boolean; - onChange?: (...args: unknown[]) => unknown; - disabled?: boolean; -} - -const ExpenseTypeOption = ({ name, type, isChecked, onChange, disabled }: ExpenseTypeOptionProps) => { - const { formatMessage } = useIntl(); - const illustrationSrc = illustrations[type] || receiptIllustration; - const staticIllustrationSrc = staticIllustrations[type] || receiptIllustrationStatic; - return ( - - - - - - - - - -

- {formatMessage(ExpenseTypeLabels[type])} -

- - - - - -

- {formatMessage(ExpenseTypeDescription[type])} -

-
-
-
- ); -}; - -const Fieldset = styled.fieldset` - border: none; - padding: 0; - margin: 0; -`; - -const BASE_EXPENSE_TYPES = [expenseTypes.INVOICE, expenseTypes.RECEIPT, expenseTypes.GRANT]; - -interface ExpenseTypeRadioSelectProps { - /** The name of the input in the DOM */ - name: string; - /** Default value */ - value?: (typeof expenseTypes)[keyof typeof expenseTypes]; - /** A function called with the new value when it changes */ - onChange?: (...args: unknown[]) => unknown; - /** Supported expense types */ - supportedExpenseTypes: string[]; - disabled?: boolean; -} - -/** - * To select expense's type. - * - * Using `StyledRadioList` should have been the default choice, but unfortunately - * IE & Chrome don't support using `flex` on fieldset yet, so we have to create a custom - * layout. See https://github.com/w3c/csswg-drafts/issues/321 - */ -const ExpenseTypeRadioSelect = ({ - name = 'expense-type', - onChange, - value, - supportedExpenseTypes, - disabled, -}: ExpenseTypeRadioSelectProps) => { - return ( - -
- - {BASE_EXPENSE_TYPES.filter(type => supportedExpenseTypes.includes(type)).map(type => ( - - ))} - -
-
- ); -}; - -export default React.memo(ExpenseTypeRadioSelect); diff --git a/components/expenses/ItemDescriptionHint.tsx b/components/expenses/ItemDescriptionHint.tsx deleted file mode 100644 index ff1bfd04b0d..00000000000 --- a/components/expenses/ItemDescriptionHint.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; -import type { FieldProps, FormikProps } from 'formik'; -import { FormattedMessage, useIntl } from 'react-intl'; - -import StyledLinkButton from '../StyledLinkButton'; - -import type { ExpenseFormValues } from './types/FormValues'; - -type ExpenseItemDescriptionHintProps = { - form: FormikProps; - item: ExpenseFormValues['items'][0]; - isInvoice: boolean; - field: FieldProps['field']; -}; - -export const ExpenseItemDescriptionHint = ({ item, isInvoice, form, field }: ExpenseItemDescriptionHintProps) => { - const [hideHint, setHideHint] = React.useState(false); - const intl = useIntl(); - const suggested = item.__parsingResult?.description; - - if (suggested && suggested !== field.value) { - return ( - - { - form.setFieldValue(field.name, suggested); - setHideHint(true); - }} - > - {suggested} - - ), - }} - /> - - ); - } else if (!hideHint) { - return isInvoice - ? intl.formatMessage({ - defaultMessage: 'Specify item or activity and timeframe, e.g. "Volunteer Training, April 2023"', - id: 'rpqkOE', - }) - : intl.formatMessage({ - defaultMessage: 'Describe the expense, e.g. "Dinner with the team"', - id: 'cX1UXz', - }); - } else { - return null; - } -}; diff --git a/components/expenses/lib/items.ts b/components/expenses/lib/items.ts index fcf2ec2bc30..45cf970017c 100644 --- a/components/expenses/lib/items.ts +++ b/components/expenses/lib/items.ts @@ -48,17 +48,6 @@ export const newExpenseItem = (attrs = {}, expenseCurrency: string): ExpenseItem amountV2: getExpenseItemAmountV2FromNewAttrs(attrs, expenseCurrency), }); -/** Helper to add a new item to the form */ -export const addNewExpenseItem = ( - formik: FormikProps, - defaultValues: Partial = {}, -): void => { - formik.setFieldValue('items', [ - ...(formik.values.items || []), - newExpenseItem(defaultValues, formik.values.currency), - ]); -}; - /** * Returns true if the attachment require adding a file */ diff --git a/components/expenses/lib/ocr.ts b/components/expenses/lib/ocr.ts index 1b4a25da45c..b4716a12bc1 100644 --- a/components/expenses/lib/ocr.ts +++ b/components/expenses/lib/ocr.ts @@ -115,16 +115,6 @@ export const filterParsableItems = (items: UploadFileResult['parsingResult']['ex } }; -export const checkExpenseSupportsOCR = (expenseType: ExpenseType, loggedInUser): boolean => { - if (['RECEIPT', 'CHARGE'].includes(expenseType)) { - return true; - } else if (expenseType === 'INVOICE') { - return Boolean(loggedInUser?.isRoot); - } else { - return false; - } -}; - type FieldsWithOCRSupport = 'description' | 'incurredAt' | 'amountV2'; type ExpenseItemFields = Extract; @@ -181,8 +171,3 @@ export const compareItemOCRValues = (item: ExpenseItemFormValues): ExpenseOCRVal return result; }, {} as ExpenseOCRValuesComparison); }; - -/** Return true if the item has an OCR parsing result */ -export const itemHasOCR = (item: ExpenseItemFormValues): boolean => { - return Boolean(item.__parsingResult); -}; diff --git a/components/expenses/lib/utils.ts b/components/expenses/lib/utils.ts index 9f2e98857bf..86a6e4125f1 100644 --- a/components/expenses/lib/utils.ts +++ b/components/expenses/lib/utils.ts @@ -1,13 +1,7 @@ -import { isEmpty, isNil, round, sumBy, uniq } from 'lodash'; +import { isNil, round, sumBy } from 'lodash'; -import { FEATURES, isFeatureEnabled } from '../../../lib/allowed-features'; -import { CollectiveType } from '../../../lib/constants/collectives'; -import { Currency, PayPalSupportedCurrencies } from '../../../lib/constants/currency'; -import expenseTypes from '../../../lib/constants/expenseTypes'; -import { PayoutMethodType } from '../../../lib/constants/payout-method'; import { diffExchangeRates } from '../../../lib/currency-utils'; -import { validateTaxInput } from '../../taxes/TaxesFormikFields'; import type { ExpenseItemFormValues } from '../types/FormValues'; // Please adjust the values below based on `prepareItems` from `api/server/graphql/common/expenses.ts` @@ -15,19 +9,6 @@ import type { ExpenseItemFormValues } from '../types/FormValues'; export const FX_RATE_WARNING_THRESHOLD = 0.02; export const FX_RATE_ERROR_THRESHOLD = 0.1; -export const checkRequiresAddress = values => { - const collectiveTypesRequiringAddress = [CollectiveType.INDIVIDUAL, CollectiveType.USER, CollectiveType.ORGANIZATION]; - const expenseTypesRequiringAddress = [expenseTypes.INVOICE, expenseTypes.GRANT]; - - return ( - expenseTypesRequiringAddress.includes(values.type) && - (values.payee?.isNewUser || - (values.payee && - !values.payee.isInvite && - (collectiveTypesRequiringAddress.includes(values.payee.type) || values.payee.isHost))) - ); -}; - export const isTaxRateValid = rate => !isNil(rate) && rate >= 0 && rate <= 1; export const getTaxAmount = (baseAmount, tax) => { @@ -104,58 +85,3 @@ export const computeExpenseAmounts = (expenseCurrency: string, items: ExpenseIte export const getAmountWithoutTaxes = (totalAmount, taxes) => { return totalAmount / (1 + sumBy(taxes, 'rate')); }; - -export const validateExpenseTaxes = (intl, taxes) => { - const enabledTaxes = taxes?.filter(tax => !tax.isDisabled) || []; - if (!enabledTaxes.length) { - return null; - } else { - const taxesErrors = enabledTaxes.map(tax => validateTaxInput(intl, tax)); - const hasErrors = taxesErrors.some(errors => !isEmpty(errors)); - return hasErrors ? taxesErrors : null; - } -}; - -/** - * Returns the list of supported currencies for this expense / payout method. - * The collective currency always comes first. - */ -export const getSupportedCurrencies = (collective, { payee, payoutMethod, type, currency }) => { - // We don't allow changing the currency for virtual card charges - if (type === expenseTypes.CHARGE) { - return [currency]; - } - - // Multi-currency opt-out - if ( - !isFeatureEnabled(collective, FEATURES.MULTI_CURRENCY_EXPENSES) || - !isFeatureEnabled(collective.host, FEATURES.MULTI_CURRENCY_EXPENSES) || - payoutMethod?.type === PayoutMethodType.ACCOUNT_BALANCE - ) { - return [collective.currency]; - } - - // Allow any currency for invites - if (payee?.isInvite && !payoutMethod?.data?.currency) { - return Currency; - } - - // Adapt based on payout method type - const isPayPal = payoutMethod?.type === PayoutMethodType.PAYPAL; - if (isPayPal) { - const defaultCurrency = PayPalSupportedCurrencies.includes(collective.currency) ? collective.currency : 'USD'; - return uniq([defaultCurrency, ...PayPalSupportedCurrencies]); - } else if (payoutMethod?.type === PayoutMethodType.OTHER) { - return Currency.includes(collective.currency) ? uniq([collective.currency, ...Currency]) : Currency; - } else { - return uniq( - [collective.currency, collective.host?.currency, payoutMethod?.currency, payoutMethod?.data?.currency].filter( - Boolean, - ), - ); - } -}; - -export const expenseTypeSupportsItemCurrency = expenseType => { - return [expenseTypes.RECEIPT, expenseTypes.INVOICE].includes(expenseType); -}; diff --git a/components/expenses/list/SubmittedExpenseListItem.tsx b/components/expenses/list/SubmittedExpenseListItem.tsx index a77479cd3f2..692b5c403a7 100644 --- a/components/expenses/list/SubmittedExpenseListItem.tsx +++ b/components/expenses/list/SubmittedExpenseListItem.tsx @@ -2,15 +2,12 @@ import React from 'react'; import { clsx } from 'clsx'; import { includes, truncate } from 'lodash'; import { Check, Copy, Ellipsis, Link } from 'lucide-react'; -import { useRouter } from 'next/router'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import type { ExpensePageExpenseFieldsFragment } from '../../../lib/graphql/types/v2/graphql'; import { ExpenseStatus, ExpenseType } from '../../../lib/graphql/types/v2/graphql'; import useClipboard from '../../../lib/hooks/useClipboard'; -import useLoggedInUser from '../../../lib/hooks/useLoggedInUser'; import { i18nExpenseType } from '../../../lib/i18n/expense'; -import { PREVIEW_FEATURE_KEYS } from '../../../lib/preview-features'; import { getWebsiteUrl } from '../../../lib/utils'; import { AccountHoverCard } from '../../AccountHoverCard'; @@ -43,13 +40,7 @@ const I18nMessages = defineMessages({ }); export function SubmittedExpenseListItem(props: SubmittedExpenseListItemProps) { - const { LoggedInUser } = useLoggedInUser(); - const router = useRouter(); - const hasNewSubmitExpenseFlow = - LoggedInUser?.hasPreviewFeatureEnabled(PREVIEW_FEATURE_KEYS.NEW_EXPENSE_FLOW) || router.query.newExpenseFlowEnabled; - const canDuplicateExpense = - hasNewSubmitExpenseFlow && [ExpenseType.INVOICE, ExpenseType.RECEIPT].includes(props.expense.type) && props.expense.status !== ExpenseStatus.DRAFT; diff --git a/components/submit-expense/SubmittedExpense.tsx b/components/submit-expense/SubmittedExpense.tsx index e078fbc154a..6195055c9f1 100644 --- a/components/submit-expense/SubmittedExpense.tsx +++ b/components/submit-expense/SubmittedExpense.tsx @@ -55,7 +55,6 @@ export function SubmittedExpense(props: SubmittedExpenseProps) { enableKeyboardShortcuts={false} drawerActionsContainer={null} canEditTags={false} - isEditing={false} isLoadingLoggedInUser={false} showProcessButtons={false} expense={expense} diff --git a/lang/ca.json b/lang/ca.json index 7f0ffa0f94a..df71078c2e7 100644 --- a/lang/ca.json +++ b/lang/ca.json @@ -30,7 +30,6 @@ "+RvjCt": "Targeta eliminada satisfatòriament", "+S3jp9": "Nova actualització", "+SSp9V": "We encountered an issue loading this {type,select,section{section}other{page}}. Please reload the page or contact support if the problem persists.", - "+t6c4i": "Instruccions per a la categoria del compte", "+U6ozc": "Tipus", "+UdXIM": " Creat a través de GitHub", "+UwJxq": "Tarifes d'el Host previstes", @@ -148,13 +147,11 @@ "2u8jmQ": "Activa el límit d'intents per als pagaments amb 2FA", "2Un6+c": "KYC Information", "2uzHxT": "Importa un fitxer CSV", - "3135/i": "Moneda de la despesa", "322m9e": "El missatge ha de tenir almenys 10 caràcters", "32rBNK": "Condicions del servei", "34Up+l": "Mostra'n més", "35Jfcr": "Les targetes regal permeten als vostres empleats o membres de la comunitat donar suport als projectes de codi obert que els encanten. Més informació.", "37zpJw": "Cerca països...", - "38dzz9": "Categoria de despeses", "38fPNE": "Configuració de la importació de transaccions", "3A7J9A": "No hem trobat cap amfitrió que coincideixi amb tots els vostres criteris.", "3ABdi3": "Aquest compte no existeix", @@ -577,7 +574,6 @@ "AzGwgz": "The minimum amount for transferring to {selectedCurrency} is {minAmountForSelectedCurrency}", "AZmsFu": " rejected contribution from ", "AzRKUx": "Create Beneficiary", - "b++lom": "Sí, cancel·la l'edició", "B+fVMt": "More than {amount}", "B+NQ+r": " drafted an {expenseDescription} on ", "b07w+D": "Categoria", @@ -1174,7 +1170,6 @@ "CreatedAt": "Created At", "CreatedBy": "by {name}", "CreateEvent": "Create event", - "CreateExpense.HelpCreateInfo": "Request payment from {collective}. Expenses will be processed once approved by a Collective admin. The amount, description, and your profile name are public, but attachments, payment details, and other personal info is kept private.", "CreateExpenseFAQ.getPaid": "How do I get paid from a Collective?", "CreateExpenseFAQ.getPaidDetails": "Submit an expense and provide your payment information.", "CreateExpenseFAQ.howAreApproved": "How are expenses approved?", @@ -1297,7 +1292,6 @@ "DhxIpu": "New comment on ", "DinF1w": "Net Amount for {collective}", "dIoEln": "Total disbursed", - "DismissableHelp.DontShowAgain": "Ok, don’t show me again", "DisplayName": "Display Name", "DJnUUS": "This expense has not been approved", "dK5ItS": "By default only fiscal host administrators can submit expenses on behalf of vendors. You can allow other users who submit expenses to collectives you host to also submit expenses on behalf vendors.", @@ -1583,7 +1577,6 @@ "Expense.Direction.Received": "Received", "Expense.Direction.Submitted": "Submitted", "expense.draft": "Draft", - "Expense.edit": "Edit expense", "expense.editDetails": "Edit expense details", "expense.editNotes": "Edit notes", "expense.editPaidBy": "Edit paid by", @@ -1591,15 +1584,12 @@ "expense.editPayee": "Edit payee", "expense.editTitle": "Edit expense title", "expense.editType": "Edit expense type", - "Expense.EnterExpenseTitle": "Expense title (Public)", - "Expense.EnterRequestSubject": "Enter grant subject (Public)", "Expense.GoToPage": "Go to expense page", "expense.hostFeeInCollectiveCurrency": "comissió d'hostatjament", "expense.incurredAt": "Data", "expense.invite.declined": "Invite declined", "Expense.InviteIsOnItsWay.Description": "An invitation to submit this expense has been sent to {email}. Once they confirm and finish the process, it will appear on the expenses list.", "Expense.InvoiceItems": "Invoice items", - "Expense.JoinAndSubmit": "Join and Submit", "expense.linked": "Expense linked", "expense.markAsPaid": "Mark as paid", "Expense.MarkAsSpamLabel": "Why are you marking this expense as spam?", @@ -1628,7 +1618,6 @@ "Expense.PayTo": "Pay to", "expense.pending": "Pending", "expense.pickPayoutMethod": "Pick new payout method", - "Expense.PrivacyWarning": "This information is public. Do not put any private details in this field.", "Expense.PrivateNote": "Private note", "Expense.Receipt": "Receipt", "expense.ReceiptRequired": "Receipt required", @@ -1646,21 +1635,15 @@ "Expense.RequestDetails": "Detalls de la sol·licitud", "Expense.RequestedBy": "Invited by {name}", "expense.requestReApproval.btn": "Request re-approval", - "Expense.SaveExpense": "Save Expense", - "Expense.SaveReceipt": "Save Receipt", "expense.schedule.btn": "Schedule to Pay with {paymentMethod}", "expense.scheduledForPayment": "Scheduled for payment", "Expense.SeeDetails": "See expense details", "Expense.SendInvite": "Send Invite", - "Expense.SignUpInfoBox": "You need to create an account to receive a payment from {collectiveName}, by clicking 'Join and Submit' you agree to create an account on {WebsiteName}.", "expense.spam.notAllowed": "You can't mark your own expenses as spam", "expense.status": "Status", "Expense.Status.Refunded": "Refunded", "Expense.Submitted": "Expense submitted", "Expense.SubmittedBy": "Submitted by {name}", - "Expense.SuccessPage": "You can edit or review updates on this page.", - "Expense.Summary.Recurring.CheckboxDescription": "Choose this option to automatically submit a copy of this invoice on a periodic basis.", - "Expense.Summary.Recurring.CheckboxTitle": "Is this a recurring expense?", "Expense.type": "Type", "expense.type": "Tipus", "Expense.Type.Charge": "Virtual Card Charge", @@ -1677,16 +1660,12 @@ "ExpenseAmount": "Expense amount", "ExpenseApprover": "Expense Approver", "ExpenseForm.AddAttachedFile": "Add new document", - "ExpenseForm.AddGrantItem": "Add grant item", - "ExpenseForm.AddLineItem": "Add new item", - "ExpenseForm.AddReceipt": "Add new receipt", "ExpenseForm.AddressLabel": "Physical address", "ExpenseForm.CancelEditExpense": "Cancel Edit", "ExpenseForm.ChooseCountry": "Choose country", "ExpenseForm.ClearExpenseForm": "Clear Form", "ExpenseForm.ConfirmCancelEditExpense": "Are you sure you want to cancel the edits?", "ExpenseForm.ConfirmClearExpenseForm": "Are you sure you want to clear the expense form?", - "ExpenseForm.DescriptionPlaceholder": "Enter expense title here...", "ExpenseForm.FundingRequestDescription": "Request a grant for your project or initiative.", "ExpenseForm.GrantSubjectPlaceholder": "e.g., Research, software development, etc.", "ExpenseForm.inviteAdditionalInfo": "Want to enter payout details, such as a PayPal address or bank account?", @@ -1707,12 +1686,7 @@ "ExpenseForm.SavePayout": "Save this info for future payouts", "ExpenseForm.SignUp.OrgAdminNote": "You need to be an admin of the Organization to submit expenses.", "ExpenseForm.SignUp.SignIn": "We will use this email to create your account. If you already have an account {loginLink}.", - "ExpenseForm.StepExpense": "Upload one or multiple receipt", - "ExpenseForm.StepExpenseFundingRequest": "Set grant details", - "ExpenseForm.StepExpenseInvoice": "Set invoice details", - "ExpenseForm.StepPayeeInvoice": "Payee information", "ExpenseForm.Submit": "Submit expense", - "ExpenseForm.SubmitRequest": "Envia la sol·licitud", "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.", @@ -1747,7 +1721,6 @@ "ExpensesSubmittedByName": "Expenses submitted by {name}", "ExpenseSubmitters": "Expense Submitters", "ExpenseSummary.addNotesLabel": "Add notes", - "ExpenseSummaryTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} other {Expense}} Summary to {collectiveName}", "ExpenseTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} other {Expense}} {id} to {collectiveName}", "Explore": "Explore", "Export.Format": "Export {format}", @@ -1967,7 +1940,6 @@ "fiscalHosting.youngActivists.description": "Who may lack the experience to manage their own legal entity.", "FiscalHostPolicies": "Fiscal Host Policies", "fiscaltos": "terms of fiscal sponsorship", - "fIsGOi": "No, continue editing", "FJBnaq": "Client ID and client secret", "FKMNjH": "You won't be able to resume the synchronization in the future.", "FLqc8O": "Virtual card added", @@ -3347,7 +3319,6 @@ "PJubm9": "Financial tracking and transparency means reporting writes itself", "pKF7TO": "Require the contributor to provide their physical address when they contribute more than:", "Pkorog": "Account under verification", - "Pkq+ZR": "Please make sure that all the expense items in this expense belong to the selected expense category. If needed, you may submit additional items in separate expenses with different expense categories.", "Pkx+Wj": "Share results", "platform": "Platform", "platform.explainerVideo": "Explainer video", @@ -3381,8 +3352,6 @@ "PreviewFeatures.CollectiveOverview.Welcome.Description": "We’ve created this space for you to keep on top of everything happening in your Collective, please let us know how we can make it better!", "PreviewFeatures.CollectiveOverview.Welcome.Title": "Welcome to your new Collective Overview", "PreviewFeatures.crowdfundingRedesignDescription": "Be part of the crowdfunding redesign effort and get access to previews of new crowdfunding and profile pages. Experience enhanced profile pages with separate fundraising and storytelling views, clearer relationships between collectives and their projects, improved goal tracking, and better collective narratives that showcase your impact and long-term sustainability.{newLine}{newLine}Check out the blog post for more details.", - "PreviewFeatures.inlineEditExpenseDescription": "Edit expense details directly in the Dashboard without navigating to separate pages.", - "PreviewFeatures.inlineEditExpenseTitle": "Inline Expense Editing", "PreviewFeatures.keyboardShortcutsDescription": "Navigate the expense flow more efficiently with keyboard shortcuts. Speed up your workflow and reduce mouse dependency for common actions.", "PreviewFeatures.keyboardShortcutsExpenseDetailsAttachments": "{leftKey} and {rightKey} to navigate through attachments.", "PreviewFeatures.keyboardShortcutsExpenseDetailsClose": "{key} to close Expense details.", @@ -3396,8 +3365,6 @@ "PreviewFeatures.keyboardShortcutsExpenseListSecurity": "{key} to check Security alerts for selected Expense.", "PreviewFeatures.keyboardShortcutsTitle": "Keyboard Shortcuts", "PreviewFeatures.LimitedAccess": "Limited preview", - "PreviewFeatures.newExpenseFlowDescription": "Experience an improved expense submission flow in the Dashboard with better user experience, clearer navigation, and enhanced form validation.", - "PreviewFeatures.newExpenseFlowTitle": "New Expense Submission Flow", "PreviewFeatures.publicBeta": "Public beta", "PreviewFeatures.searchCommandDescription": "Discover a new way to search for collectives, transactions, expenses, and more through an intuitive command menu interface. Access information faster with powerful search capabilities.", "PreviewFeatures.searchCommandTitle": "Search Command Menu", @@ -3455,7 +3422,6 @@ "pricing.start.feeNote": "You wont be able to charge Collectives or set any Host Fee.", "pricing.title": "Our Pricing Structure", "pricing.unlimitedCollectives": "Unlimited Collectives", - "privacypolicy": "privacy policy", "Private": "Private", "PrivateCommentsMessage.Allowed": "Your comments are private.", "PrivateCommentsMessage.AllowedDetails": "Expenses comments are private, because they sometimes contain confidential information such as payment details. Only the expense submitter, the admins and the accountants can see them.", @@ -4247,7 +4213,6 @@ "TopContributors.Individuals": "Individuals", "TopContributors.Organizations": "Organizations", "tos": "terms of service", - "TOSAndPrivacyPolicyAgreement": "I agree with the {toslink} and {privacylink} of Open Collective.", "total": "total", "TotalAmount": "Total amount", "TotalAmountWithoutFee": "Total amount (without fees)", @@ -4487,14 +4452,10 @@ "UpgradePlanCTA.title.DISABLED": "Upgrade Required", "UpgradePlanCTA.title.UNSUPPORTED": "Feature not supported for your account", "UpgradePlanCTA.upgradeButton": "Upgrade your plan", - "UploadDocumentation": "Upload documentation", - "UploadDocumentationDescription": "If you want to include any documentation, you can upload it here.", "uploadImage.isDragActive": "Drop it like it's hot 🔥", "uploadImage.isDragReject": "🚫 This file type is not accepted", "uploadImage.isUploading": "Uploading image...", "uploadImage.sizeRejected": "Image resolution needs to be between {minResolution} and {maxResolution}. File size must be below {maxFileSize}.", - "UploadInvoice": "Upload invoice", - "UploadInvoiceDescription": "If you already have an invoice document, you can upload it here.", "uQguR/": "All Collectives", "Uqkhct": "No virtual cards", "uR+TjD": "Are you sure you want to delete this payout method?", @@ -4556,8 +4517,6 @@ "VendorsAndOrganizations.Description": "Manage all the external organizations you work with as vendors and quickly surface all the activity between your hosted Collectives and other platform Organizations.", "vEQcqS": "Two factor authentication added", "verificationCriteria": "verification criteria", - "VerifyEmailAddress": "Verify your email address", - "VerifyEmailInstructions": "A verification email has been sent to {email}. Click the link to complete submitting this expense. If you have not received the email, please check your spam.", "VErmYl": "Duplicate grant", "Vf1x2A": "Joined the platform on", "VfJsl4": "Beneficiary", @@ -4939,7 +4898,6 @@ "zNBAqh": "Aquest valor és una estimació. Si us plau, indiqueu l'import exacte rebut si el coneixeu.", "ZNBeE4": "Exactament {amount}", "ZNzyMo": "Exportar impostos i comissions del processador de pagaments com a columnes", - "zO+Zv3": "Anexos addicionals", "zoC8Gb": "El beneficiari cobreix les taxes", "zOk9pq": "Anar al registre d'activitats", "ZonfjV": "No podeu editar aquest col·lectiu", diff --git a/lang/cs.json b/lang/cs.json index 3e041e7e517..d0ee79473c0 100644 --- a/lang/cs.json +++ b/lang/cs.json @@ -30,7 +30,6 @@ "+RvjCt": "Karta byla úspěšně odstraněna", "+S3jp9": "Nová aktualizace", "+SSp9V": "We encountered an issue loading this {type,select,section{section}other{page}}. Please reload the page or contact support if the problem persists.", - "+t6c4i": "Instrukce kategorie účtu", "+U6ozc": "Typ", "+UdXIM": " vytvořeno prostřednictvím GitHub", "+UwJxq": "Očekávané hostitelské poplatky", @@ -148,13 +147,11 @@ "2u8jmQ": "Povolit limit rolování 2FA pro výplaty", "2Un6+c": "KYC Information", "2uzHxT": "Importovat CSV", - "3135/i": "Platební měna", "322m9e": "Zpráva musí být alespoň 10 znaku dlouhá", "32rBNK": "Terms of Service", "34Up+l": "Zobrazit více", "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "37zpJw": "Search countries...", - "38dzz9": "Kategorie výdajů", "38fPNE": "Transaction Import Settings", "3A7J9A": "Nemohli jsme najít hostitele, který splňuje všechna vaše kritéria.", "3ABdi3": "Tento účet neexistuje", @@ -577,7 +574,6 @@ "AzGwgz": "Minimální částka pro převod do {selectedCurrency} je {minAmountForSelectedCurrency}", "AZmsFu": " odmítnutý příspěvek od ", "AzRKUx": "Create Beneficiary", - "b++lom": "Ano, zrušit úpravy", "B+fVMt": "Více než {amount}", "B+NQ+r": " navrhl {expenseDescription} na ", "b07w+D": "Úroveň", @@ -1174,7 +1170,6 @@ "CreatedAt": "Created At", "CreatedBy": "by {name}", "CreateEvent": "Create event", - "CreateExpense.HelpCreateInfo": "Request payment from {collective}. Expenses will be processed once approved by a Collective admin. The amount, description, and your profile name are public, but attachments, payment details, and other personal info is kept private.", "CreateExpenseFAQ.getPaid": "How do I get paid from a Collective?", "CreateExpenseFAQ.getPaidDetails": "Submit an expense and provide your payment information.", "CreateExpenseFAQ.howAreApproved": "How are expenses approved?", @@ -1297,7 +1292,6 @@ "DhxIpu": "New comment on ", "DinF1w": "Net Amount for {collective}", "dIoEln": "Total disbursed", - "DismissableHelp.DontShowAgain": "Ok, nezobrazovat znovu", "DisplayName": "Display Name", "DJnUUS": "This expense has not been approved", "dK5ItS": "By default only fiscal host administrators can submit expenses on behalf of vendors. You can allow other users who submit expenses to collectives you host to also submit expenses on behalf vendors.", @@ -1583,7 +1577,6 @@ "Expense.Direction.Received": "Received", "Expense.Direction.Submitted": "Submitted", "expense.draft": "Draft", - "Expense.edit": "Edit expense", "expense.editDetails": "Edit expense details", "expense.editNotes": "Edit notes", "expense.editPaidBy": "Edit paid by", @@ -1591,15 +1584,12 @@ "expense.editPayee": "Edit payee", "expense.editTitle": "Edit expense title", "expense.editType": "Edit expense type", - "Expense.EnterExpenseTitle": "Expense title (Public)", - "Expense.EnterRequestSubject": "Enter grant subject (Public)", "Expense.GoToPage": "Go to expense page", "expense.hostFeeInCollectiveCurrency": "hostitelský poplatek", "expense.incurredAt": "Datum", "expense.invite.declined": "Invite declined", "Expense.InviteIsOnItsWay.Description": "An invitation to submit this expense has been sent to {email}. Once they confirm and finish the process, it will appear on the expenses list.", "Expense.InvoiceItems": "Položky faktury", - "Expense.JoinAndSubmit": "Join and Submit", "expense.linked": "Expense linked", "expense.markAsPaid": "Označit jako uhrazené", "Expense.MarkAsSpamLabel": "Why are you marking this expense as spam?", @@ -1628,7 +1618,6 @@ "Expense.PayTo": "Zaplatit komu", "expense.pending": "Pending", "expense.pickPayoutMethod": "Pick new payout method", - "Expense.PrivacyWarning": "This information is public. Do not put any private details in this field.", "Expense.PrivateNote": "Private note", "Expense.Receipt": "Receipt", "expense.ReceiptRequired": "Receipt required", @@ -1646,21 +1635,15 @@ "Expense.RequestDetails": "Request Details", "Expense.RequestedBy": "Invited by {name}", "expense.requestReApproval.btn": "Request re-approval", - "Expense.SaveExpense": "Save Expense", - "Expense.SaveReceipt": "Save Receipt", "expense.schedule.btn": "Schedule to Pay with {paymentMethod}", "expense.scheduledForPayment": "Scheduled for payment", "Expense.SeeDetails": "See expense details", "Expense.SendInvite": "Send Invite", - "Expense.SignUpInfoBox": "You need to create an account to receive a payment from {collectiveName}, by clicking 'Join and Submit' you agree to create an account on {WebsiteName}.", "expense.spam.notAllowed": "You can't mark your own expenses as spam", "expense.status": "Stav", "Expense.Status.Refunded": "Refunded", "Expense.Submitted": "Expense submitted", "Expense.SubmittedBy": "Odeslal/a {name}", - "Expense.SuccessPage": "You can edit or review updates on this page.", - "Expense.Summary.Recurring.CheckboxDescription": "Choose this option to automatically submit a copy of this invoice on a periodic basis.", - "Expense.Summary.Recurring.CheckboxTitle": "Is this a recurring expense?", "Expense.type": "Type", "expense.type": "Typ", "Expense.Type.Charge": "Virtual Card Charge", @@ -1677,16 +1660,12 @@ "ExpenseAmount": "Expense amount", "ExpenseApprover": "Expense Approver", "ExpenseForm.AddAttachedFile": "Add new document", - "ExpenseForm.AddGrantItem": "Add grant item", - "ExpenseForm.AddLineItem": "Přidat novou položku", - "ExpenseForm.AddReceipt": "Přidat novou účtenku", "ExpenseForm.AddressLabel": "Physical address", "ExpenseForm.CancelEditExpense": "Cancel Edit", "ExpenseForm.ChooseCountry": "Vyberte zemi", "ExpenseForm.ClearExpenseForm": "Clear Form", "ExpenseForm.ConfirmCancelEditExpense": "Are you sure you want to cancel the edits?", "ExpenseForm.ConfirmClearExpenseForm": "Are you sure you want to clear the expense form?", - "ExpenseForm.DescriptionPlaceholder": "Zadejte název výdaje zde...", "ExpenseForm.FundingRequestDescription": "Request a grant for your project or initiative.", "ExpenseForm.GrantSubjectPlaceholder": "e.g., Research, software development, etc.", "ExpenseForm.inviteAdditionalInfo": "Want to enter payout details, such as a PayPal address or bank account?", @@ -1707,12 +1686,7 @@ "ExpenseForm.SavePayout": "Save this info for future payouts", "ExpenseForm.SignUp.OrgAdminNote": "You need to be an admin of the Organization to submit expenses.", "ExpenseForm.SignUp.SignIn": "We will use this email to create your account. If you already have an account {loginLink}.", - "ExpenseForm.StepExpense": "Upload one or multiple receipt", - "ExpenseForm.StepExpenseFundingRequest": "Set grant details", - "ExpenseForm.StepExpenseInvoice": "Set invoice details", - "ExpenseForm.StepPayeeInvoice": "Payee information", "ExpenseForm.Submit": "Odeslat výdaj", - "ExpenseForm.SubmitRequest": "Odeslat žádost", "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.", @@ -1747,7 +1721,6 @@ "ExpensesSubmittedByName": "Expenses submitted by {name}", "ExpenseSubmitters": "Expense Submitters", "ExpenseSummary.addNotesLabel": "Přidat poznámky", - "ExpenseSummaryTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} Summary to {collectiveName}", "ExpenseTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} {id} to {collectiveName}", "Explore": "Explore", "Export.Format": "Exportovat {format}", @@ -1967,7 +1940,6 @@ "fiscalHosting.youngActivists.description": "Who may lack the experience to manage their own legal entity.", "FiscalHostPolicies": "Fiscal Host Policies", "fiscaltos": "podmínky fiskálního sponzorství", - "fIsGOi": "No, continue editing", "FJBnaq": "Client ID and client secret", "FKMNjH": "You won't be able to resume the synchronization in the future.", "FLqc8O": "Virtual card added", @@ -3347,7 +3319,6 @@ "PJubm9": "Financial tracking and transparency means reporting writes itself", "pKF7TO": "Require the contributor to provide their physical address when they contribute more than:", "Pkorog": "Account under verification", - "Pkq+ZR": "Please make sure that all the expense items in this expense belong to the selected expense category. If needed, you may submit additional items in separate expenses with different expense categories.", "Pkx+Wj": "Share results", "platform": "Platform", "platform.explainerVideo": "Explainer video", @@ -3381,8 +3352,6 @@ "PreviewFeatures.CollectiveOverview.Welcome.Description": "We’ve created this space for you to keep on top of everything happening in your Collective, please let us know how we can make it better!", "PreviewFeatures.CollectiveOverview.Welcome.Title": "Welcome to your new Collective Overview", "PreviewFeatures.crowdfundingRedesignDescription": "Be part of the crowdfunding redesign effort and get access to previews of new crowdfunding and profile pages. Experience enhanced profile pages with separate fundraising and storytelling views, clearer relationships between collectives and their projects, improved goal tracking, and better collective narratives that showcase your impact and long-term sustainability.{newLine}{newLine}Check out the blog post for more details.", - "PreviewFeatures.inlineEditExpenseDescription": "Edit expense details directly in the Dashboard without navigating to separate pages.", - "PreviewFeatures.inlineEditExpenseTitle": "Inline Expense Editing", "PreviewFeatures.keyboardShortcutsDescription": "Navigate the expense flow more efficiently with keyboard shortcuts. Speed up your workflow and reduce mouse dependency for common actions.", "PreviewFeatures.keyboardShortcutsExpenseDetailsAttachments": "{leftKey} and {rightKey} to navigate through attachments.", "PreviewFeatures.keyboardShortcutsExpenseDetailsClose": "{key} to close Expense details.", @@ -3396,8 +3365,6 @@ "PreviewFeatures.keyboardShortcutsExpenseListSecurity": "{key} to check Security alerts for selected Expense.", "PreviewFeatures.keyboardShortcutsTitle": "Keyboard Shortcuts", "PreviewFeatures.LimitedAccess": "Limited preview", - "PreviewFeatures.newExpenseFlowDescription": "Experience an improved expense submission flow in the Dashboard with better user experience, clearer navigation, and enhanced form validation.", - "PreviewFeatures.newExpenseFlowTitle": "New Expense Submission Flow", "PreviewFeatures.publicBeta": "Public beta", "PreviewFeatures.searchCommandDescription": "Discover a new way to search for collectives, transactions, expenses, and more through an intuitive command menu interface. Access information faster with powerful search capabilities.", "PreviewFeatures.searchCommandTitle": "Search Command Menu", @@ -3455,7 +3422,6 @@ "pricing.start.feeNote": "You wont be able to charge Collectives or set any Host Fee.", "pricing.title": "Our Pricing Structure", "pricing.unlimitedCollectives": "Unlimited Collectives", - "privacypolicy": "privacy policy", "Private": "Private", "PrivateCommentsMessage.Allowed": "Your comments are private.", "PrivateCommentsMessage.AllowedDetails": "Expenses comments are private, because they sometimes contain confidential information such as payment details. Only the expense submitter, the admins and the accountants can see them.", @@ -4247,7 +4213,6 @@ "TopContributors.Individuals": "Jednotlivci", "TopContributors.Organizations": "Organizace", "tos": "podmínky služby", - "TOSAndPrivacyPolicyAgreement": "I agree with the {toslink} and {privacylink} of Open Collective.", "total": "total", "TotalAmount": "Total amount", "TotalAmountWithoutFee": "Total amount (without fees)", @@ -4487,14 +4452,10 @@ "UpgradePlanCTA.title.DISABLED": "Upgrade Required", "UpgradePlanCTA.title.UNSUPPORTED": "Feature not supported for your account", "UpgradePlanCTA.upgradeButton": "Upgrade your plan", - "UploadDocumentation": "Upload documentation", - "UploadDocumentationDescription": "If you want to include any documentation, you can upload it here.", "uploadImage.isDragActive": "Drop it like it's hot 🔥", "uploadImage.isDragReject": "🚫 This file type is not accepted", "uploadImage.isUploading": "Uploading image...", "uploadImage.sizeRejected": "Image resolution needs to be between {minResolution} and {maxResolution}. File size must be below {maxFileSize}.", - "UploadInvoice": "Upload invoice", - "UploadInvoiceDescription": "If you already have an invoice document, you can upload it here.", "uQguR/": "All Collectives", "Uqkhct": "No virtual cards", "uR+TjD": "Are you sure you want to delete this payout method?", @@ -4556,8 +4517,6 @@ "VendorsAndOrganizations.Description": "Manage all the external organizations you work with as vendors and quickly surface all the activity between your hosted Collectives and other platform Organizations.", "vEQcqS": "Two factor authentication added", "verificationCriteria": "verification criteria", - "VerifyEmailAddress": "Verify your email address", - "VerifyEmailInstructions": "A verification email has been sent to {email}. Click the link to complete submitting this expense. If you have not received the email, please check your spam.", "VErmYl": "Duplicate grant", "Vf1x2A": "Joined the platform on", "VfJsl4": "Beneficiary", @@ -4939,7 +4898,6 @@ "zNBAqh": "This value is an estimate. Please set the exact amount received if known.", "ZNBeE4": "Exactly {amount}", "ZNzyMo": "Export taxes and payment processor fees as columns", - "zO+Zv3": "Additional attachments", "zoC8Gb": "The payee is covering the fees", "zOk9pq": "Go to activity log", "ZonfjV": "You cannot edit this collective", diff --git a/lang/de.json b/lang/de.json index bd2d317ee72..9f48f8edc4d 100644 --- a/lang/de.json +++ b/lang/de.json @@ -30,7 +30,6 @@ "+RvjCt": "Karte erfolgreich gelöscht", "+S3jp9": "Neues Update", "+SSp9V": "We encountered an issue loading this {type,select,section{section}other{page}}. Please reload the page or contact support if the problem persists.", - "+t6c4i": "Anweisungen zur Kontokategorie", "+U6ozc": "Typ", "+UdXIM": " würde über GitHub erstellt", "+UwJxq": "Erwartete Träger-Gebühren", @@ -148,13 +147,11 @@ "2u8jmQ": "Laufende Limit-Zwei-Faktor-Authentifizierung (2FA) für Auszahlungen aktivieren", "2Un6+c": "KYC Information", "2uzHxT": "CSV-Datei importieren", - "3135/i": "Ausgabenwährung", "322m9e": "Nachricht muss mindestens 10 Zeichen lang sein", "32rBNK": "Nutzungsbedingungen", "34Up+l": "Mehr anzeigen", "35Jfcr": "Geschenkkarten ermöglichen deinen Mitarbeitern oder Community-Mitgliedern die Projekte zu unterstützen, die sie lieben. Erfahre mehr.", "37zpJw": "Länder suchen...", - "38dzz9": "Ausgabenkategorie", "38fPNE": "Transaktions-Import-Einstellungen", "3A7J9A": "Wir konnten keinen Host finden, der Ihren Kriterien entspricht.", "3ABdi3": "Dieses Konto existiert nicht", @@ -577,7 +574,6 @@ "AzGwgz": "Der Mindestbetrag für die Überweisung nach {selectedCurrency} ist {minAmountForSelectedCurrency}", "AZmsFu": " hat den Beitrag von abgelehnt", "AzRKUx": "Create Beneficiary", - "b++lom": "Ja, Bearbeitung abbrechen", "B+fVMt": "Mehr als {amount}", "B+NQ+r": " hat einen {expenseDescription} auf erstellt", "b07w+D": "Stufe", @@ -1174,7 +1170,6 @@ "CreatedAt": "Created At", "CreatedBy": "von {name}", "CreateEvent": "Create event", - "CreateExpense.HelpCreateInfo": "Zahlung von {collective} erfragen. Ausgaben werden abgearbeitet, nachdem ein Administrator des Kollektivs sie genehmigt. Die Menge, die Beschreibung und Ihr Profilname werden veröffentlicht aber die Anhänge, Zahlungsdetails und andere persönliche Daten bleiben geheim.", "CreateExpenseFAQ.getPaid": "Wie werde ich von einem Kollektiv bezahlt?", "CreateExpenseFAQ.getPaidDetails": "Übermitteln Sie Ihre Ausgaben und geben Sie Ihre Zahlungsinformationen an.", "CreateExpenseFAQ.howAreApproved": "Wie werden die Ausgaben genehmigt?", @@ -1297,7 +1292,6 @@ "DhxIpu": "Neuer Kommentar zu ", "DinF1w": "Nettobetrag für {collective}", "dIoEln": "Total disbursed", - "DismissableHelp.DontShowAgain": "Verstanden, mir nicht mehr anzeigen", "DisplayName": "Display Name", "DJnUUS": "This expense has not been approved", "dK5ItS": "Standardmäßig können nur Administratoren von Finanzträgern Ausgaben im Namen von Verkäufern einreichen. Sie können anderen Benutzern, die Ausgaben für von dir gehostete Kollektive einreichen, erlauben, ebenfalls Ausgaben im Namen von Anbietern einzureichen.", @@ -1583,7 +1577,6 @@ "Expense.Direction.Received": "Erhalten", "Expense.Direction.Submitted": "Eingereicht", "expense.draft": "Entwurf", - "Expense.edit": "Ausgabe bearbeiten", "expense.editDetails": "Edit expense details", "expense.editNotes": "Edit notes", "expense.editPaidBy": "Edit paid by", @@ -1591,15 +1584,12 @@ "expense.editPayee": "Edit payee", "expense.editTitle": "Edit expense title", "expense.editType": "Edit expense type", - "Expense.EnterExpenseTitle": "Ausgabentitel (öffentlich)", - "Expense.EnterRequestSubject": "Gib den Ausgabetitel (öffentlich) ein", "Expense.GoToPage": "Gehe zur Ausgabenseite", "expense.hostFeeInCollectiveCurrency": "Träger-Gebühr", "expense.incurredAt": "Datum", "expense.invite.declined": "Invite declined", "Expense.InviteIsOnItsWay.Description": "Eine Einladung zur Einreichung dieser Ausgabe wurde an {email} gesendet. Sobald die Person den Vorgang bestätigt und abgeschlossen hat, wird sie in der Ausgabenliste erscheinen.", "Expense.InvoiceItems": "Rechnungsposten", - "Expense.JoinAndSubmit": "Beitreten und Absenden", "expense.linked": "Expense linked", "expense.markAsPaid": "Als bezahlt markieren", "Expense.MarkAsSpamLabel": "Why are you marking this expense as spam?", @@ -1628,7 +1618,6 @@ "Expense.PayTo": "Zahle an", "expense.pending": "In Arbeit", "expense.pickPayoutMethod": "Pick new payout method", - "Expense.PrivacyWarning": "Diese Informationen sind öffentlich. Trage keine privaten Daten in dieses Feld ein.", "Expense.PrivateNote": "Private Notiz", "Expense.Receipt": "Receipt", "expense.ReceiptRequired": "Beleg erforderlich", @@ -1646,21 +1635,15 @@ "Expense.RequestDetails": "Details anfordern", "Expense.RequestedBy": "Eingeladen von {name}", "expense.requestReApproval.btn": "Erneute Freigabe anfordern", - "Expense.SaveExpense": "Ausgabe speichern", - "Expense.SaveReceipt": "Quittung speichern", "expense.schedule.btn": "Bezahle mit {paymentMethod}", "expense.scheduledForPayment": "Geplant für Zahlung", "Expense.SeeDetails": "Ausgabendetails anzeigen", "Expense.SendInvite": "Einladung senden", - "Expense.SignUpInfoBox": "Du musst ein Konto erstellen, um eine Zahlung von {collectiveName} zu erhalten. Wenn du auf 'Anmelden und Abschicken' klickst, stimmst du zu, ein Konto auf {WebsiteName} zu erstellen.", "expense.spam.notAllowed": "You can't mark your own expenses as spam", "expense.status": "Status", "Expense.Status.Refunded": "Erstattet", "Expense.Submitted": "Ausgabe eingereicht", "Expense.SubmittedBy": "Eingereicht von {name}", - "Expense.SuccessPage": "Du kannst Updates auf dieser Seite bearbeiten oder überprüfen.", - "Expense.Summary.Recurring.CheckboxDescription": "Wähle diese Option, um automatisch eine Kopie dieser Rechnung in regelmäßigen Abständen zu übermitteln.", - "Expense.Summary.Recurring.CheckboxTitle": "Ist das eine wiederkehrende Ausgabe?", "Expense.type": "Type", "expense.type": "Art", "Expense.Type.Charge": "Virtual Card Charge", @@ -1677,16 +1660,12 @@ "ExpenseAmount": "Ausgabenbetrag", "ExpenseApprover": "Expense Approver", "ExpenseForm.AddAttachedFile": "Neues Dokument hinzufügen", - "ExpenseForm.AddGrantItem": "Berechtigungselement hinzufügen", - "ExpenseForm.AddLineItem": "Neuen Posten hinzufügen", - "ExpenseForm.AddReceipt": "Neue Quittung hinzufügen", "ExpenseForm.AddressLabel": "Physische Adresse", "ExpenseForm.CancelEditExpense": "Bearbeiten abbrechen", "ExpenseForm.ChooseCountry": "Land wählen", "ExpenseForm.ClearExpenseForm": "Formular löschen", "ExpenseForm.ConfirmCancelEditExpense": "Bist du sicher, dass du die Änderungen abbrechen möchtest?", "ExpenseForm.ConfirmClearExpenseForm": "Bist du sicher, dass du die Ausgabe löschen möchtest?", - "ExpenseForm.DescriptionPlaceholder": "Ausgabentitel hier eingeben...", "ExpenseForm.FundingRequestDescription": "Fordere einen Zuschuss für dein Projekt oder deine Initiative an.", "ExpenseForm.GrantSubjectPlaceholder": "e.g., Research, software development, etc.", "ExpenseForm.inviteAdditionalInfo": "Möchtest du Auszahlungsdetails wie eine PayPal-Adresse oder ein Bankkonto eingeben?", @@ -1707,12 +1686,7 @@ "ExpenseForm.SavePayout": "Diese Karte für zukünftige Zahlungen speichern", "ExpenseForm.SignUp.OrgAdminNote": "Du musst ein Administrator der Organisation sein, um Ausgaben einzureichen.", "ExpenseForm.SignUp.SignIn": "Wir werden diese E-Mail verwenden, um dein Konto zu erstellen. Wenn du bereits ein Konto hast {loginLink}.", - "ExpenseForm.StepExpense": "Einen oder mehrere Belege hochladen", - "ExpenseForm.StepExpenseFundingRequest": "Zuwendungsdetails festlegen", - "ExpenseForm.StepExpenseInvoice": "Rechnungsdetails hinzufügen", - "ExpenseForm.StepPayeeInvoice": "Angaben zum Zahlungsempfänger", "ExpenseForm.Submit": "Ausgaben einreichen", - "ExpenseForm.SubmitRequest": "Anfrage absenden", "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.", @@ -1747,7 +1721,6 @@ "ExpensesSubmittedByName": "Expenses submitted by {name}", "ExpenseSubmitters": "Expense Submitters", "ExpenseSummary.addNotesLabel": "Notizen hinzufügen", - "ExpenseSummaryTitle": "{type, select, CHARGE {Ladung} INVOICE {Rechnung} RECEIPT {Quittung} GRANT {Erlaubnis} SETTLEMENT {Begleichung} other {Ausgabe}} Zusammenfassung an {collectiveName}", "ExpenseTitle": "{type, select, CHARGE {Ladung} INVOICE {Rechnung} RECEIPT {Bekomme} GRANT {Gewähre} SETTLEMENT {Begleichung} other {Kosten}} {id} {collectiveName}", "Explore": "Erkunden", "Export.Format": "Exportiere {format}", @@ -1967,7 +1940,6 @@ "fiscalHosting.youngActivists.description": "Denen möglicherweise die Erfahrung fehlt, um ihre eigene juristische Einheit zu verwalten.", "FiscalHostPolicies": "Fiscal Host Policies", "fiscaltos": "Bedingungen des steuerlichen Sponsoring", - "fIsGOi": "Nein, weiter bearbeiten", "FJBnaq": "Client-ID und Client-Geheimnis", "FKMNjH": "You won't be able to resume the synchronization in the future.", "FLqc8O": "Virtuelle Karte hinzugefügt", @@ -3347,7 +3319,6 @@ "PJubm9": "Finanzielle Nachverfolgungen und Transparenzberichte schreiben sich von selbst", "pKF7TO": "Require the contributor to provide their physical address when they contribute more than:", "Pkorog": "Account under verification", - "Pkq+ZR": "Please make sure that all the expense items in this expense belong to the selected expense category. If needed, you may submit additional items in separate expenses with different expense categories.", "Pkx+Wj": "Share results", "platform": "Plattform", "platform.explainerVideo": "Erklärvideo", @@ -3381,8 +3352,6 @@ "PreviewFeatures.CollectiveOverview.Welcome.Description": "We’ve created this space for you to keep on top of everything happening in your Collective, please let us know how we can make it better!", "PreviewFeatures.CollectiveOverview.Welcome.Title": "Welcome to your new Collective Overview", "PreviewFeatures.crowdfundingRedesignDescription": "Be part of the crowdfunding redesign effort and get access to previews of new crowdfunding and profile pages. Experience enhanced profile pages with separate fundraising and storytelling views, clearer relationships between collectives and their projects, improved goal tracking, and better collective narratives that showcase your impact and long-term sustainability.{newLine}{newLine}Check out the blog post for more details.", - "PreviewFeatures.inlineEditExpenseDescription": "Edit expense details directly in the Dashboard without navigating to separate pages.", - "PreviewFeatures.inlineEditExpenseTitle": "Inline Expense Editing", "PreviewFeatures.keyboardShortcutsDescription": "Navigate the expense flow more efficiently with keyboard shortcuts. Speed up your workflow and reduce mouse dependency for common actions.", "PreviewFeatures.keyboardShortcutsExpenseDetailsAttachments": "{leftKey} and {rightKey} to navigate through attachments.", "PreviewFeatures.keyboardShortcutsExpenseDetailsClose": "{key} to close Expense details.", @@ -3396,8 +3365,6 @@ "PreviewFeatures.keyboardShortcutsExpenseListSecurity": "{key} to check Security alerts for selected Expense.", "PreviewFeatures.keyboardShortcutsTitle": "Keyboard Shortcuts", "PreviewFeatures.LimitedAccess": "Limited preview", - "PreviewFeatures.newExpenseFlowDescription": "Experience an improved expense submission flow in the Dashboard with better user experience, clearer navigation, and enhanced form validation.", - "PreviewFeatures.newExpenseFlowTitle": "New Expense Submission Flow", "PreviewFeatures.publicBeta": "Public beta", "PreviewFeatures.searchCommandDescription": "Discover a new way to search for collectives, transactions, expenses, and more through an intuitive command menu interface. Access information faster with powerful search capabilities.", "PreviewFeatures.searchCommandTitle": "Search Command Menu", @@ -3455,7 +3422,6 @@ "pricing.start.feeNote": "You wont be able to charge Collectives or set any Host Fee.", "pricing.title": "Unsere Preisstruktur", "pricing.unlimitedCollectives": "Unlimited Collectives", - "privacypolicy": "Datenschutzerklärung", "Private": "Private", "PrivateCommentsMessage.Allowed": "Deine Kommentare sind privat.", "PrivateCommentsMessage.AllowedDetails": "Expenses comments are private, because they sometimes contain confidential information such as payment details. Only the expense submitter, the admins and the accountants can see them.", @@ -4247,7 +4213,6 @@ "TopContributors.Individuals": "Personen", "TopContributors.Organizations": "Organisationen", "tos": "Nutzungsbedingungen", - "TOSAndPrivacyPolicyAgreement": "I agree with the {toslink} and {privacylink} of Open Collective.", "total": "total", "TotalAmount": "Total amount", "TotalAmountWithoutFee": "Total amount (without fees)", @@ -4487,14 +4452,10 @@ "UpgradePlanCTA.title.DISABLED": "Upgrade Required", "UpgradePlanCTA.title.UNSUPPORTED": "Feature not supported for your account", "UpgradePlanCTA.upgradeButton": "Upgrade your plan", - "UploadDocumentation": "Upload documentation", - "UploadDocumentationDescription": "If you want to include any documentation, you can upload it here.", "uploadImage.isDragActive": "Drop it like it's hot 🔥", "uploadImage.isDragReject": "🚫 This file type is not accepted", "uploadImage.isUploading": "Uploading image...", "uploadImage.sizeRejected": "Image resolution needs to be between {minResolution} and {maxResolution}. File size must be below {maxFileSize}.", - "UploadInvoice": "Upload invoice", - "UploadInvoiceDescription": "If you already have an invoice document, you can upload it here.", "uQguR/": "All Collectives", "Uqkhct": "No virtual cards", "uR+TjD": "Are you sure you want to delete this payout method?", @@ -4556,8 +4517,6 @@ "VendorsAndOrganizations.Description": "Manage all the external organizations you work with as vendors and quickly surface all the activity between your hosted Collectives and other platform Organizations.", "vEQcqS": "Two factor authentication added", "verificationCriteria": "verification criteria", - "VerifyEmailAddress": "Verifizieren Sie ihre E-Mail-Adresse", - "VerifyEmailInstructions": "A verification email has been sent to {email}. Click the link to complete submitting this expense. If you have not received the email, please check your spam.", "VErmYl": "Duplicate grant", "Vf1x2A": "Joined the platform on", "VfJsl4": "Beneficiary", @@ -4939,7 +4898,6 @@ "zNBAqh": "Bei diesem Wert handelt es sich um eine Schätzung. Bitte gib den genauen Betrag an, sofern bekannt.", "ZNBeE4": "Exactly {amount}", "ZNzyMo": "Export taxes and payment processor fees as columns", - "zO+Zv3": "Additional attachments", "zoC8Gb": "Der Zahlungsempfänger trägt die Gebühren", "zOk9pq": "Go to activity log", "ZonfjV": "You cannot edit this collective", diff --git a/lang/en.json b/lang/en.json index 063df9b7aaa..238f821adbd 100644 --- a/lang/en.json +++ b/lang/en.json @@ -30,7 +30,6 @@ "+RvjCt": "Card successfully deleted", "+S3jp9": "New Update", "+SSp9V": "We encountered an issue loading this {type,select,section{section}other{page}}. Please reload the page or contact support if the problem persists.", - "+t6c4i": "Account Category Instructions", "+U6ozc": "Type", "+UdXIM": " created through GitHub", "+UwJxq": "Expected Host Fees", @@ -148,13 +147,11 @@ "2u8jmQ": "Enable rolling limit 2FA for payouts", "2Un6+c": "KYC Information", "2uzHxT": "Import CSV", - "3135/i": "Expense Currency", "322m9e": "Message needs to be at least 10 characters long", "32rBNK": "Terms of Service", "34Up+l": "View more", "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "37zpJw": "Search countries...", - "38dzz9": "Expense Category", "38fPNE": "Transaction Import Settings", "3A7J9A": "We could not find a host that matches all your criteria.", "3ABdi3": "This account doesn't exist", @@ -577,7 +574,6 @@ "AzGwgz": "The minimum amount for transferring to {selectedCurrency} is {minAmountForSelectedCurrency}", "AZmsFu": " rejected contribution from ", "AzRKUx": "Create Beneficiary", - "b++lom": "Yes, cancel editing", "B+fVMt": "More than {amount}", "B+NQ+r": " drafted an {expenseDescription} on ", "b07w+D": "Tier", @@ -1174,7 +1170,6 @@ "CreatedAt": "Created At", "CreatedBy": "by {name}", "CreateEvent": "Create event", - "CreateExpense.HelpCreateInfo": "Request payment from {collective}. Expenses will be processed once approved by a Collective admin. The amount, description, and your profile name are public, but attachments, payment details, and other personal info is kept private.", "CreateExpenseFAQ.getPaid": "How do I get paid from a Collective?", "CreateExpenseFAQ.getPaidDetails": "Submit an expense and provide your payment information.", "CreateExpenseFAQ.howAreApproved": "How are expenses approved?", @@ -1297,7 +1292,6 @@ "DhxIpu": "New comment on ", "DinF1w": "Net Amount for {collective}", "dIoEln": "Total disbursed", - "DismissableHelp.DontShowAgain": "Ok, don’t show me again", "DisplayName": "Display Name", "DJnUUS": "This expense has not been approved", "dK5ItS": "By default only fiscal host administrators can submit expenses on behalf of vendors. You can allow other users who submit expenses to collectives you host to also submit expenses on behalf vendors.", @@ -1583,7 +1577,6 @@ "Expense.Direction.Received": "Received", "Expense.Direction.Submitted": "Submitted", "expense.draft": "Draft", - "Expense.edit": "Edit expense", "expense.editDetails": "Edit expense details", "expense.editNotes": "Edit notes", "expense.editPaidBy": "Edit paid by", @@ -1591,15 +1584,12 @@ "expense.editPayee": "Edit payee", "expense.editTitle": "Edit expense title", "expense.editType": "Edit expense type", - "Expense.EnterExpenseTitle": "Expense title (Public)", - "Expense.EnterRequestSubject": "Enter grant subject (Public)", "Expense.GoToPage": "Go to expense page", "expense.hostFeeInCollectiveCurrency": "host fee", "expense.incurredAt": "Date", "expense.invite.declined": "Invite declined", "Expense.InviteIsOnItsWay.Description": "An invitation to submit this expense has been sent to {email}. Once they confirm and finish the process, it will appear on the expenses list.", "Expense.InvoiceItems": "Invoice items", - "Expense.JoinAndSubmit": "Join and Submit", "expense.linked": "Expense linked", "expense.markAsPaid": "Mark as paid", "Expense.MarkAsSpamLabel": "Why are you marking this expense as spam?", @@ -1628,7 +1618,6 @@ "Expense.PayTo": "Pay to", "expense.pending": "Pending", "expense.pickPayoutMethod": "Pick new payout method", - "Expense.PrivacyWarning": "This information is public. Do not put any private details in this field.", "Expense.PrivateNote": "Private note", "Expense.Receipt": "Receipt", "expense.ReceiptRequired": "Receipt required", @@ -1646,21 +1635,15 @@ "Expense.RequestDetails": "Request Details", "Expense.RequestedBy": "Invited by {name}", "expense.requestReApproval.btn": "Request re-approval", - "Expense.SaveExpense": "Save Expense", - "Expense.SaveReceipt": "Save Receipt", "expense.schedule.btn": "Schedule to Pay with {paymentMethod}", "expense.scheduledForPayment": "Scheduled for payment", "Expense.SeeDetails": "See expense details", "Expense.SendInvite": "Send Invite", - "Expense.SignUpInfoBox": "You need to create an account to receive a payment from {collectiveName}, by clicking 'Join and Submit' you agree to create an account on {WebsiteName}.", "expense.spam.notAllowed": "You can't mark your own expenses as spam", "expense.status": "Status", "Expense.Status.Refunded": "Refunded", "Expense.Submitted": "Expense submitted", "Expense.SubmittedBy": "Submitted by {name}", - "Expense.SuccessPage": "You can edit or review updates on this page.", - "Expense.Summary.Recurring.CheckboxDescription": "Choose this option to automatically submit a copy of this invoice on a periodic basis.", - "Expense.Summary.Recurring.CheckboxTitle": "Is this a recurring expense?", "Expense.type": "Type", "expense.type": "Type", "Expense.Type.Charge": "Virtual Card Charge", @@ -1677,16 +1660,12 @@ "ExpenseAmount": "Expense amount", "ExpenseApprover": "Expense Approver", "ExpenseForm.AddAttachedFile": "Add new document", - "ExpenseForm.AddGrantItem": "Add grant item", - "ExpenseForm.AddLineItem": "Add new item", - "ExpenseForm.AddReceipt": "Add new receipt", "ExpenseForm.AddressLabel": "Physical address", "ExpenseForm.CancelEditExpense": "Cancel Edit", "ExpenseForm.ChooseCountry": "Choose country", "ExpenseForm.ClearExpenseForm": "Clear Form", "ExpenseForm.ConfirmCancelEditExpense": "Are you sure you want to cancel the edits?", "ExpenseForm.ConfirmClearExpenseForm": "Are you sure you want to clear the expense form?", - "ExpenseForm.DescriptionPlaceholder": "Enter expense title here...", "ExpenseForm.FundingRequestDescription": "Request a grant for your project or initiative.", "ExpenseForm.GrantSubjectPlaceholder": "e.g., Research, software development, etc.", "ExpenseForm.inviteAdditionalInfo": "Want to enter payout details, such as a PayPal address or bank account?", @@ -1707,12 +1686,7 @@ "ExpenseForm.SavePayout": "Save this info for future payouts", "ExpenseForm.SignUp.OrgAdminNote": "You need to be an admin of the Organization to submit expenses.", "ExpenseForm.SignUp.SignIn": "We will use this email to create your account. If you already have an account {loginLink}.", - "ExpenseForm.StepExpense": "Upload one or multiple receipt", - "ExpenseForm.StepExpenseFundingRequest": "Set grant details", - "ExpenseForm.StepExpenseInvoice": "Set invoice details", - "ExpenseForm.StepPayeeInvoice": "Payee information", "ExpenseForm.Submit": "Submit expense", - "ExpenseForm.SubmitRequest": "Submit request", "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.", @@ -1747,7 +1721,6 @@ "ExpensesSubmittedByName": "Expenses submitted by {name}", "ExpenseSubmitters": "Expense Submitters", "ExpenseSummary.addNotesLabel": "Add notes", - "ExpenseSummaryTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} Summary to {collectiveName}", "ExpenseTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} {id} to {collectiveName}", "Explore": "Explore", "Export.Format": "Export {format}", @@ -1967,7 +1940,6 @@ "fiscalHosting.youngActivists.description": "Who may lack the experience to manage their own legal entity.", "FiscalHostPolicies": "Fiscal Host Policies", "fiscaltos": "terms of fiscal sponsorship", - "fIsGOi": "No, continue editing", "FJBnaq": "Client ID and client secret", "FKMNjH": "You won't be able to resume the synchronization in the future.", "FLqc8O": "Virtual card added", @@ -3347,7 +3319,6 @@ "PJubm9": "Financial tracking and transparency means reporting writes itself", "pKF7TO": "Require the contributor to provide their physical address when they contribute more than:", "Pkorog": "Account under verification", - "Pkq+ZR": "Please make sure that all the expense items in this expense belong to the selected expense category. If needed, you may submit additional items in separate expenses with different expense categories.", "Pkx+Wj": "Share results", "platform": "Platform", "platform.explainerVideo": "Explainer video", @@ -3381,8 +3352,6 @@ "PreviewFeatures.CollectiveOverview.Welcome.Description": "We’ve created this space for you to keep on top of everything happening in your Collective, please let us know how we can make it better!", "PreviewFeatures.CollectiveOverview.Welcome.Title": "Welcome to your new Collective Overview", "PreviewFeatures.crowdfundingRedesignDescription": "Be part of the crowdfunding redesign effort and get access to previews of new crowdfunding and profile pages. Experience enhanced profile pages with separate fundraising and storytelling views, clearer relationships between collectives and their projects, improved goal tracking, and better collective narratives that showcase your impact and long-term sustainability.{newLine}{newLine}Check out the blog post for more details.", - "PreviewFeatures.inlineEditExpenseDescription": "Edit expense details directly in the Dashboard without navigating to separate pages.", - "PreviewFeatures.inlineEditExpenseTitle": "Inline Expense Editing", "PreviewFeatures.keyboardShortcutsDescription": "Navigate the expense flow more efficiently with keyboard shortcuts. Speed up your workflow and reduce mouse dependency for common actions.", "PreviewFeatures.keyboardShortcutsExpenseDetailsAttachments": "{leftKey} and {rightKey} to navigate through attachments.", "PreviewFeatures.keyboardShortcutsExpenseDetailsClose": "{key} to close Expense details.", @@ -3396,8 +3365,6 @@ "PreviewFeatures.keyboardShortcutsExpenseListSecurity": "{key} to check Security alerts for selected Expense.", "PreviewFeatures.keyboardShortcutsTitle": "Keyboard Shortcuts", "PreviewFeatures.LimitedAccess": "Limited preview", - "PreviewFeatures.newExpenseFlowDescription": "Experience an improved expense submission flow in the Dashboard with better user experience, clearer navigation, and enhanced form validation.", - "PreviewFeatures.newExpenseFlowTitle": "New Expense Submission Flow", "PreviewFeatures.publicBeta": "Public beta", "PreviewFeatures.searchCommandDescription": "Discover a new way to search for collectives, transactions, expenses, and more through an intuitive command menu interface. Access information faster with powerful search capabilities.", "PreviewFeatures.searchCommandTitle": "Search Command Menu", @@ -3455,7 +3422,6 @@ "pricing.start.feeNote": "You wont be able to charge Collectives or set any Host Fee.", "pricing.title": "Our Pricing Structure", "pricing.unlimitedCollectives": "Unlimited Collectives", - "privacypolicy": "privacy policy", "Private": "Private", "PrivateCommentsMessage.Allowed": "Your comments are private.", "PrivateCommentsMessage.AllowedDetails": "Expenses comments are private, because they sometimes contain confidential information such as payment details. Only the expense submitter, the admins and the accountants can see them.", @@ -4247,7 +4213,6 @@ "TopContributors.Individuals": "Individuals", "TopContributors.Organizations": "Organizations", "tos": "terms of service", - "TOSAndPrivacyPolicyAgreement": "I agree with the {toslink} and {privacylink} of Open Collective.", "total": "total", "TotalAmount": "Total amount", "TotalAmountWithoutFee": "Total amount (without fees)", @@ -4487,14 +4452,10 @@ "UpgradePlanCTA.title.DISABLED": "Upgrade Required", "UpgradePlanCTA.title.UNSUPPORTED": "Feature not supported for your account", "UpgradePlanCTA.upgradeButton": "Upgrade your plan", - "UploadDocumentation": "Upload documentation", - "UploadDocumentationDescription": "If you want to include any documentation, you can upload it here.", "uploadImage.isDragActive": "Drop it like it's hot 🔥", "uploadImage.isDragReject": "🚫 This file type is not accepted", "uploadImage.isUploading": "Uploading image...", "uploadImage.sizeRejected": "Image resolution needs to be between {minResolution} and {maxResolution}. File size must be below {maxFileSize}.", - "UploadInvoice": "Upload invoice", - "UploadInvoiceDescription": "If you already have an invoice document, you can upload it here.", "uQguR/": "All Collectives", "Uqkhct": "No virtual cards", "uR+TjD": "Are you sure you want to delete this payout method?", @@ -4556,8 +4517,6 @@ "VendorsAndOrganizations.Description": "Manage all the external organizations you work with as vendors and quickly surface all the activity between your hosted Collectives and other platform Organizations.", "vEQcqS": "Two factor authentication added", "verificationCriteria": "verification criteria", - "VerifyEmailAddress": "Verify your email address", - "VerifyEmailInstructions": "A verification email has been sent to {email}. Click the link to complete submitting this expense. If you have not received the email, please check your spam.", "VErmYl": "Duplicate grant", "Vf1x2A": "Joined the platform on", "VfJsl4": "Beneficiary", @@ -4939,7 +4898,6 @@ "zNBAqh": "This value is an estimate. Please set the exact amount received if known.", "ZNBeE4": "Exactly {amount}", "ZNzyMo": "Export taxes and payment processor fees as columns", - "zO+Zv3": "Additional attachments", "zoC8Gb": "The payee is covering the fees", "zOk9pq": "Go to activity log", "ZonfjV": "You cannot edit this collective", diff --git a/lang/es.json b/lang/es.json index a5e66851d5b..1d33e991554 100644 --- a/lang/es.json +++ b/lang/es.json @@ -30,7 +30,6 @@ "+RvjCt": "Tarjeta eliminada correctamente", "+S3jp9": "Nueva Entrada", "+SSp9V": "Hemos tenido un problema al cargar esta {type,select,section{sección}other{página}}. Vuelve a cargar la página o ponte en contacto con soporte si el problema persiste.", - "+t6c4i": "Instrucciones sobre la Categoría de Cuenta", "+U6ozc": "Tipo", "+UdXIM": " creado a través de GitHub", "+UwJxq": "Tarifas de Anfitrión esperadas", @@ -148,13 +147,11 @@ "2u8jmQ": "Activar el límite renovable de 2FA para los pagos", "2Un6+c": "Datos KYC (o Conoce a tu Cliente)", "2uzHxT": "Importar CSV", - "3135/i": "Moneda de gasto", "322m9e": "El mensaje debe tener al menos 10 caracteres", "32rBNK": "Términos de servicio", "34Up+l": "Ver más", "35Jfcr": "Las tarjetas regalo permiten a tus empleados o miembros de la comunidad apoyar los proyectos de código abierto que les gustan. Más información.", "37zpJw": "Buscar países...", - "38dzz9": "Categoría de gasto", "38fPNE": "Configuración de la importación de transacciones", "3A7J9A": "No hemos encontrado ningún Anfitrión que cumpla todos tus criterios.", "3ABdi3": "Esta cuenta no existe", @@ -577,7 +574,6 @@ "AzGwgz": "El monto mínimo para transferir a {selectedCurrency} es de {minAmountForSelectedCurrency}", "AZmsFu": " rechazó la colaboración de ", "AzRKUx": "Crear Beneficiario", - "b++lom": "Sí, cancela la edición", "B+fVMt": "Más de {amount}", "B+NQ+r": " elaboró un {expenseDescription} en ", "b07w+D": "Categoría", @@ -1174,7 +1170,6 @@ "CreatedAt": "Creado en", "CreatedBy": "por {name}", "CreateEvent": "Crear Evento", - "CreateExpense.HelpCreateInfo": "Solicitar el pago a {collective}. Los gastos se procesarán una vez aprobados por un administrador del Colectivo. El monto, la descripción y el nombre de tu perfil son públicos, pero los archivos adjuntos, los detalles del pago y otros datos personales se mantienen privados.", "CreateExpenseFAQ.getPaid": "¿Cómo puedo recibir el pago desde un Colectivo?", "CreateExpenseFAQ.getPaidDetails": "Envía un gasto y proporciona tu información de pago.", "CreateExpenseFAQ.howAreApproved": "¿Cómo se aprueban los gastos?", @@ -1297,7 +1292,6 @@ "DhxIpu": "Nuevo comentario en ", "DinF1w": "Monto neto para {collective}", "dIoEln": "Total desembolsado", - "DismissableHelp.DontShowAgain": "No mostrar de nuevo", "DisplayName": "Display Name", "DJnUUS": "Este gasto no ha sido aprobado", "dK5ItS": "Por defecto, sólo administradores de Anfitriones Fiscales pueden enviar gastos en nombre de proveedores. Puedes permitir que otros usuarios que envíen gastos a los Colectivos que alojas también envíen gastos en nombre de los proveedores.", @@ -1583,7 +1577,6 @@ "Expense.Direction.Received": "Recibido", "Expense.Direction.Submitted": "Enviado", "expense.draft": "Borrador", - "Expense.edit": "Editar gasto", "expense.editDetails": "Editar detalles del gasto", "expense.editNotes": "Editar notas", "expense.editPaidBy": "Edición pagada por", @@ -1591,15 +1584,12 @@ "expense.editPayee": "Editar beneficiario", "expense.editTitle": "Editar título del gasto", "expense.editType": "Editar tipo de gasto", - "Expense.EnterExpenseTitle": "Título del gasto (Público)", - "Expense.EnterRequestSubject": "Escriba el tema de la subvención (Público)", "Expense.GoToPage": "Ir a la página de gastos", "expense.hostFeeInCollectiveCurrency": "comisión del host", "expense.incurredAt": "Fecha", "expense.invite.declined": "Invitación rechazada", "Expense.InviteIsOnItsWay.Description": "Se ha enviado una invitación para presentar este gasto a {email}. Una vez que confirme y finalice el proceso, aparecerá en la lista de gastos.", "Expense.InvoiceItems": "Elementos de la factura", - "Expense.JoinAndSubmit": "Unirse y Enviar", "expense.linked": "Gasto vinculado", "expense.markAsPaid": "Marcar como pagado", "Expense.MarkAsSpamLabel": "¿Por qué marcas este gasto como spam?", @@ -1628,7 +1618,6 @@ "Expense.PayTo": "Pagar a", "expense.pending": "Pendiente", "expense.pickPayoutMethod": "Elegir nuevo método de pago", - "Expense.PrivacyWarning": "Esta información es pública. No pongas ningún dato privado en este campo.", "Expense.PrivateNote": "Nota privada", "Expense.Receipt": "Recibo", "expense.ReceiptRequired": "Recibo requerido", @@ -1646,21 +1635,15 @@ "Expense.RequestDetails": "Detalles de la solicitud", "Expense.RequestedBy": "Invitado por {name}", "expense.requestReApproval.btn": "Solicitar una nueva aprobación", - "Expense.SaveExpense": "Guardar Gasto", - "Expense.SaveReceipt": "Guardar recibo", "expense.schedule.btn": "Programar el Pago con {paymentMethod}", "expense.scheduledForPayment": "Programado para el pago", "Expense.SeeDetails": "Ver detalles del gasto", "Expense.SendInvite": "Enviar invitación", - "Expense.SignUpInfoBox": "Debes crear una cuenta para recibir un pago de {collectiveName}. Al hacer clic en \"Unirse y Enviar\", aceptas crear una cuenta en {WebsiteName}.", "expense.spam.notAllowed": "No puedes marcar tus propios gastos como spam", "expense.status": "Estado", "Expense.Status.Refunded": "Reembolsado", "Expense.Submitted": "Gasto enviado", "Expense.SubmittedBy": "Enviado por {name}", - "Expense.SuccessPage": "Puedes editar o revisar actualizaciones en esta página.", - "Expense.Summary.Recurring.CheckboxDescription": "Elige esta opción para enviar automáticamente una copia de esta factura de manera periódica.", - "Expense.Summary.Recurring.CheckboxTitle": "¿Es un gasto recurrente?", "Expense.type": "Tipo", "expense.type": "Tipo", "Expense.Type.Charge": "Cargo por Tarjeta Virtual", @@ -1677,16 +1660,12 @@ "ExpenseAmount": "Monto de gastos", "ExpenseApprover": "Aprobador de gastos", "ExpenseForm.AddAttachedFile": "Añade un nuevo documento", - "ExpenseForm.AddGrantItem": "Añadir elemento de subvención", - "ExpenseForm.AddLineItem": "Añadir un nuevo elemento", - "ExpenseForm.AddReceipt": "Añadir un nuevo recibo", "ExpenseForm.AddressLabel": "Dirección física", "ExpenseForm.CancelEditExpense": "Cancelar la Edición", "ExpenseForm.ChooseCountry": "Seleccione un país", "ExpenseForm.ClearExpenseForm": "Limpiar Formulario", "ExpenseForm.ConfirmCancelEditExpense": "¿Estás seguro de que quieres cancelar las ediciones?", "ExpenseForm.ConfirmClearExpenseForm": "¿Estás seguro de que deseas limpiar el formulario de gastos?", - "ExpenseForm.DescriptionPlaceholder": "Introduce aquí el título del gasto...", "ExpenseForm.FundingRequestDescription": "Solicitar una subvención para tu proyecto o iniciativa.", "ExpenseForm.GrantSubjectPlaceholder": "e.g., Research, software development, etc.", "ExpenseForm.inviteAdditionalInfo": "¿Quiere introducir los datos de pago, como una dirección de PayPal o una cuenta bancaria?", @@ -1707,12 +1686,7 @@ "ExpenseForm.SavePayout": "Guardar esta información para pagos futuros", "ExpenseForm.SignUp.OrgAdminNote": "Necesitas ser un administrador de la organización para enviar gastos.", "ExpenseForm.SignUp.SignIn": "Utilizaremos este correo electrónico para crear tu cuenta. Si ya tienes una cuenta {loginLink}.", - "ExpenseForm.StepExpense": "Sube uno o más recibos", - "ExpenseForm.StepExpenseFundingRequest": "Enviar detalles de subvención", - "ExpenseForm.StepExpenseInvoice": "Establecer detalles de la factura", - "ExpenseForm.StepPayeeInvoice": "Información del beneficiario", "ExpenseForm.Submit": "Enviar gasto", - "ExpenseForm.SubmitRequest": "Enviar solicitud", "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.", @@ -1747,7 +1721,6 @@ "ExpensesSubmittedByName": "Expenses submitted by {name}", "ExpenseSubmitters": "Presentadores de gastos", "ExpenseSummary.addNotesLabel": "Añadir notas", - "ExpenseSummaryTitle": "Resumen de {type, select, CHARGE {cargos} INVOICE {facturas} RECEIPT {recibos} GRANT {subvenciones} SETTLEMENT {acuerdos} PLATFORM_BILLING {factura de la plataforma} other {gastos}} a {collectiveName}", "ExpenseTitle": "{id} de {type, select, CHARGE {cargo} INVOICE {factura} RECEIPT {recibo} GRANT {subvención} SETTLEMENT {acuerdo} PLATFORM_BILLING {factura de la plataforma} other {gasto}} a {collectiveName}", "Explore": "Explorar", "Export.Format": "Exportar {format}", @@ -1967,7 +1940,6 @@ "fiscalHosting.youngActivists.description": "Que pueden carecer de experiencia para gestionar su propia entidad jurídica.", "FiscalHostPolicies": "Fiscal Host Policies", "fiscaltos": "condiciones de patrocinio fiscal", - "fIsGOi": "No, sigue editando", "FJBnaq": "Identificación y secreto del cliente", "FKMNjH": "No podrás reanudar la sincronización en el futuro.", "FLqc8O": "Tarjeta virtual añadida", @@ -3347,7 +3319,6 @@ "PJubm9": "Seguimiento y transparencia de las finanzas significa que los informes se escriben solos", "pKF7TO": "Requerir al colaborador que proporcione su dirección física cuando contribuya con más de:", "Pkorog": "Account under verification", - "Pkq+ZR": "Por favor, asegúrate de que todas las partidas de este gasto pertenecen a la categoría de gasto seleccionada. Si es necesario, puedes enviar partidas adicionales en gastos separados con categorías de gasto diferentes.", "Pkx+Wj": "Comparte los resultados", "platform": "Plataforma", "platform.explainerVideo": "Vídeo explicativo", @@ -3381,8 +3352,6 @@ "PreviewFeatures.CollectiveOverview.Welcome.Description": "Hemos creado este espacio para que estés al tanto de todo lo que ocurre en tu Colectivo, ¡dinos cómo podemos mejorarlo!", "PreviewFeatures.CollectiveOverview.Welcome.Title": "Bienvenido a tu nuevo Resumen del Colectivo", "PreviewFeatures.crowdfundingRedesignDescription": "Participa en el proyecto de rediseño del crowdfunding y accede a avances de las nuevas páginas de crowdfunding y perfiles. Disfruta de perfiles mejorados con vistas separadas para la recaudación de fondos y la narración, relaciones más claras entre los colectivos y sus proyectos, un mejor seguimiento de los objetivos y narrativas más completas que muestran tu impacto y sostenibilidad a largo plazo.{newLine}{newLine}Echa un vistazo a la entrada del blog para obtener más detalles.", - "PreviewFeatures.inlineEditExpenseDescription": "Edita los detalles de los gastos directamente en el Tablero sin tener que navegar a páginas separadas.", - "PreviewFeatures.inlineEditExpenseTitle": "Edición de gastos integrada", "PreviewFeatures.keyboardShortcutsDescription": "Navega por el flujo de gastos de forma más eficiente con atajos de teclado. Acelera tu flujo de trabajo y reduce la dependencia del ratón para las acciones más comunes.", "PreviewFeatures.keyboardShortcutsExpenseDetailsAttachments": "{leftKey} and {rightKey} to navigate through attachments.", "PreviewFeatures.keyboardShortcutsExpenseDetailsClose": "{key} to close Expense details.", @@ -3396,8 +3365,6 @@ "PreviewFeatures.keyboardShortcutsExpenseListSecurity": "{key} to check Security alerts for selected Expense.", "PreviewFeatures.keyboardShortcutsTitle": "Atajos de teclado", "PreviewFeatures.LimitedAccess": "Vista previa limitada", - "PreviewFeatures.newExpenseFlowDescription": "Disfrute de un flujo mejorado para el envío de gastos en el Tablero, con una interfaz más intuitiva, una navegación más clara y una validación de formularios optimizada.", - "PreviewFeatures.newExpenseFlowTitle": "Nuevo flujo para la presentación de gastos", "PreviewFeatures.publicBeta": "Beta pública", "PreviewFeatures.searchCommandDescription": "Descubre una nueva forma de buscar colectivos, transacciones, gastos y mucho más a través de un intuitivo menú de comandos. Accede a la información más rápidamente con potentes funciones de búsqueda.", "PreviewFeatures.searchCommandTitle": "Menú de comandos de búsqueda", @@ -3455,7 +3422,6 @@ "pricing.start.feeNote": "No podrás cobrar a los Colectivos ni establecer ninguna Tarifa de Anfitrión.", "pricing.title": "Nuestra Estructura de Precios", "pricing.unlimitedCollectives": "Colectivos ilimitados", - "privacypolicy": "política de privacidad", "Private": "Privado", "PrivateCommentsMessage.Allowed": "Tus comentarios son privados.", "PrivateCommentsMessage.AllowedDetails": "Los comentarios sobre los gastos son privados, ya que a veces contienen información confidencial, como datos de pago. Solo pueden verlos la persona que envía el gasto, los administradores y los contables.", @@ -4247,7 +4213,6 @@ "TopContributors.Individuals": "Individuos", "TopContributors.Organizations": "Organizaciones", "tos": "términos de servicio", - "TOSAndPrivacyPolicyAgreement": "Estoy de acuerdo con {toslink} y {privacylink} de Open Collective.", "total": "total", "TotalAmount": "Monto total", "TotalAmountWithoutFee": "Importe total (sin comisiones)", @@ -4487,14 +4452,10 @@ "UpgradePlanCTA.title.DISABLED": "Actualización requerida", "UpgradePlanCTA.title.UNSUPPORTED": "Función no disponible para tu cuenta", "UpgradePlanCTA.upgradeButton": "Mejora tu plan", - "UploadDocumentation": "Subir documentación", - "UploadDocumentationDescription": "Si quieres incluir documentación, puedes hacerlo aquí.", "uploadImage.isDragActive": "Suéltala como si quemara 🔥", "uploadImage.isDragReject": "🚫 Este tipo de archivo no es aceptado", "uploadImage.isUploading": "Subiendo imagen...", "uploadImage.sizeRejected": "La resolución de la imagen debe estar entre {minResolution} y {maxResolution}. El tamaño del archivo debe ser inferior a {maxFileSize}.", - "UploadInvoice": "Subir una factura", - "UploadInvoiceDescription": "Si ya tienes un documento de factura, puedes subirlo aquí.", "uQguR/": "Todos los Colectivos", "Uqkhct": "No hay tarjetas virtuales", "uR+TjD": "¿Seguro que quieres eliminar este método de pago?", @@ -4556,8 +4517,6 @@ "VendorsAndOrganizations.Description": "Manage all the external organizations you work with as vendors and quickly surface all the activity between your hosted Collectives and other platform Organizations.", "vEQcqS": "Añadida la autenticación de dos factores", "verificationCriteria": "criterios de verificación", - "VerifyEmailAddress": "Verificar tu dirección de correo electrónico", - "VerifyEmailInstructions": "Se ha enviado un correo electrónico de verificación a {email}. Haz clic en el enlace para completar el envío de este gasto. Si no te aparece el correo electrónico en tu bandeja de entrada revisa tu bandeja de Spam.", "VErmYl": "Duplicar subvención", "Vf1x2A": "Joined the platform on", "VfJsl4": "Beneficiario/a", @@ -4939,7 +4898,6 @@ "zNBAqh": "Este valor es una estimación. Por favor, indica el importe exacto recibido si lo conoces.", "ZNBeE4": "Exactamente {amount}", "ZNzyMo": "Impuestos de exportación y tarifas del procesador de pagos como columnas", - "zO+Zv3": "Archivos adjuntos adicionales", "zoC8Gb": "El beneficiario está cubriendo las tarifas", "zOk9pq": "Ir al registro de actividades", "ZonfjV": "No puede editar a este colectivo", diff --git a/lang/fr.json b/lang/fr.json index 6a24d14ec6e..1b375596155 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -30,7 +30,6 @@ "+RvjCt": "Carte supprimée avec succès", "+S3jp9": "Nouvelle mise à jour", "+SSp9V": "Nous avons rencontré un problème lors du chargement de cette {type,select,section{section}other{page}}. Veuillez recharger la page ou contacter le support si le problème persiste.", - "+t6c4i": "Instructions pour la catégorie comptable", "+U6ozc": "Type", "+UdXIM": " créé via GitHub", "+UwJxq": "Frais d'Hôte attendus", @@ -148,13 +147,11 @@ "2u8jmQ": "Activer la limite glissante de 2FA pour les remboursements", "2Un6+c": "Information sur le KYC", "2uzHxT": "Importer les CSV", - "3135/i": "Devise de dépense", "322m9e": "Le message doit comporter au moins 10 caractères", "32rBNK": "Conditions d'utilisation", "34Up+l": "Voir plus", "35Jfcr": "Les cartes cadeaux permettent à vos employés ou aux membres de la communauté de soutenir les projets qu'ils aiment. En savoir plus.", "37zpJw": "Rechercher des pays...", - "38dzz9": "Catégorie de dépense", "38fPNE": "Paramètres d'importation de transactions", "3A7J9A": "Nous n'avons pas trouvé d'Hôte correspondant à tous vos critères.", "3ABdi3": "Ce compte n'existe pas", @@ -577,7 +574,6 @@ "AzGwgz": "Le montant minimum pour le transfert vers {selectedCurrency} est {minAmountForSelectedCurrency}", "AZmsFu": " a rejeté une contribution de ", "AzRKUx": "Créer un bénéficiaire", - "b++lom": "Oui, annuler l'édition", "B+fVMt": "Plus que {amount}", "B+NQ+r": " a rédigé une {expenseDescription} sur ", "b07w+D": "Palier", @@ -1174,7 +1170,6 @@ "CreatedAt": "Créé le", "CreatedBy": "par {name}", "CreateEvent": "Créer un évènement", - "CreateExpense.HelpCreateInfo": "Demander un paiement à {collective}. Les dépenses seront traitées après avoir été approuvées par un administrateur du Collectif. Le montant, la description, et votre nom de profil sont publics, mais les pièces jointes, le détail du paiement et les autres informations personnelles resteront privées.", "CreateExpenseFAQ.getPaid": "Comment puis-je être payé par un Collectif ?", "CreateExpenseFAQ.getPaidDetails": "Ajoutez une dépense et fournissez vos informations de paiement.", "CreateExpenseFAQ.howAreApproved": "Comment les dépenses sont-elles approuvées ?", @@ -1297,7 +1292,6 @@ "DhxIpu": "Nouveau commentaire sur ", "DinF1w": "Montant net pour {collective}", "dIoEln": "Total déboursé", - "DismissableHelp.DontShowAgain": "Ok, ne plus me montrer", "DisplayName": "Nom affiché", "DJnUUS": "Cette dépense n'a pas été approuvée", "dK5ItS": "Par défaut, seuls les administrateurs fiscaux de l'hôte peuvent soumettre des dépenses pour le compte des fournisseurs. Vous pouvez autoriser d'autres utilisateurs qui soumettent des dépenses à des collectifs que vous gérez à soumettre également des dépenses pour le compte de fournisseurs.", @@ -1583,7 +1577,6 @@ "Expense.Direction.Received": "Reçues", "Expense.Direction.Submitted": "Envoyées", "expense.draft": "Brouillon", - "Expense.edit": "Éditer la dépense", "expense.editDetails": "Modifier les détails des dépenses", "expense.editNotes": "Modifier les notes", "expense.editPaidBy": "Modifier le paiement par", @@ -1591,15 +1584,12 @@ "expense.editPayee": "Modifier le bénéficiaire", "expense.editTitle": "Modifier le titre de la dépense", "expense.editType": "Modifier le type de dépense", - "Expense.EnterExpenseTitle": "Titre de la dépense (Public)", - "Expense.EnterRequestSubject": "Entrez le sujet de la subvention (Public)", "Expense.GoToPage": "Aller à la page de dépense", "expense.hostFeeInCollectiveCurrency": "commission de l'hôte", "expense.incurredAt": "Date", "expense.invite.declined": "Invitation déclinée", "Expense.InviteIsOnItsWay.Description": "Une invitation à envoyer cette dépense a été envoyée à {email}. Une fois celle-ci confirmée, elle apparaîtra sur la liste des dépenses.", "Expense.InvoiceItems": "Éléments de facture", - "Expense.JoinAndSubmit": "Rejoindre et soumettre", "expense.linked": "Dépense liée", "expense.markAsPaid": "Marquer comme payée", "Expense.MarkAsSpamLabel": "Pourquoi avez-vous marqué cette dépense comme un spam ?", @@ -1628,7 +1618,6 @@ "Expense.PayTo": "Payer à", "expense.pending": "En attente", "expense.pickPayoutMethod": "Choisir un nouveau moyen de paiement", - "Expense.PrivacyWarning": "Ce champ est public. N'ajoutez pas d'informations personnelles dans ce champ.", "Expense.PrivateNote": "Note privée", "Expense.Receipt": "Reçu", "expense.ReceiptRequired": "Reçu requis", @@ -1646,21 +1635,15 @@ "Expense.RequestDetails": "Détails de la requête", "Expense.RequestedBy": "Invité par {name}", "expense.requestReApproval.btn": "Demander une nouvelle approbation", - "Expense.SaveExpense": "Enregistrer la dépense", - "Expense.SaveReceipt": "Enregistrer le reçu", "expense.schedule.btn": "Planifier pour payer avec {paymentMethod}", "expense.scheduledForPayment": "Paiement programmé", "Expense.SeeDetails": "Voir les détails de la dépense", "Expense.SendInvite": "Envoyer une invitation", - "Expense.SignUpInfoBox": "Vous devez créer un compte pour recevoir un paiement de {collectiveName}. En cliquant sur \"Rejoindre et envoyer\" vous acceptez de créer un compte sur {WebsiteName}.", "expense.spam.notAllowed": "Vous ne pouvez pas marquer vos propres dépenses comme spam", "expense.status": "Statut", "Expense.Status.Refunded": "Remboursée", "Expense.Submitted": "Dépense envoyée", "Expense.SubmittedBy": "Postée par {name}", - "Expense.SuccessPage": "Vous pouvez modifier ou revoir les mises à jour sur cette page.", - "Expense.Summary.Recurring.CheckboxDescription": "Choisissez cette option pour soumettre automatiquement une copie de cette facture de manière périodique.", - "Expense.Summary.Recurring.CheckboxTitle": "Est-ce une dépense récurrente ?", "Expense.type": "Type", "expense.type": "Type", "Expense.Type.Charge": "Paiement par carte virtuelle", @@ -1677,16 +1660,12 @@ "ExpenseAmount": "Montant de la note de frais", "ExpenseApprover": "Approbateur de dépenses", "ExpenseForm.AddAttachedFile": "Ajouter un nouveau document", - "ExpenseForm.AddGrantItem": "Ajouter un élément de subvention", - "ExpenseForm.AddLineItem": "Ajouter un élément", - "ExpenseForm.AddReceipt": "Ajouter un reçu", "ExpenseForm.AddressLabel": "Adresse physique", "ExpenseForm.CancelEditExpense": "Annuler la modification", "ExpenseForm.ChooseCountry": "Choisissez un pays", "ExpenseForm.ClearExpenseForm": "Réinitialiser le formulaire", "ExpenseForm.ConfirmCancelEditExpense": "Êtes-vous sûr que vous voulez annuler les modifications ?", "ExpenseForm.ConfirmClearExpenseForm": "Êtes-vous sûr de vouloir réinitialiser ce formulaire de dépense ?", - "ExpenseForm.DescriptionPlaceholder": "Entrez le titre de la dépense ici...", "ExpenseForm.FundingRequestDescription": "Demandez une subvention pour votre projet ou votre initiative.", "ExpenseForm.GrantSubjectPlaceholder": "e.g., Research, software development, etc.", "ExpenseForm.inviteAdditionalInfo": "Vous voulez entrer les coordonnées de paiement, comme une adresse PayPal ou un compte bancaire ?", @@ -1707,12 +1686,7 @@ "ExpenseForm.SavePayout": "Enregistrer cette information pour les futurs remboursements", "ExpenseForm.SignUp.OrgAdminNote": "Vous devez être administrateur de l'Organisation pour soumettre des dépenses.", "ExpenseForm.SignUp.SignIn": "Nous utiliserons cette adresse électronique afin de créer votre compte. Si vous avez déjà un compte {loginLink}.", - "ExpenseForm.StepExpense": "Ajoutez un ou plusieurs reçus", - "ExpenseForm.StepExpenseFundingRequest": "Définir les détails de la subvention", - "ExpenseForm.StepExpenseInvoice": "Définir les détails de la facture", - "ExpenseForm.StepPayeeInvoice": "Informations sur le bénéficiaire", "ExpenseForm.Submit": "Envoyer une dépense", - "ExpenseForm.SubmitRequest": "Soumettre une requête", "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.", @@ -1747,7 +1721,6 @@ "ExpensesSubmittedByName": "Expenses submitted by {name}", "ExpenseSubmitters": "Soumetteur de dépenses", "ExpenseSummary.addNotesLabel": "Ajouter des notes", - "ExpenseSummaryTitle": "Résumé de {type, select, CHARGE {Frais} INVOICE {Facture} RECEIPT {Reçu} GRANT {Subvention} SETTLEMENT {Règlement} PLATFORM_BILLING {Facture de plateform} other {Dépense}} de {collectiveName}", "ExpenseTitle": "{type, select, CHARGE {Frais} INVOICE {Facture} RECEIPT {Reçu} GRANT {Subvention} SETTLEMENT {Règlement} PLATFORM_BILLING {Facture de plateform} other {Dépense}} {id} à {collectiveName}", "Explore": "Explorer", "Export.Format": "Exporter le {format}", @@ -1967,7 +1940,6 @@ "fiscalHosting.youngActivists.description": "Qui peuvent manquer d'expérience pour gérer leur propre entité.", "FiscalHostPolicies": "Politiques de l'Hôte fiscal", "fiscaltos": "conditions de parrainage fiscal", - "fIsGOi": "Non, continuer l'édition", "FJBnaq": "ID du client et secret client", "FKMNjH": "Vous ne pourrez plus reprendre la synchronisation à l'avenir.", "FLqc8O": "Carte virtuelle ajoutée", @@ -3347,7 +3319,6 @@ "PJubm9": "Le suivi financier et la transparence impliquent que les rapports se rédigent eux-même", "pKF7TO": "Exiger du contributeur qu'il indique son adresse postale lorsqu'il verse une somme supérieure à :", "Pkorog": "Account under verification", - "Pkq+ZR": "Veuillez vous assurer que tous les éléments de dépense dans cette dépense appartiennent à la catégorie de dépense sélectionnée. Si nécessaire, vous pouvez soumettre des articles supplémentaires dans des dépenses distinctes avec différentes catégories de dépenses.", "Pkx+Wj": "Partager les résultats", "platform": "Plateforme", "platform.explainerVideo": "Vidéo explicative", @@ -3381,8 +3352,6 @@ "PreviewFeatures.CollectiveOverview.Welcome.Description": "Nous avons créé cet espace pour vous permettre de rester au courant de tout ce qui se passe dans votre Collectif. Faites-nous savoir comment nous pouvons le rendre meilleur !", "PreviewFeatures.CollectiveOverview.Welcome.Title": "Bienvenue dans votre nouvelle vue d'ensemble", "PreviewFeatures.crowdfundingRedesignDescription": "Participez à la refonte du financement participatif et accédez en avant-première aux nouvelles pages et profils de financement participatif. Découvrez des pages de profil améliorées avec des vues distinctes pour la collecte de fonds et le storytelling, des relations plus claires entre les Collectifs et leurs projets, un suivi amélioré des objectifs et de meilleurs récits collectifs qui mettent en valeur votre impact et votre viabilité à long terme.{newLine}{newLine}Consultez l'article de blog pour plus de détails.\n\nTraduit avec DeepL.com (version gratuite)", - "PreviewFeatures.inlineEditExpenseDescription": "Modifier les détails des dépenses directement dans le tableau de bord sans naviguer vers des pages séparées.", - "PreviewFeatures.inlineEditExpenseTitle": "Modification des dépenses en ligne", "PreviewFeatures.keyboardShortcutsDescription": "Naviguez plus efficacement dans le flux de dépenses avec les raccourcis clavier. Accélérez votre flux de travail et réduisez la dépendance à la souris pour les actions courantes.", "PreviewFeatures.keyboardShortcutsExpenseDetailsAttachments": "{leftKey} and {rightKey} to navigate through attachments.", "PreviewFeatures.keyboardShortcutsExpenseDetailsClose": "{key} to close Expense details.", @@ -3396,8 +3365,6 @@ "PreviewFeatures.keyboardShortcutsExpenseListSecurity": "{key} to check Security alerts for selected Expense.", "PreviewFeatures.keyboardShortcutsTitle": "Raccourcis clavier", "PreviewFeatures.LimitedAccess": "Aperçu limité", - "PreviewFeatures.newExpenseFlowDescription": "Découvrez un processus amélioré de soumission des dépenses dans le tableau de bord, avec une meilleure expérience utilisateur, une navigation plus claire et une validation des formulaires optimisée.", - "PreviewFeatures.newExpenseFlowTitle": "Flux de soumission de nouvelles dépenses", "PreviewFeatures.publicBeta": "Bêta publique", "PreviewFeatures.searchCommandDescription": "Découvrez une nouvelle façon de rechercher des Collectifs, des transactions, des dépenses, et plus encore grâce à une interface intuitive dans le menu de commandes. Accédez plus rapidement aux informations grâce à de puissantes capacités de recherche.", "PreviewFeatures.searchCommandTitle": "Rechercher le menu de commande", @@ -3455,7 +3422,6 @@ "pricing.start.feeNote": "Vous ne serez pas en mesure de facturer des Collectifs ou de définir des frais d'Hébergement.", "pricing.title": "Notre structure tarifaire", "pricing.unlimitedCollectives": "Collectifs illimités", - "privacypolicy": "politique de confidentialité", "Private": "Privé", "PrivateCommentsMessage.Allowed": "Vos commentaires sont privés.", "PrivateCommentsMessage.AllowedDetails": "Les commentaires sur les dépenses sont privés, car ils contiennent parfois des informations confidentielles telles que les informations de paiement. Seul la personne qui a soumis la dépense et les administrateurs peuvent les voir.", @@ -4247,7 +4213,6 @@ "TopContributors.Individuals": "Personnes", "TopContributors.Organizations": "Organisations", "tos": "conditions d'utilisation", - "TOSAndPrivacyPolicyAgreement": "J'accepte les {toslink} et {privacylink} d'Open Collective.", "total": "total", "TotalAmount": "Montant total", "TotalAmountWithoutFee": "Montant total (sans frais)", @@ -4487,14 +4452,10 @@ "UpgradePlanCTA.title.DISABLED": "Mise à jour requise", "UpgradePlanCTA.title.UNSUPPORTED": "Fonctionnalité non prise en charge pour votre compte", "UpgradePlanCTA.upgradeButton": "Passer à un forfait supérieur", - "UploadDocumentation": "Télécharger un document", - "UploadDocumentationDescription": "Si vous voulez ajouter un document, vous pouvez le télécharger ici.", "uploadImage.isDragActive": "Glisser l'image", "uploadImage.isDragReject": "🚫 Ce type de fichier n'est pas accepté", "uploadImage.isUploading": "Téléchargement de l'image...", "uploadImage.sizeRejected": "La résolution de l'image doit être comprise entre {minResolution} et {maxResolution}. La taille du fichier doit être inférieure à {maxFileSize}.", - "UploadInvoice": "Téléverser une facture", - "UploadInvoiceDescription": "Si vous avez déjà une facture, téléchargez-la ici.", "uQguR/": "Tous les collectifs", "Uqkhct": "Aucune carte virtuelle", "uR+TjD": "Êtes-vous sûr de vouloir supprimer cette méthode de paiement ?", @@ -4556,8 +4517,6 @@ "VendorsAndOrganizations.Description": "Manage all the external organizations you work with as vendors and quickly surface all the activity between your hosted Collectives and other platform Organizations.", "vEQcqS": "Authentification à deux facteurs ajoutée", "verificationCriteria": "critères de vérification", - "VerifyEmailAddress": "Vérifiez votre adresse e-mail", - "VerifyEmailInstructions": "Un e-mail de vérification a été envoyé à {email}. Cliquez sur le lien pour compléter l'envoi de cette dépense. Si vous n'avez pas reçu l'e-mail, veuillez vérifier vos spams.", "VErmYl": "Dupliquer la subvention", "Vf1x2A": "A rejoint la plateforme le", "VfJsl4": "Bénéficiaire", @@ -4939,7 +4898,6 @@ "zNBAqh": "Cette valeur est une estimation. Veuillez définir le montant exact reçu si connu.", "ZNBeE4": "{amount} exactement", "ZNzyMo": "Exporter les taxes et les frais de traitement des paiements en colonnes", - "zO+Zv3": "Pièces jointes supplémentaires", "zoC8Gb": "Le bénéficiaire couvre les frais", "zOk9pq": "Aller au journal d'activité", "ZonfjV": "Vous ne pouvez pas modifier ce Collectif", diff --git a/lang/he.json b/lang/he.json index 8d8bd9ec4e3..ca30a9e6aba 100644 --- a/lang/he.json +++ b/lang/he.json @@ -30,7 +30,6 @@ "+RvjCt": "הכרטיס נמחק בהצלחה", "+S3jp9": "עדכון חדש", "+SSp9V": "We encountered an issue loading this {type,select,section{section}other{page}}. Please reload the page or contact support if the problem persists.", - "+t6c4i": "הנחיות קטגוריית חשבון", "+U6ozc": "סוג", "+UdXIM": "חשבון נוצר באמצעות GitHub", "+UwJxq": "דמי מארח צפויים", @@ -148,13 +147,11 @@ "2u8jmQ": "לאפשר מגבלת סכום לתשלומים עם אימות דו-שלבי", "2Un6+c": "KYC Information", "2uzHxT": "ייבא קובץ CSV", - "3135/i": "מטבע הוצעה", "322m9e": "הסיסמה חייבת להיות באורך של 10 תווים לפחות", "32rBNK": "Terms of Service", "34Up+l": "הצג עוד", "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "37zpJw": "Search countries...", - "38dzz9": "קטגורית הוצאה", "38fPNE": "הגדרות ייבוא ​​עסקאות", "3A7J9A": "לא הצלחנו למצוא מארח שתואם את כל הקריטריונים שלך.", "3ABdi3": "חשבון זה לא קיים", @@ -577,7 +574,6 @@ "AzGwgz": "The minimum amount for transferring to {selectedCurrency} is {minAmountForSelectedCurrency}", "AZmsFu": " rejected contribution from ", "AzRKUx": "Create Beneficiary", - "b++lom": "כן, ברצוני לבטל עריכה", "B+fVMt": "More than {amount}", "B+NQ+r": " drafted an {expenseDescription} on ", "b07w+D": "רמה", @@ -1174,7 +1170,6 @@ "CreatedAt": "Created At", "CreatedBy": "על ידי {name}", "CreateEvent": "Create event", - "CreateExpense.HelpCreateInfo": "דרישת תשלום מ{collective}. התשלום יעבור אישור של מנהלי הקבוצה והארגון. הסכום, תיאור, ושם הפרופיל יהיו פומביים, אבל צרופות, פרטי התשלום ומידע אישי נוסף יישאר חסוי.", "CreateExpenseFAQ.getPaid": "איך אפשר לקבל תשלום מ־Collective?", "CreateExpenseFAQ.getPaidDetails": "מילוי פרטים לקבלת תשלום.", "CreateExpenseFAQ.howAreApproved": "איך תשלומים עוברים לאישור?", @@ -1297,7 +1292,6 @@ "DhxIpu": "New comment on ", "DinF1w": "Net Amount for {collective}", "dIoEln": "Total disbursed", - "DismissableHelp.DontShowAgain": "בסדר, לא להציג לי שוב", "DisplayName": "Display Name", "DJnUUS": "This expense has not been approved", "dK5ItS": "By default only fiscal host administrators can submit expenses on behalf of vendors. You can allow other users who submit expenses to collectives you host to also submit expenses on behalf vendors.", @@ -1583,7 +1577,6 @@ "Expense.Direction.Received": "Received", "Expense.Direction.Submitted": "Submitted", "expense.draft": "Draft", - "Expense.edit": "עריכת הוצאה", "expense.editDetails": "Edit expense details", "expense.editNotes": "Edit notes", "expense.editPaidBy": "Edit paid by", @@ -1591,15 +1584,12 @@ "expense.editPayee": "Edit payee", "expense.editTitle": "Edit expense title", "expense.editType": "Edit expense type", - "Expense.EnterExpenseTitle": "שם ההוצאה: (פומבי)", - "Expense.EnterRequestSubject": "מקבל המענק (פומבי)", "Expense.GoToPage": "לצפיה בדף ההוצאה", "expense.hostFeeInCollectiveCurrency": "תקורה", "expense.incurredAt": "תאריך", "expense.invite.declined": "Invite declined", "Expense.InviteIsOnItsWay.Description": "הזמנה לבקשת תשלום עבור הוצאה זו נשלחה למייל {email}. כשיאשרו ויסיימה את התהליך זה יופיע ברשימת ההוצאות.", "Expense.InvoiceItems": "פריטים בדרישת התשלום", - "Expense.JoinAndSubmit": "הצטרפות והגשה", "expense.linked": "Expense linked", "expense.markAsPaid": "סימון כשולם", "Expense.MarkAsSpamLabel": "Why are you marking this expense as spam?", @@ -1628,7 +1618,6 @@ "Expense.PayTo": "לשלם לטובת", "expense.pending": "בטיפול", "expense.pickPayoutMethod": "Pick new payout method", - "Expense.PrivacyWarning": "המידע הזה פומבי, אין לכלול מידע פרטי בשדה זה.", "Expense.PrivateNote": "הערה פרטית", "Expense.Receipt": "Receipt", "expense.ReceiptRequired": "דרושה קבלה", @@ -1646,21 +1635,15 @@ "Expense.RequestDetails": "פרטי הבקשה", "Expense.RequestedBy": "Invited by {name}", "expense.requestReApproval.btn": "Request re-approval", - "Expense.SaveExpense": "Save Expense", - "Expense.SaveReceipt": "שמירת קבלה", "expense.schedule.btn": "תשלום מתוזמן באמצעות {paymentMethod}", "expense.scheduledForPayment": "תשלום מתוזמן", "Expense.SeeDetails": "See expense details", "Expense.SendInvite": "שליחת הזמנה", - "Expense.SignUpInfoBox": "You need to create an account to receive a payment from {collectiveName}, by clicking 'Join and Submit' you agree to create an account on {WebsiteName}.", "expense.spam.notAllowed": "You can't mark your own expenses as spam", "expense.status": "מצב", "Expense.Status.Refunded": "זיכוי בוצע", "Expense.Submitted": "הוצאה עודכנה", "Expense.SubmittedBy": "נשלח על-ידי {name}", - "Expense.SuccessPage": "באפשרותך לצפות ולערוך עדכונים בעמוד זה.", - "Expense.Summary.Recurring.CheckboxDescription": "בחירת אפשרות זו לשליחה אוטומטית של עותק מהדרישה הזו מדי תקופה.", - "Expense.Summary.Recurring.CheckboxTitle": "האם זו הוצאה מחזורית?", "Expense.type": "Type", "expense.type": "סוג", "Expense.Type.Charge": "Virtual Card Charge", @@ -1677,16 +1660,12 @@ "ExpenseAmount": "סכום ההוצאה", "ExpenseApprover": "Expense Approver", "ExpenseForm.AddAttachedFile": "הוספת מסמך חדש", - "ExpenseForm.AddGrantItem": "הוספת פריט למענק", - "ExpenseForm.AddLineItem": "הוספת פריט חדש", - "ExpenseForm.AddReceipt": "הוספת קבלה חדשה", "ExpenseForm.AddressLabel": "כתובת פיזית", "ExpenseForm.CancelEditExpense": "Cancel Edit", "ExpenseForm.ChooseCountry": "נא לבחור מדינה", "ExpenseForm.ClearExpenseForm": "Clear Form", "ExpenseForm.ConfirmCancelEditExpense": "Are you sure you want to cancel the edits?", "ExpenseForm.ConfirmClearExpenseForm": "Are you sure you want to clear the expense form?", - "ExpenseForm.DescriptionPlaceholder": "כאן כותבים את הכותרת/ שם של ההוצאה...", "ExpenseForm.FundingRequestDescription": "בקשה למענק עבור הפרויקט או היוזמה שלך.", "ExpenseForm.GrantSubjectPlaceholder": "e.g., Research, software development, etc.", "ExpenseForm.inviteAdditionalInfo": "להוסיף פרטים לאמצעי תשלום כגון פרטי חשבון בנק או חשבון פייפאל?", @@ -1707,12 +1686,7 @@ "ExpenseForm.SavePayout": "שמירת המידע לתשלומים עתידיים", "ExpenseForm.SignUp.OrgAdminNote": "עליך להיות מנהל הארגון כדי להזין הוצאות צפויות.", "ExpenseForm.SignUp.SignIn": "החשבון שלך יהיה על-שם כתובת המייל. אם כבר יש לך חשבון {loginLink}.", - "ExpenseForm.StepExpense": "העלאת קבצים", - "ExpenseForm.StepExpenseFundingRequest": "פרטים אודות המענק", - "ExpenseForm.StepExpenseInvoice": "פרטים לדרישת התשלום ללקוח", - "ExpenseForm.StepPayeeInvoice": "פרטים על הספק", "ExpenseForm.Submit": "עדכון הוצאה", - "ExpenseForm.SubmitRequest": "סיום ושליחת הבקשה", "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.", @@ -1747,7 +1721,6 @@ "ExpensesSubmittedByName": "Expenses submitted by {name}", "ExpenseSubmitters": "Expense Submitters", "ExpenseSummary.addNotesLabel": "הוספת הערות", - "ExpenseSummaryTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} Summary to {collectiveName}", "ExpenseTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} {id} to {collectiveName}", "Explore": "Explore", "Export.Format": "ייצוא בתור {format}", @@ -1967,7 +1940,6 @@ "fiscalHosting.youngActivists.description": "ייתכן ואין להם עדיין את הכישורים והמיומנויות לניהול ישות משפטית באופן עצמאי.", "FiscalHostPolicies": "Fiscal Host Policies", "fiscaltos": "תנאי החסות הכספית", - "fIsGOi": "לא, להמשך עריכה", "FJBnaq": "זיהוי לקוח וסוד לקוח", "FKMNjH": "You won't be able to resume the synchronization in the future.", "FLqc8O": "Virtual card added", @@ -3347,7 +3319,6 @@ "PJubm9": "המשמעות של מעקב כספי ושקיפות היא שהדוחות מופקים בזמן אמת ואוטומטית", "pKF7TO": "Require the contributor to provide their physical address when they contribute more than:", "Pkorog": "Account under verification", - "Pkq+ZR": "Please make sure that all the expense items in this expense belong to the selected expense category. If needed, you may submit additional items in separate expenses with different expense categories.", "Pkx+Wj": "שיתוף התוצאות", "platform": "פלטפורמה", "platform.explainerVideo": "מדריך וידאו", @@ -3381,8 +3352,6 @@ "PreviewFeatures.CollectiveOverview.Welcome.Description": "We’ve created this space for you to keep on top of everything happening in your Collective, please let us know how we can make it better!", "PreviewFeatures.CollectiveOverview.Welcome.Title": "Welcome to your new Collective Overview", "PreviewFeatures.crowdfundingRedesignDescription": "Be part of the crowdfunding redesign effort and get access to previews of new crowdfunding and profile pages. Experience enhanced profile pages with separate fundraising and storytelling views, clearer relationships between collectives and their projects, improved goal tracking, and better collective narratives that showcase your impact and long-term sustainability.{newLine}{newLine}Check out the blog post for more details.", - "PreviewFeatures.inlineEditExpenseDescription": "Edit expense details directly in the Dashboard without navigating to separate pages.", - "PreviewFeatures.inlineEditExpenseTitle": "Inline Expense Editing", "PreviewFeatures.keyboardShortcutsDescription": "Navigate the expense flow more efficiently with keyboard shortcuts. Speed up your workflow and reduce mouse dependency for common actions.", "PreviewFeatures.keyboardShortcutsExpenseDetailsAttachments": "{leftKey} and {rightKey} to navigate through attachments.", "PreviewFeatures.keyboardShortcutsExpenseDetailsClose": "{key} to close Expense details.", @@ -3396,8 +3365,6 @@ "PreviewFeatures.keyboardShortcutsExpenseListSecurity": "{key} to check Security alerts for selected Expense.", "PreviewFeatures.keyboardShortcutsTitle": "Keyboard Shortcuts", "PreviewFeatures.LimitedAccess": "Limited preview", - "PreviewFeatures.newExpenseFlowDescription": "Experience an improved expense submission flow in the Dashboard with better user experience, clearer navigation, and enhanced form validation.", - "PreviewFeatures.newExpenseFlowTitle": "New Expense Submission Flow", "PreviewFeatures.publicBeta": "Public beta", "PreviewFeatures.searchCommandDescription": "Discover a new way to search for collectives, transactions, expenses, and more through an intuitive command menu interface. Access information faster with powerful search capabilities.", "PreviewFeatures.searchCommandTitle": "Search Command Menu", @@ -3455,7 +3422,6 @@ "pricing.start.feeNote": "לא תוכלו לגבות תקורות מהקבוצות או להגדיר עמלות ארגון-גג.", "pricing.title": "צורות תמחור ותשלום למערכת", "pricing.unlimitedCollectives": "אין מגבלה על מספר הקבוצות", - "privacypolicy": "מדיניות פרטיות", "Private": "Private", "PrivateCommentsMessage.Allowed": "התגובות שלך פרטיות.", "PrivateCommentsMessage.AllowedDetails": "Expenses comments are private, because they sometimes contain confidential information such as payment details. Only the expense submitter, the admins and the accountants can see them.", @@ -4247,7 +4213,6 @@ "TopContributors.Individuals": "יחידים", "TopContributors.Organizations": "ארגונים", "tos": "תנאי השירות", - "TOSAndPrivacyPolicyAgreement": "מסכים/ה ל{toslink} וה{privacylink} של Open Collective.", "total": "total", "TotalAmount": "סכום כולל", "TotalAmountWithoutFee": "סה\"כ (ללא עמלות)", @@ -4487,14 +4452,10 @@ "UpgradePlanCTA.title.DISABLED": "Upgrade Required", "UpgradePlanCTA.title.UNSUPPORTED": "Feature not supported for your account", "UpgradePlanCTA.upgradeButton": "Upgrade your plan", - "UploadDocumentation": "העלאת מסמכים", - "UploadDocumentationDescription": "אם בצונך לצרף מסמכים, אפשר להעלות כאן.", "uploadImage.isDragActive": "זרקו את זה כמו גחל לוהט 🔥", "uploadImage.isDragReject": "🚫 סוג קובץ זה אינו מורשה", "uploadImage.isUploading": "מעלה תמונה...", "uploadImage.sizeRejected": "התמונה צריכה להיות ברזולוציה שבין {minResolution} ל-{maxResolution}, וגודל קובץ מקסימלי {maxFileSize}.", - "UploadInvoice": "העלאת חשבונית/ דרישת תשלום", - "UploadInvoiceDescription": "If you already have an invoice document, you can upload it here.", "uQguR/": "כל הקבוצות", "Uqkhct": "No virtual cards", "uR+TjD": "Are you sure you want to delete this payout method?", @@ -4556,8 +4517,6 @@ "VendorsAndOrganizations.Description": "Manage all the external organizations you work with as vendors and quickly surface all the activity between your hosted Collectives and other platform Organizations.", "vEQcqS": "Two factor authentication added", "verificationCriteria": "קריטריון אימות", - "VerifyEmailAddress": "אימות כתובת מייל", - "VerifyEmailInstructions": "כתובת מייל לאימות נשלחה לכתובת {email}. נא ללחוץ על הלינק כדי להמשיך בעריכת דרישת התשלום. אם לא קיבלת את המייל, נא לבדוק בספאם.", "VErmYl": "Duplicate grant", "Vf1x2A": "Joined the platform on", "VfJsl4": "Beneficiary", @@ -4939,7 +4898,6 @@ "zNBAqh": "This value is an estimate. Please set the exact amount received if known.", "ZNBeE4": "Exactly {amount}", "ZNzyMo": "Export taxes and payment processor fees as columns", - "zO+Zv3": "Additional attachments", "zoC8Gb": "המוטב משלם את העמלות", "zOk9pq": "Go to activity log", "ZonfjV": "אין באפשרותך לערוך קבוצה זו", diff --git a/lang/it.json b/lang/it.json index 9acb058bc46..2a57842a5ba 100644 --- a/lang/it.json +++ b/lang/it.json @@ -30,7 +30,6 @@ "+RvjCt": "Carta eliminata con successo", "+S3jp9": "Nuovo aggiornamento", "+SSp9V": "We encountered an issue loading this {type,select,section{section}other{page}}. Please reload the page or contact support if the problem persists.", - "+t6c4i": "Istruzioni categoria conto", "+U6ozc": "Tipo", "+UdXIM": " creato attraverso GitHub", "+UwJxq": "Commissioni dal Host attese", @@ -148,13 +147,11 @@ "2u8jmQ": "Attivare il limite cumulativo 2FA per pagamenti", "2Un6+c": "KYC Information", "2uzHxT": "Importa CSV", - "3135/i": "Valuta spese", "322m9e": "Il messaggio deve essere lungo almeno 10 caratteri", "32rBNK": "Termini di Servizio", "34Up+l": "Mostra di più", "35Jfcr": "Le carte regalo consentono ai tuoi dipendenti o membri della comunità di supportare i progetti open source che amano. Scopri di più.", "37zpJw": "Cerca paesi...", - "38dzz9": "Categoria di spesa", "38fPNE": "Impostazioni Importazione Transazioni", "3A7J9A": "Non siamo riusciti a trovare un Ospite Fiscale compatibile con i tuoi criteri.", "3ABdi3": "L'account non esiste", @@ -577,7 +574,6 @@ "AzGwgz": "L'importo minimo per il trasferimento in {selectedCurrency} è di {minAmountForSelectedCurrency}", "AZmsFu": " rejected contribution from ", "AzRKUx": "Create Beneficiary", - "b++lom": "Sì, annulla le modifiche", "B+fVMt": "Più di {amount}", "B+NQ+r": " drafted an {expenseDescription} on ", "b07w+D": "Livello", @@ -1174,7 +1170,6 @@ "CreatedAt": "Created At", "CreatedBy": "da {name}", "CreateEvent": "Create event", - "CreateExpense.HelpCreateInfo": "Request payment from {collective}. Expenses will be processed once approved by a Collective admin. The amount, description, and your profile name are public, but attachments, payment details, and other personal info is kept private.", "CreateExpenseFAQ.getPaid": "Come faccio a farmi pagare da un collettivo?", "CreateExpenseFAQ.getPaidDetails": "Submit an expense and provide your payment information.", "CreateExpenseFAQ.howAreApproved": "Come vengono approvate le spese?", @@ -1297,7 +1292,6 @@ "DhxIpu": "New comment on ", "DinF1w": "Net Amount for {collective}", "dIoEln": "Total disbursed", - "DismissableHelp.DontShowAgain": "Ok, don’t show me again", "DisplayName": "Display Name", "DJnUUS": "This expense has not been approved", "dK5ItS": "By default only fiscal host administrators can submit expenses on behalf of vendors. You can allow other users who submit expenses to collectives you host to also submit expenses on behalf vendors.", @@ -1583,7 +1577,6 @@ "Expense.Direction.Received": "Ricevuti", "Expense.Direction.Submitted": "Submitted", "expense.draft": "Bozza", - "Expense.edit": "Edit expense", "expense.editDetails": "Edit expense details", "expense.editNotes": "Edit notes", "expense.editPaidBy": "Edit paid by", @@ -1591,15 +1584,12 @@ "expense.editPayee": "Edit payee", "expense.editTitle": "Edit expense title", "expense.editType": "Edit expense type", - "Expense.EnterExpenseTitle": "Expense title (Public)", - "Expense.EnterRequestSubject": "Enter grant subject (Public)", "Expense.GoToPage": "Go to expense page", "expense.hostFeeInCollectiveCurrency": "host fee", "expense.incurredAt": "Data", "expense.invite.declined": "Invite declined", "Expense.InviteIsOnItsWay.Description": "An invitation to submit this expense has been sent to {email}. Once they confirm and finish the process, it will appear on the expenses list.", "Expense.InvoiceItems": "Invoice items", - "Expense.JoinAndSubmit": "Join and Submit", "expense.linked": "Expense linked", "expense.markAsPaid": "Segna come pagato", "Expense.MarkAsSpamLabel": "Why are you marking this expense as spam?", @@ -1628,7 +1618,6 @@ "Expense.PayTo": "Paga a", "expense.pending": "In attesa", "expense.pickPayoutMethod": "Pick new payout method", - "Expense.PrivacyWarning": "This information is public. Do not put any private details in this field.", "Expense.PrivateNote": "Nota privata", "Expense.Receipt": "Receipt", "expense.ReceiptRequired": "Ricevuta necessaria", @@ -1646,21 +1635,15 @@ "Expense.RequestDetails": "Request Details", "Expense.RequestedBy": "Invited by {name}", "expense.requestReApproval.btn": "Request re-approval", - "Expense.SaveExpense": "Save Expense", - "Expense.SaveReceipt": "Salva ricevuta", "expense.schedule.btn": "Schedule to Pay with {paymentMethod}", "expense.scheduledForPayment": "Scheduled for payment", "Expense.SeeDetails": "See expense details", "Expense.SendInvite": "Invia Invito", - "Expense.SignUpInfoBox": "You need to create an account to receive a payment from {collectiveName}, by clicking 'Join and Submit' you agree to create an account on {WebsiteName}.", "expense.spam.notAllowed": "You can't mark your own expenses as spam", "expense.status": "Stato", "Expense.Status.Refunded": "Rimborsato", "Expense.Submitted": "Expense submitted", "Expense.SubmittedBy": "Submitted by {name}", - "Expense.SuccessPage": "You can edit or review updates on this page.", - "Expense.Summary.Recurring.CheckboxDescription": "Choose this option to automatically submit a copy of this invoice on a periodic basis.", - "Expense.Summary.Recurring.CheckboxTitle": "Is this a recurring expense?", "Expense.type": "Type", "expense.type": "Tipo", "Expense.Type.Charge": "Virtual Card Charge", @@ -1677,16 +1660,12 @@ "ExpenseAmount": "Expense amount", "ExpenseApprover": "Expense Approver", "ExpenseForm.AddAttachedFile": "Aggiungi nuovo documento", - "ExpenseForm.AddGrantItem": "Add grant item", - "ExpenseForm.AddLineItem": "Add new item", - "ExpenseForm.AddReceipt": "Add new receipt", "ExpenseForm.AddressLabel": "Physical address", "ExpenseForm.CancelEditExpense": "Cancel Edit", "ExpenseForm.ChooseCountry": "Choose country", "ExpenseForm.ClearExpenseForm": "Clear Form", "ExpenseForm.ConfirmCancelEditExpense": "Are you sure you want to cancel the edits?", "ExpenseForm.ConfirmClearExpenseForm": "Are you sure you want to clear the expense form?", - "ExpenseForm.DescriptionPlaceholder": "Enter expense title here...", "ExpenseForm.FundingRequestDescription": "Request a grant for your project or initiative.", "ExpenseForm.GrantSubjectPlaceholder": "e.g., Research, software development, etc.", "ExpenseForm.inviteAdditionalInfo": "Want to enter payout details, such as a PayPal address or bank account?", @@ -1707,12 +1686,7 @@ "ExpenseForm.SavePayout": "Salva queste informazioni per i pagamenti futuri", "ExpenseForm.SignUp.OrgAdminNote": "You need to be an admin of the Organization to submit expenses.", "ExpenseForm.SignUp.SignIn": "We will use this email to create your account. If you already have an account {loginLink}.", - "ExpenseForm.StepExpense": "Upload one or multiple receipt", - "ExpenseForm.StepExpenseFundingRequest": "Set grant details", - "ExpenseForm.StepExpenseInvoice": "Set invoice details", - "ExpenseForm.StepPayeeInvoice": "Payee information", "ExpenseForm.Submit": "Invia spese", - "ExpenseForm.SubmitRequest": "Invia richiesta", "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.", @@ -1747,7 +1721,6 @@ "ExpensesSubmittedByName": "Expenses submitted by {name}", "ExpenseSubmitters": "Expense Submitters", "ExpenseSummary.addNotesLabel": "Aggiungi note", - "ExpenseSummaryTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} other {Expense}} Summary to {collectiveName}", "ExpenseTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} other {Expense}} {id} to {collectiveName}", "Explore": "Explore", "Export.Format": "Esporta {format}", @@ -1967,7 +1940,6 @@ "fiscalHosting.youngActivists.description": "Who may lack the experience to manage their own legal entity.", "FiscalHostPolicies": "Fiscal Host Policies", "fiscaltos": "terms of fiscal sponsorship", - "fIsGOi": "No, continue editing", "FJBnaq": "Client ID and client secret", "FKMNjH": "You won't be able to resume the synchronization in the future.", "FLqc8O": "Carta virtuale aggiunta", @@ -3347,7 +3319,6 @@ "PJubm9": "Financial tracking and transparency means reporting writes itself", "pKF7TO": "Require the contributor to provide their physical address when they contribute more than:", "Pkorog": "Account under verification", - "Pkq+ZR": "Please make sure that all the expense items in this expense belong to the selected expense category. If needed, you may submit additional items in separate expenses with different expense categories.", "Pkx+Wj": "Condividi i risultati", "platform": "Platform", "platform.explainerVideo": "Explainer video", @@ -3381,8 +3352,6 @@ "PreviewFeatures.CollectiveOverview.Welcome.Description": "We’ve created this space for you to keep on top of everything happening in your Collective, please let us know how we can make it better!", "PreviewFeatures.CollectiveOverview.Welcome.Title": "Welcome to your new Collective Overview", "PreviewFeatures.crowdfundingRedesignDescription": "Be part of the crowdfunding redesign effort and get access to previews of new crowdfunding and profile pages. Experience enhanced profile pages with separate fundraising and storytelling views, clearer relationships between collectives and their projects, improved goal tracking, and better collective narratives that showcase your impact and long-term sustainability.{newLine}{newLine}Check out the blog post for more details.", - "PreviewFeatures.inlineEditExpenseDescription": "Edit expense details directly in the Dashboard without navigating to separate pages.", - "PreviewFeatures.inlineEditExpenseTitle": "Inline Expense Editing", "PreviewFeatures.keyboardShortcutsDescription": "Navigate the expense flow more efficiently with keyboard shortcuts. Speed up your workflow and reduce mouse dependency for common actions.", "PreviewFeatures.keyboardShortcutsExpenseDetailsAttachments": "{leftKey} and {rightKey} to navigate through attachments.", "PreviewFeatures.keyboardShortcutsExpenseDetailsClose": "{key} to close Expense details.", @@ -3396,8 +3365,6 @@ "PreviewFeatures.keyboardShortcutsExpenseListSecurity": "{key} to check Security alerts for selected Expense.", "PreviewFeatures.keyboardShortcutsTitle": "Keyboard Shortcuts", "PreviewFeatures.LimitedAccess": "Limited preview", - "PreviewFeatures.newExpenseFlowDescription": "Experience an improved expense submission flow in the Dashboard with better user experience, clearer navigation, and enhanced form validation.", - "PreviewFeatures.newExpenseFlowTitle": "New Expense Submission Flow", "PreviewFeatures.publicBeta": "Public beta", "PreviewFeatures.searchCommandDescription": "Discover a new way to search for collectives, transactions, expenses, and more through an intuitive command menu interface. Access information faster with powerful search capabilities.", "PreviewFeatures.searchCommandTitle": "Search Command Menu", @@ -3455,7 +3422,6 @@ "pricing.start.feeNote": "You wont be able to charge Collectives or set any Host Fee.", "pricing.title": "Our Pricing Structure", "pricing.unlimitedCollectives": "Unlimited Collectives", - "privacypolicy": "privacy policy", "Private": "Private", "PrivateCommentsMessage.Allowed": "I tuoi commenti sono privati.", "PrivateCommentsMessage.AllowedDetails": "Expenses comments are private, because they sometimes contain confidential information such as payment details. Only the expense submitter, the admins and the accountants can see them.", @@ -4247,7 +4213,6 @@ "TopContributors.Individuals": "Individuals", "TopContributors.Organizations": "Organizzazioni", "tos": "termini di servizio", - "TOSAndPrivacyPolicyAgreement": "I agree with the {toslink} and {privacylink} of Open Collective.", "total": "total", "TotalAmount": "Importo totale", "TotalAmountWithoutFee": "Importo totale (senza commissioni)", @@ -4487,14 +4452,10 @@ "UpgradePlanCTA.title.DISABLED": "Upgrade Required", "UpgradePlanCTA.title.UNSUPPORTED": "Feature not supported for your account", "UpgradePlanCTA.upgradeButton": "Upgrade your plan", - "UploadDocumentation": "Upload documentation", - "UploadDocumentationDescription": "If you want to include any documentation, you can upload it here.", "uploadImage.isDragActive": "Drop it like it's hot 🔥", "uploadImage.isDragReject": "🚫 This file type is not accepted", "uploadImage.isUploading": "Caricamento dell'immagine in corso...", "uploadImage.sizeRejected": "Image resolution needs to be between {minResolution} and {maxResolution}. File size must be below {maxFileSize}.", - "UploadInvoice": "Upload invoice", - "UploadInvoiceDescription": "If you already have an invoice document, you can upload it here.", "uQguR/": "All Collectives", "Uqkhct": "No virtual cards", "uR+TjD": "Are you sure you want to delete this payout method?", @@ -4556,8 +4517,6 @@ "VendorsAndOrganizations.Description": "Manage all the external organizations you work with as vendors and quickly surface all the activity between your hosted Collectives and other platform Organizations.", "vEQcqS": "Two factor authentication added", "verificationCriteria": "criteri di verifica", - "VerifyEmailAddress": "Verifica il tuo indirizzo email", - "VerifyEmailInstructions": "A verification email has been sent to {email}. Click the link to complete submitting this expense. If you have not received the email, please check your spam.", "VErmYl": "Duplicate grant", "Vf1x2A": "Joined the platform on", "VfJsl4": "Beneficiary", @@ -4939,7 +4898,6 @@ "zNBAqh": "This value is an estimate. Please set the exact amount received if known.", "ZNBeE4": "Exactly {amount}", "ZNzyMo": "Export taxes and payment processor fees as columns", - "zO+Zv3": "Additional attachments", "zoC8Gb": "The payee is covering the fees", "zOk9pq": "Go to activity log", "ZonfjV": "You cannot edit this collective", diff --git a/lang/ja.json b/lang/ja.json index 67fcd8da994..3eb76190b2e 100644 --- a/lang/ja.json +++ b/lang/ja.json @@ -30,7 +30,6 @@ "+RvjCt": "カードが正常に削除されました。", "+S3jp9": "New Update", "+SSp9V": "We encountered an issue loading this {type,select,section{section}other{page}}. Please reload the page or contact support if the problem persists.", - "+t6c4i": "Account Category Instructions", "+U6ozc": "Type", "+UdXIM": " がGitHub経由で作成されました", "+UwJxq": "Expected Host Fees", @@ -148,13 +147,11 @@ "2u8jmQ": "Enable rolling limit 2FA for payouts", "2Un6+c": "KYC Information", "2uzHxT": "Import CSV", - "3135/i": "Expense Currency", "322m9e": "メッセージは10文字以上にする必要があります。", "32rBNK": "Terms of Service", "34Up+l": "View more", "35Jfcr": "ギフトカードは、社員やコミュニティメンバーが自分の好きなオープンソースプロジェクトを支援できるようにします。\n詳しく見る", "37zpJw": "Search countries...", - "38dzz9": "Expense Category", "38fPNE": "Transaction Import Settings", "3A7J9A": "We could not find a host that matches all your criteria.", "3ABdi3": "そのアカウントは存在しません。", @@ -577,7 +574,6 @@ "AzGwgz": "The minimum amount for transferring to {selectedCurrency} is {minAmountForSelectedCurrency}", "AZmsFu": " rejected contribution from ", "AzRKUx": "Create Beneficiary", - "b++lom": "はい、編集をキャンセルします", "B+fVMt": "More than {amount}", "B+NQ+r": " drafted an {expenseDescription} on ", "b07w+D": "Tier", @@ -1174,7 +1170,6 @@ "CreatedAt": "Created At", "CreatedBy": "{name}", "CreateEvent": "Create event", - "CreateExpense.HelpCreateInfo": "{collective} に支払いをリクエストします。コレクティブの管理者がその支払いを承認すると、経費として処理されます。金額、説明、プロフィールに登録した表示名は公開されますが、添付ファイル、支払いの詳細、およびその他の個人情報は公開されません。", "CreateExpenseFAQ.getPaid": "コレクティブから支払いを受けるにはどうすればよいですか?", "CreateExpenseFAQ.getPaidDetails": "経費を申請し、支払いに必要な情報を提供してください。", "CreateExpenseFAQ.howAreApproved": "経費はどのようにして承認されるのですか?", @@ -1297,7 +1292,6 @@ "DhxIpu": "New comment on ", "DinF1w": "Net Amount for {collective}", "dIoEln": "Total disbursed", - "DismissableHelp.DontShowAgain": "今後は表示しない", "DisplayName": "Display Name", "DJnUUS": "This expense has not been approved", "dK5ItS": "By default only fiscal host administrators can submit expenses on behalf of vendors. You can allow other users who submit expenses to collectives you host to also submit expenses on behalf vendors.", @@ -1583,7 +1577,6 @@ "Expense.Direction.Received": "請求を受けた分", "Expense.Direction.Submitted": "請求を行った分", "expense.draft": "Draft", - "Expense.edit": "経費を編集", "expense.editDetails": "Edit expense details", "expense.editNotes": "Edit notes", "expense.editPaidBy": "Edit paid by", @@ -1591,15 +1584,12 @@ "expense.editPayee": "Edit payee", "expense.editTitle": "Edit expense title", "expense.editType": "Edit expense type", - "Expense.EnterExpenseTitle": "経費名 (公開)", - "Expense.EnterRequestSubject": "Enter grant subject (Public)", "Expense.GoToPage": "この経費の詳細ページへ移動", "expense.hostFeeInCollectiveCurrency": "ホスト手数料", "expense.incurredAt": "日付", "expense.invite.declined": "Invite declined", "Expense.InviteIsOnItsWay.Description": "An invitation to submit this expense has been sent to {email}. Once they confirm and finish the process, it will appear on the expenses list.", "Expense.InvoiceItems": "請求項目", - "Expense.JoinAndSubmit": "Join and Submit", "expense.linked": "Expense linked", "expense.markAsPaid": "支払い済みにする", "Expense.MarkAsSpamLabel": "Why are you marking this expense as spam?", @@ -1628,7 +1618,6 @@ "Expense.PayTo": "支払先", "expense.pending": "保留中", "expense.pickPayoutMethod": "Pick new payout method", - "Expense.PrivacyWarning": "この情報は公開されます。この項目には個人情報を記載しないでください。", "Expense.PrivateNote": "管理者宛ての通信・備考欄", "Expense.Receipt": "Receipt", "expense.ReceiptRequired": "Receipt required", @@ -1646,21 +1635,15 @@ "Expense.RequestDetails": "Request Details", "Expense.RequestedBy": "Invited by {name}", "expense.requestReApproval.btn": "Request re-approval", - "Expense.SaveExpense": "経費の情報を保存", - "Expense.SaveReceipt": "領収書を保存", "expense.schedule.btn": "Schedule to Pay with {paymentMethod}", "expense.scheduledForPayment": "Scheduled for payment", "Expense.SeeDetails": "See expense details", "Expense.SendInvite": "Send Invite", - "Expense.SignUpInfoBox": "You need to create an account to receive a payment from {collectiveName}, by clicking 'Join and Submit' you agree to create an account on {WebsiteName}.", "expense.spam.notAllowed": "You can't mark your own expenses as spam", "expense.status": "状態", "Expense.Status.Refunded": "Refunded", "Expense.Submitted": "Expense submitted", "Expense.SubmittedBy": "{name} が提出", - "Expense.SuccessPage": "You can edit or review updates on this page.", - "Expense.Summary.Recurring.CheckboxDescription": "このオプションを選択すると、請求書のコピーを定期的に自動で提出します。", - "Expense.Summary.Recurring.CheckboxTitle": "これは繰り返し生じる経費の申請ですか?", "Expense.type": "Type", "expense.type": "請求の種類", "Expense.Type.Charge": "Virtual Card Charge", @@ -1677,16 +1660,12 @@ "ExpenseAmount": "Expense amount", "ExpenseApprover": "Expense Approver", "ExpenseForm.AddAttachedFile": "新しいドキュメントを追加", - "ExpenseForm.AddGrantItem": "Add grant item", - "ExpenseForm.AddLineItem": "新しい項目を追加", - "ExpenseForm.AddReceipt": "新しいレシートを追加", "ExpenseForm.AddressLabel": "Physical address", "ExpenseForm.CancelEditExpense": "編集をキャンセル", "ExpenseForm.ChooseCountry": "国", "ExpenseForm.ClearExpenseForm": "記入内容を消去", "ExpenseForm.ConfirmCancelEditExpense": "編集をキャンセルしてもよろしいですか?", "ExpenseForm.ConfirmClearExpenseForm": "本当に経費申請フォームの入力内容を消去してもよろしいですか?", - "ExpenseForm.DescriptionPlaceholder": "ここに経費名を入力してください...", "ExpenseForm.FundingRequestDescription": "Request a grant for your project or initiative.", "ExpenseForm.GrantSubjectPlaceholder": "e.g., Research, software development, etc.", "ExpenseForm.inviteAdditionalInfo": "Want to enter payout details, such as a PayPal address or bank account?", @@ -1707,12 +1686,7 @@ "ExpenseForm.SavePayout": "Save this info for future payouts", "ExpenseForm.SignUp.OrgAdminNote": "You need to be an admin of the Organization to submit expenses.", "ExpenseForm.SignUp.SignIn": "We will use this email to create your account. If you already have an account {loginLink}.", - "ExpenseForm.StepExpense": "Upload one or multiple receipt", - "ExpenseForm.StepExpenseFundingRequest": "Set grant details", - "ExpenseForm.StepExpenseInvoice": "請求書の詳細設定", - "ExpenseForm.StepPayeeInvoice": "支払先情報", "ExpenseForm.Submit": "経費を申請", - "ExpenseForm.SubmitRequest": "Submit request", "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.", @@ -1747,7 +1721,6 @@ "ExpensesSubmittedByName": "Expenses submitted by {name}", "ExpenseSubmitters": "Expense Submitters", "ExpenseSummary.addNotesLabel": "メモを追加", - "ExpenseSummaryTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} Summary to {collectiveName}", "ExpenseTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} {id} to {collectiveName}", "Explore": "探索", "Export.Format": "エクスポート {format}", @@ -1967,7 +1940,6 @@ "fiscalHosting.youngActivists.description": "法人を自ら運営する経験が十分ではないかもしれない、若手の活動家やチェンジメーカー", "FiscalHostPolicies": "Fiscal Host Policies", "fiscaltos": "terms of fiscal sponsorship", - "fIsGOi": "いいえ、編集を続けます", "FJBnaq": "Client ID and client secret", "FKMNjH": "You won't be able to resume the synchronization in the future.", "FLqc8O": "バーチャル カードを追加しました", @@ -3347,7 +3319,6 @@ "PJubm9": "財務の管理と透明性のためのレポートを自動作成", "pKF7TO": "Require the contributor to provide their physical address when they contribute more than:", "Pkorog": "Account under verification", - "Pkq+ZR": "Please make sure that all the expense items in this expense belong to the selected expense category. If needed, you may submit additional items in separate expenses with different expense categories.", "Pkx+Wj": "結果を共有", "platform": "プラットフォーム", "platform.explainerVideo": "解説動画", @@ -3381,8 +3352,6 @@ "PreviewFeatures.CollectiveOverview.Welcome.Description": "We’ve created this space for you to keep on top of everything happening in your Collective, please let us know how we can make it better!", "PreviewFeatures.CollectiveOverview.Welcome.Title": "Welcome to your new Collective Overview", "PreviewFeatures.crowdfundingRedesignDescription": "Be part of the crowdfunding redesign effort and get access to previews of new crowdfunding and profile pages. Experience enhanced profile pages with separate fundraising and storytelling views, clearer relationships between collectives and their projects, improved goal tracking, and better collective narratives that showcase your impact and long-term sustainability.{newLine}{newLine}Check out the blog post for more details.", - "PreviewFeatures.inlineEditExpenseDescription": "Edit expense details directly in the Dashboard without navigating to separate pages.", - "PreviewFeatures.inlineEditExpenseTitle": "Inline Expense Editing", "PreviewFeatures.keyboardShortcutsDescription": "Navigate the expense flow more efficiently with keyboard shortcuts. Speed up your workflow and reduce mouse dependency for common actions.", "PreviewFeatures.keyboardShortcutsExpenseDetailsAttachments": "{leftKey} and {rightKey} to navigate through attachments.", "PreviewFeatures.keyboardShortcutsExpenseDetailsClose": "{key} to close Expense details.", @@ -3396,8 +3365,6 @@ "PreviewFeatures.keyboardShortcutsExpenseListSecurity": "{key} to check Security alerts for selected Expense.", "PreviewFeatures.keyboardShortcutsTitle": "Keyboard Shortcuts", "PreviewFeatures.LimitedAccess": "Limited preview", - "PreviewFeatures.newExpenseFlowDescription": "Experience an improved expense submission flow in the Dashboard with better user experience, clearer navigation, and enhanced form validation.", - "PreviewFeatures.newExpenseFlowTitle": "New Expense Submission Flow", "PreviewFeatures.publicBeta": "Public beta", "PreviewFeatures.searchCommandDescription": "Discover a new way to search for collectives, transactions, expenses, and more through an intuitive command menu interface. Access information faster with powerful search capabilities.", "PreviewFeatures.searchCommandTitle": "Search Command Menu", @@ -3455,7 +3422,6 @@ "pricing.start.feeNote": "You wont be able to charge Collectives or set any Host Fee.", "pricing.title": "価格構成", "pricing.unlimitedCollectives": "Unlimited Collectives", - "privacypolicy": "privacy policy", "Private": "Private", "PrivateCommentsMessage.Allowed": "コメントは非公開です。", "PrivateCommentsMessage.AllowedDetails": "Expenses comments are private, because they sometimes contain confidential information such as payment details. Only the expense submitter, the admins and the accountants can see them.", @@ -4247,7 +4213,6 @@ "TopContributors.Individuals": "個人", "TopContributors.Organizations": "組織", "tos": "利用規約", - "TOSAndPrivacyPolicyAgreement": "I agree with the {toslink} and {privacylink} of Open Collective.", "total": "total", "TotalAmount": "合計金額", "TotalAmountWithoutFee": "合計金額 (手数料抜き)", @@ -4487,14 +4452,10 @@ "UpgradePlanCTA.title.DISABLED": "Upgrade Required", "UpgradePlanCTA.title.UNSUPPORTED": "Feature not supported for your account", "UpgradePlanCTA.upgradeButton": "Upgrade your plan", - "UploadDocumentation": "Upload documentation", - "UploadDocumentationDescription": "If you want to include any documentation, you can upload it here.", "uploadImage.isDragActive": "Drop it like it's hot 🔥", "uploadImage.isDragReject": "🚫 この種類のファイルは受け付けていません", "uploadImage.isUploading": "Uploading image...", "uploadImage.sizeRejected": "Image resolution needs to be between {minResolution} and {maxResolution}. File size must be below {maxFileSize}.", - "UploadInvoice": "請求書のアップロード", - "UploadInvoiceDescription": "すでに請求書をお持ちの場合は、こちらからアップロードできます。", "uQguR/": "All Collectives", "Uqkhct": "No virtual cards", "uR+TjD": "Are you sure you want to delete this payout method?", @@ -4556,8 +4517,6 @@ "VendorsAndOrganizations.Description": "Manage all the external organizations you work with as vendors and quickly surface all the activity between your hosted Collectives and other platform Organizations.", "vEQcqS": "Two factor authentication added", "verificationCriteria": "承認基準", - "VerifyEmailAddress": "メールアドレス認証", - "VerifyEmailInstructions": "A verification email has been sent to {email}. Click the link to complete submitting this expense. If you have not received the email, please check your spam.", "VErmYl": "Duplicate grant", "Vf1x2A": "Joined the platform on", "VfJsl4": "Beneficiary", @@ -4939,7 +4898,6 @@ "zNBAqh": "This value is an estimate. Please set the exact amount received if known.", "ZNBeE4": "Exactly {amount}", "ZNzyMo": "Export taxes and payment processor fees as columns", - "zO+Zv3": "Additional attachments", "zoC8Gb": "The payee is covering the fees", "zOk9pq": "Go to activity log", "ZonfjV": "You cannot edit this collective", diff --git a/lang/ko.json b/lang/ko.json index 830416769f8..37103ad3447 100644 --- a/lang/ko.json +++ b/lang/ko.json @@ -30,7 +30,6 @@ "+RvjCt": "카드를 삭제했어요", "+S3jp9": "새로운 업데이트", "+SSp9V": "We encountered an issue loading this {type,select,section{section}other{page}}. Please reload the page or contact support if the problem persists.", - "+t6c4i": "계정 분류 지침", "+U6ozc": "유형", "+UdXIM": "GitHub을 사용해 를 생성했어요", "+UwJxq": "예상 호스트 수수료", @@ -148,13 +147,11 @@ "2u8jmQ": "Enable rolling limit 2FA for payouts", "2Un6+c": "KYC Information", "2uzHxT": "CSV 가져오기", - "3135/i": "Expense Currency", "322m9e": "메시지는 10자 이상이어야 해요", "32rBNK": "Terms of Service", "34Up+l": "더보기", "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "37zpJw": "Search countries...", - "38dzz9": "지출 카테고리", "38fPNE": "Transaction Import Settings", "3A7J9A": "해당 키워드로 찾을 수 있는 호스트가 없어요.", "3ABdi3": "이 계정은 존재하지 않아요", @@ -577,7 +574,6 @@ "AzGwgz": "The minimum amount for transferring to {selectedCurrency} is {minAmountForSelectedCurrency}", "AZmsFu": " rejected contribution from ", "AzRKUx": "Create Beneficiary", - "b++lom": "Yes, cancel editing", "B+fVMt": "More than {amount}", "B+NQ+r": " drafted an {expenseDescription} on ", "b07w+D": "Tier", @@ -1174,7 +1170,6 @@ "CreatedAt": "Created At", "CreatedBy": "by {name}", "CreateEvent": "Create event", - "CreateExpense.HelpCreateInfo": "Request payment from {collective}. Expenses will be processed once approved by a Collective admin. The amount, description, and your profile name are public, but attachments, payment details, and other personal info is kept private.", "CreateExpenseFAQ.getPaid": "How do I get paid from a Collective?", "CreateExpenseFAQ.getPaidDetails": "Submit an expense and provide your payment information.", "CreateExpenseFAQ.howAreApproved": "How are expenses approved?", @@ -1297,7 +1292,6 @@ "DhxIpu": "New comment on ", "DinF1w": "Net Amount for {collective}", "dIoEln": "Total disbursed", - "DismissableHelp.DontShowAgain": "Ok, don’t show me again", "DisplayName": "Display Name", "DJnUUS": "This expense has not been approved", "dK5ItS": "By default only fiscal host administrators can submit expenses on behalf of vendors. You can allow other users who submit expenses to collectives you host to also submit expenses on behalf vendors.", @@ -1583,7 +1577,6 @@ "Expense.Direction.Received": "Received", "Expense.Direction.Submitted": "Submitted", "expense.draft": "Draft", - "Expense.edit": "경비 수정", "expense.editDetails": "Edit expense details", "expense.editNotes": "Edit notes", "expense.editPaidBy": "Edit paid by", @@ -1591,15 +1584,12 @@ "expense.editPayee": "Edit payee", "expense.editTitle": "Edit expense title", "expense.editType": "Edit expense type", - "Expense.EnterExpenseTitle": "Expense title (Public)", - "Expense.EnterRequestSubject": "Enter grant subject (Public)", "Expense.GoToPage": "Go to expense page", "expense.hostFeeInCollectiveCurrency": "호스트 수수료", "expense.incurredAt": "날짜", "expense.invite.declined": "Invite declined", "Expense.InviteIsOnItsWay.Description": "An invitation to submit this expense has been sent to {email}. Once they confirm and finish the process, it will appear on the expenses list.", "Expense.InvoiceItems": "청구서 항목", - "Expense.JoinAndSubmit": "Join and Submit", "expense.linked": "Expense linked", "expense.markAsPaid": "Mark as paid", "Expense.MarkAsSpamLabel": "Why are you marking this expense as spam?", @@ -1628,7 +1618,6 @@ "Expense.PayTo": "지불", "expense.pending": "대기 중", "expense.pickPayoutMethod": "Pick new payout method", - "Expense.PrivacyWarning": "이 정보는 모든 사용자에게 공개돼요. 이름이나 주소 등 민감할 수 있는 정보는 넣지 마세요.", "Expense.PrivateNote": "비공개 메모", "Expense.Receipt": "Receipt", "expense.ReceiptRequired": "Receipt required", @@ -1646,21 +1635,15 @@ "Expense.RequestDetails": "Request Details", "Expense.RequestedBy": "Invited by {name}", "expense.requestReApproval.btn": "Request re-approval", - "Expense.SaveExpense": "Save Expense", - "Expense.SaveReceipt": "Save Receipt", "expense.schedule.btn": "Schedule to Pay with {paymentMethod}", "expense.scheduledForPayment": "Scheduled for payment", "Expense.SeeDetails": "See expense details", "Expense.SendInvite": "초대 보내기", - "Expense.SignUpInfoBox": "You need to create an account to receive a payment from {collectiveName}, by clicking 'Join and Submit' you agree to create an account on {WebsiteName}.", "expense.spam.notAllowed": "You can't mark your own expenses as spam", "expense.status": "상태", "Expense.Status.Refunded": "Refunded", "Expense.Submitted": "비용 요청 제출됨", "Expense.SubmittedBy": "Submitted by {name}", - "Expense.SuccessPage": "You can edit or review updates on this page.", - "Expense.Summary.Recurring.CheckboxDescription": "Choose this option to automatically submit a copy of this invoice on a periodic basis.", - "Expense.Summary.Recurring.CheckboxTitle": "Is this a recurring expense?", "Expense.type": "Type", "expense.type": "유형", "Expense.Type.Charge": "Virtual Card Charge", @@ -1677,16 +1660,12 @@ "ExpenseAmount": "Expense amount", "ExpenseApprover": "Expense Approver", "ExpenseForm.AddAttachedFile": "Add new document", - "ExpenseForm.AddGrantItem": "Add grant item", - "ExpenseForm.AddLineItem": "새 항목 추가", - "ExpenseForm.AddReceipt": "Add new receipt", "ExpenseForm.AddressLabel": "Physical address", "ExpenseForm.CancelEditExpense": "Cancel Edit", "ExpenseForm.ChooseCountry": "국가 선택", "ExpenseForm.ClearExpenseForm": "Clear Form", "ExpenseForm.ConfirmCancelEditExpense": "Are you sure you want to cancel the edits?", "ExpenseForm.ConfirmClearExpenseForm": "Are you sure you want to clear the expense form?", - "ExpenseForm.DescriptionPlaceholder": "Enter expense title here...", "ExpenseForm.FundingRequestDescription": "Request a grant for your project or initiative.", "ExpenseForm.GrantSubjectPlaceholder": "e.g., Research, software development, etc.", "ExpenseForm.inviteAdditionalInfo": "Want to enter payout details, such as a PayPal address or bank account?", @@ -1707,12 +1686,7 @@ "ExpenseForm.SavePayout": "Save this info for future payouts", "ExpenseForm.SignUp.OrgAdminNote": "You need to be an admin of the Organization to submit expenses.", "ExpenseForm.SignUp.SignIn": "We will use this email to create your account. If you already have an account {loginLink}.", - "ExpenseForm.StepExpense": "Upload one or multiple receipt", - "ExpenseForm.StepExpenseFundingRequest": "Set grant details", - "ExpenseForm.StepExpenseInvoice": "청구서 세부정보 설정", - "ExpenseForm.StepPayeeInvoice": "수취인 정보", "ExpenseForm.Submit": "경비 제출", - "ExpenseForm.SubmitRequest": "요청 제출", "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.", @@ -1747,7 +1721,6 @@ "ExpensesSubmittedByName": "Expenses submitted by {name}", "ExpenseSubmitters": "Expense Submitters", "ExpenseSummary.addNotesLabel": "메모 추가", - "ExpenseSummaryTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} Summary to {collectiveName}", "ExpenseTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} {id} to {collectiveName}", "Explore": "Explore", "Export.Format": "Export {format}", @@ -1967,7 +1940,6 @@ "fiscalHosting.youngActivists.description": "Who may lack the experience to manage their own legal entity.", "FiscalHostPolicies": "Fiscal Host Policies", "fiscaltos": "terms of fiscal sponsorship", - "fIsGOi": "No, continue editing", "FJBnaq": "Client ID and client secret", "FKMNjH": "You won't be able to resume the synchronization in the future.", "FLqc8O": "Virtual card added", @@ -3347,7 +3319,6 @@ "PJubm9": "Financial tracking and transparency means reporting writes itself", "pKF7TO": "Require the contributor to provide their physical address when they contribute more than:", "Pkorog": "Account under verification", - "Pkq+ZR": "Please make sure that all the expense items in this expense belong to the selected expense category. If needed, you may submit additional items in separate expenses with different expense categories.", "Pkx+Wj": "Share results", "platform": "플랫폼", "platform.explainerVideo": "설명 영상", @@ -3381,8 +3352,6 @@ "PreviewFeatures.CollectiveOverview.Welcome.Description": "Open Collective에서 일어나는 모든 일을 파악할 수 있도록 이 공간을 만들었으니, 어떻게 하면 더 나은 공간이 될 수 있을지 알려주세요!", "PreviewFeatures.CollectiveOverview.Welcome.Title": "새로운 단체에 오신 것을 환영합니다", "PreviewFeatures.crowdfundingRedesignDescription": "Be part of the crowdfunding redesign effort and get access to previews of new crowdfunding and profile pages. Experience enhanced profile pages with separate fundraising and storytelling views, clearer relationships between collectives and their projects, improved goal tracking, and better collective narratives that showcase your impact and long-term sustainability.{newLine}{newLine}Check out the blog post for more details.", - "PreviewFeatures.inlineEditExpenseDescription": "Edit expense details directly in the Dashboard without navigating to separate pages.", - "PreviewFeatures.inlineEditExpenseTitle": "Inline Expense Editing", "PreviewFeatures.keyboardShortcutsDescription": "Navigate the expense flow more efficiently with keyboard shortcuts. Speed up your workflow and reduce mouse dependency for common actions.", "PreviewFeatures.keyboardShortcutsExpenseDetailsAttachments": "{leftKey} and {rightKey} to navigate through attachments.", "PreviewFeatures.keyboardShortcutsExpenseDetailsClose": "{key} to close Expense details.", @@ -3396,8 +3365,6 @@ "PreviewFeatures.keyboardShortcutsExpenseListSecurity": "{key} to check Security alerts for selected Expense.", "PreviewFeatures.keyboardShortcutsTitle": "Keyboard Shortcuts", "PreviewFeatures.LimitedAccess": "제한된 프리뷰", - "PreviewFeatures.newExpenseFlowDescription": "Experience an improved expense submission flow in the Dashboard with better user experience, clearer navigation, and enhanced form validation.", - "PreviewFeatures.newExpenseFlowTitle": "New Expense Submission Flow", "PreviewFeatures.publicBeta": "공개 베타", "PreviewFeatures.searchCommandDescription": "Discover a new way to search for collectives, transactions, expenses, and more through an intuitive command menu interface. Access information faster with powerful search capabilities.", "PreviewFeatures.searchCommandTitle": "Search Command Menu", @@ -3455,7 +3422,6 @@ "pricing.start.feeNote": "You wont be able to charge Collectives or set any Host Fee.", "pricing.title": "Our Pricing Structure", "pricing.unlimitedCollectives": "Unlimited Collectives", - "privacypolicy": "개인정보취급방침", "Private": "Private", "PrivateCommentsMessage.Allowed": "Your comments are private.", "PrivateCommentsMessage.AllowedDetails": "Expenses comments are private, because they sometimes contain confidential information such as payment details. Only the expense submitter, the admins and the accountants can see them.", @@ -4247,7 +4213,6 @@ "TopContributors.Individuals": "개인", "TopContributors.Organizations": "조직", "tos": "이용 약관", - "TOSAndPrivacyPolicyAgreement": "Open Collective의 {toslink}와 {privacylink}에 동의합니다.", "total": "total", "TotalAmount": "전체 금액", "TotalAmountWithoutFee": "Total amount (without fees)", @@ -4487,14 +4452,10 @@ "UpgradePlanCTA.title.DISABLED": "Upgrade Required", "UpgradePlanCTA.title.UNSUPPORTED": "Feature not supported for your account", "UpgradePlanCTA.upgradeButton": "Upgrade your plan", - "UploadDocumentation": "Upload documentation", - "UploadDocumentationDescription": "If you want to include any documentation, you can upload it here.", "uploadImage.isDragActive": "Drop it like it's hot 🔥", "uploadImage.isDragReject": "🚫 This file type is not accepted", "uploadImage.isUploading": "Uploading image...", "uploadImage.sizeRejected": "Image resolution needs to be between {minResolution} and {maxResolution}. File size must be below {maxFileSize}.", - "UploadInvoice": "청구서 업로드", - "UploadInvoiceDescription": "If you already have an invoice document, you can upload it here.", "uQguR/": "All Collectives", "Uqkhct": "No virtual cards", "uR+TjD": "Are you sure you want to delete this payout method?", @@ -4556,8 +4517,6 @@ "VendorsAndOrganizations.Description": "Manage all the external organizations you work with as vendors and quickly surface all the activity between your hosted Collectives and other platform Organizations.", "vEQcqS": "Two factor authentication added", "verificationCriteria": "verification criteria", - "VerifyEmailAddress": "이메일 주소 인증", - "VerifyEmailInstructions": "{email}으로 확인 이메일을 보냈어요. 이 청구의 제출을 완료하려면 해당 링크를 클릭하세요. 이메일을 받지 못했나요? 정크 폴더나 스팸 폴더를 확인해 보세요.", "VErmYl": "Duplicate grant", "Vf1x2A": "Joined the platform on", "VfJsl4": "Beneficiary", @@ -4939,7 +4898,6 @@ "zNBAqh": "This value is an estimate. Please set the exact amount received if known.", "ZNBeE4": "Exactly {amount}", "ZNzyMo": "Export taxes and payment processor fees as columns", - "zO+Zv3": "Additional attachments", "zoC8Gb": "The payee is covering the fees", "zOk9pq": "Go to activity log", "ZonfjV": "You cannot edit this collective", diff --git a/lang/nl.json b/lang/nl.json index d13fa3fe866..5fb384e476a 100644 --- a/lang/nl.json +++ b/lang/nl.json @@ -30,7 +30,6 @@ "+RvjCt": "Kaart succesvol verwijderd", "+S3jp9": "Nieuwe update", "+SSp9V": "We encountered an issue loading this {type,select,section{section}other{page}}. Please reload the page or contact support if the problem persists.", - "+t6c4i": "Account Category Instructions", "+U6ozc": "Type", "+UdXIM": " aangemaakt via GitHub", "+UwJxq": "Verwachte hostkosten", @@ -148,13 +147,11 @@ "2u8jmQ": "Enable rolling limit 2FA for payouts", "2Un6+c": "KYC Information", "2uzHxT": "Importeer CSV", - "3135/i": "Expense Currency", "322m9e": "Bericht moet ten minste 10 tekens lang zijn", "32rBNK": "Gebruikersvoorwaarden", "34Up+l": "Bekijk meer", "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "37zpJw": "Search countries...", - "38dzz9": "Expense Category", "38fPNE": "Transaction Import Settings", "3A7J9A": "We konden geen host vinden voor al je criteria.", "3ABdi3": "Dit account bestaat niet", @@ -577,7 +574,6 @@ "AzGwgz": "Het minimale bedrag voor een overboeking naar {selectedCurrency} is {minAmountForSelectedCurrency}", "AZmsFu": " rejected contribution from ", "AzRKUx": "Create Beneficiary", - "b++lom": "Ja, annuleer", "B+fVMt": "Meer dan {amount}", "B+NQ+r": " drafted an {expenseDescription} on ", "b07w+D": "Niveau", @@ -1174,7 +1170,6 @@ "CreatedAt": "Created At", "CreatedBy": "door {name}", "CreateEvent": "Create event", - "CreateExpense.HelpCreateInfo": "Request payment from {collective}. Expenses will be processed once approved by a Collective admin. The amount, description, and your profile name are public, but attachments, payment details, and other personal info is kept private.", "CreateExpenseFAQ.getPaid": "How do I get paid from a Collective?", "CreateExpenseFAQ.getPaidDetails": "Submit an expense and provide your payment information.", "CreateExpenseFAQ.howAreApproved": "How are expenses approved?", @@ -1297,7 +1292,6 @@ "DhxIpu": "New comment on ", "DinF1w": "Netto bedrag voor {collective}", "dIoEln": "Total disbursed", - "DismissableHelp.DontShowAgain": "Ok, laat dit niet meer zien", "DisplayName": "Display Name", "DJnUUS": "This expense has not been approved", "dK5ItS": "By default only fiscal host administrators can submit expenses on behalf of vendors. You can allow other users who submit expenses to collectives you host to also submit expenses on behalf vendors.", @@ -1583,7 +1577,6 @@ "Expense.Direction.Received": "Ontvangen", "Expense.Direction.Submitted": "Ingeleverd", "expense.draft": "Concept", - "Expense.edit": "Uitgave bewerken", "expense.editDetails": "Edit expense details", "expense.editNotes": "Edit notes", "expense.editPaidBy": "Edit paid by", @@ -1591,15 +1584,12 @@ "expense.editPayee": "Edit payee", "expense.editTitle": "Edit expense title", "expense.editType": "Edit expense type", - "Expense.EnterExpenseTitle": "Expense title (Public)", - "Expense.EnterRequestSubject": "Enter grant subject (Public)", "Expense.GoToPage": "Go to expense page", "expense.hostFeeInCollectiveCurrency": "host fee", "expense.incurredAt": "Datum", "expense.invite.declined": "Invite declined", "Expense.InviteIsOnItsWay.Description": "An invitation to submit this expense has been sent to {email}. Once they confirm and finish the process, it will appear on the expenses list.", "Expense.InvoiceItems": "Invoice items", - "Expense.JoinAndSubmit": "Join and Submit", "expense.linked": "Expense linked", "expense.markAsPaid": "Markeren als betaald", "Expense.MarkAsSpamLabel": "Why are you marking this expense as spam?", @@ -1628,7 +1618,6 @@ "Expense.PayTo": "Betalen aan", "expense.pending": "In behandeling", "expense.pickPayoutMethod": "Pick new payout method", - "Expense.PrivacyWarning": "Deze informatie is openbaar. Plaats geen privégegevens in dit veld.", "Expense.PrivateNote": "Privénotitie", "Expense.Receipt": "Ontvangstbewijs", "expense.ReceiptRequired": "Ontvangstbewijs vereist", @@ -1646,21 +1635,15 @@ "Expense.RequestDetails": "Details aanvragen", "Expense.RequestedBy": "Uitgenodigd door {name}", "expense.requestReApproval.btn": "Request re-approval", - "Expense.SaveExpense": "Save Expense", - "Expense.SaveReceipt": "Ontvangstbewijs opslaan", "expense.schedule.btn": "Plan om te betalen met {paymentMethod}", "expense.scheduledForPayment": "Gepland voor betaling", "Expense.SeeDetails": "See expense details", "Expense.SendInvite": "Verstuur uitnodiging", - "Expense.SignUpInfoBox": "You need to create an account to receive a payment from {collectiveName}, by clicking 'Join and Submit' you agree to create an account on {WebsiteName}.", "expense.spam.notAllowed": "You can't mark your own expenses as spam", "expense.status": "Status", "Expense.Status.Refunded": "Terugbetaald", "Expense.Submitted": "Expense submitted", "Expense.SubmittedBy": "Verzonden door {name}", - "Expense.SuccessPage": "You can edit or review updates on this page.", - "Expense.Summary.Recurring.CheckboxDescription": "Choose this option to automatically submit a copy of this invoice on a periodic basis.", - "Expense.Summary.Recurring.CheckboxTitle": "Is this a recurring expense?", "Expense.type": "Type", "expense.type": "Type", "Expense.Type.Charge": "Virtual Card Charge", @@ -1677,16 +1660,12 @@ "ExpenseAmount": "Expense amount", "ExpenseApprover": "Expense Approver", "ExpenseForm.AddAttachedFile": "Nieuw document toevoegen", - "ExpenseForm.AddGrantItem": "Add grant item", - "ExpenseForm.AddLineItem": "Nieuw item toevoegen", - "ExpenseForm.AddReceipt": "Nieuw betaalbewijs toevoegen", "ExpenseForm.AddressLabel": "Fysiek adres", "ExpenseForm.CancelEditExpense": "Bewerken annuleren", "ExpenseForm.ChooseCountry": "Land kiezen", "ExpenseForm.ClearExpenseForm": "Formulier wissen", "ExpenseForm.ConfirmCancelEditExpense": "Are you sure you want to cancel the edits?", "ExpenseForm.ConfirmClearExpenseForm": "Are you sure you want to clear the expense form?", - "ExpenseForm.DescriptionPlaceholder": "Enter expense title here...", "ExpenseForm.FundingRequestDescription": "Request a grant for your project or initiative.", "ExpenseForm.GrantSubjectPlaceholder": "e.g., Research, software development, etc.", "ExpenseForm.inviteAdditionalInfo": "Want to enter payout details, such as a PayPal address or bank account?", @@ -1707,12 +1686,7 @@ "ExpenseForm.SavePayout": "Sla deze informatie op voor toekomstige uitbetalingen", "ExpenseForm.SignUp.OrgAdminNote": "You need to be an admin of the Organization to submit expenses.", "ExpenseForm.SignUp.SignIn": "We will use this email to create your account. If you already have an account {loginLink}.", - "ExpenseForm.StepExpense": "Upload one or multiple receipt", - "ExpenseForm.StepExpenseFundingRequest": "Set grant details", - "ExpenseForm.StepExpenseInvoice": "Set invoice details", - "ExpenseForm.StepPayeeInvoice": "Payee information", "ExpenseForm.Submit": "Submit expense", - "ExpenseForm.SubmitRequest": "Aanvraag verzenden", "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.", @@ -1747,7 +1721,6 @@ "ExpensesSubmittedByName": "Expenses submitted by {name}", "ExpenseSubmitters": "Expense Submitters", "ExpenseSummary.addNotesLabel": "Notities toevoegen", - "ExpenseSummaryTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} Summary to {collectiveName}", "ExpenseTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} {id} to {collectiveName}", "Explore": "Ontdekken", "Export.Format": "Exporteer {format}", @@ -1967,7 +1940,6 @@ "fiscalHosting.youngActivists.description": "Who may lack the experience to manage their own legal entity.", "FiscalHostPolicies": "Fiscal Host Policies", "fiscaltos": "terms of fiscal sponsorship", - "fIsGOi": "Nee, doorgaan met bewerken", "FJBnaq": "Client ID and client secret", "FKMNjH": "You won't be able to resume the synchronization in the future.", "FLqc8O": "Virtuele kaart toegevoegd", @@ -3347,7 +3319,6 @@ "PJubm9": "Financial tracking and transparency means reporting writes itself", "pKF7TO": "Require the contributor to provide their physical address when they contribute more than:", "Pkorog": "Account under verification", - "Pkq+ZR": "Please make sure that all the expense items in this expense belong to the selected expense category. If needed, you may submit additional items in separate expenses with different expense categories.", "Pkx+Wj": "Resultaten delen", "platform": "Platform", "platform.explainerVideo": "Uitlegvideo", @@ -3381,8 +3352,6 @@ "PreviewFeatures.CollectiveOverview.Welcome.Description": "We’ve created this space for you to keep on top of everything happening in your Collective, please let us know how we can make it better!", "PreviewFeatures.CollectiveOverview.Welcome.Title": "Welcome to your new Collective Overview", "PreviewFeatures.crowdfundingRedesignDescription": "Be part of the crowdfunding redesign effort and get access to previews of new crowdfunding and profile pages. Experience enhanced profile pages with separate fundraising and storytelling views, clearer relationships between collectives and their projects, improved goal tracking, and better collective narratives that showcase your impact and long-term sustainability.{newLine}{newLine}Check out the blog post for more details.", - "PreviewFeatures.inlineEditExpenseDescription": "Edit expense details directly in the Dashboard without navigating to separate pages.", - "PreviewFeatures.inlineEditExpenseTitle": "Inline Expense Editing", "PreviewFeatures.keyboardShortcutsDescription": "Navigate the expense flow more efficiently with keyboard shortcuts. Speed up your workflow and reduce mouse dependency for common actions.", "PreviewFeatures.keyboardShortcutsExpenseDetailsAttachments": "{leftKey} and {rightKey} to navigate through attachments.", "PreviewFeatures.keyboardShortcutsExpenseDetailsClose": "{key} to close Expense details.", @@ -3396,8 +3365,6 @@ "PreviewFeatures.keyboardShortcutsExpenseListSecurity": "{key} to check Security alerts for selected Expense.", "PreviewFeatures.keyboardShortcutsTitle": "Keyboard Shortcuts", "PreviewFeatures.LimitedAccess": "Limited preview", - "PreviewFeatures.newExpenseFlowDescription": "Experience an improved expense submission flow in the Dashboard with better user experience, clearer navigation, and enhanced form validation.", - "PreviewFeatures.newExpenseFlowTitle": "New Expense Submission Flow", "PreviewFeatures.publicBeta": "Public beta", "PreviewFeatures.searchCommandDescription": "Discover a new way to search for collectives, transactions, expenses, and more through an intuitive command menu interface. Access information faster with powerful search capabilities.", "PreviewFeatures.searchCommandTitle": "Search Command Menu", @@ -3455,7 +3422,6 @@ "pricing.start.feeNote": "You wont be able to charge Collectives or set any Host Fee.", "pricing.title": "Onze kostenstructuur", "pricing.unlimitedCollectives": "Unlimited Collectives", - "privacypolicy": "privacybeleid", "Private": "Privé", "PrivateCommentsMessage.Allowed": "Je opmerkingen zijn privé.", "PrivateCommentsMessage.AllowedDetails": "Expenses comments are private, because they sometimes contain confidential information such as payment details. Only the expense submitter, the admins and the accountants can see them.", @@ -4247,7 +4213,6 @@ "TopContributors.Individuals": "Individuen", "TopContributors.Organizations": "Organisaties", "tos": "gebruiksvoorwaarden", - "TOSAndPrivacyPolicyAgreement": "I agree with the {toslink} and {privacylink} of Open Collective.", "total": "totaal", "TotalAmount": "Totale hoeveelheid", "TotalAmountWithoutFee": "Total amount (without fees)", @@ -4487,14 +4452,10 @@ "UpgradePlanCTA.title.DISABLED": "Upgrade Required", "UpgradePlanCTA.title.UNSUPPORTED": "Feature not supported for your account", "UpgradePlanCTA.upgradeButton": "Upgrade your plan", - "UploadDocumentation": "Documentatie uploaden", - "UploadDocumentationDescription": "If you want to include any documentation, you can upload it here.", "uploadImage.isDragActive": "Drop it like it's hot 🔥", "uploadImage.isDragReject": "🚫 Dit bestandstype wordt niet geaccepteerd", "uploadImage.isUploading": "Afbeelding uploaden...", "uploadImage.sizeRejected": "Image resolution needs to be between {minResolution} and {maxResolution}. File size must be below {maxFileSize}.", - "UploadInvoice": "Upload invoice", - "UploadInvoiceDescription": "If you already have an invoice document, you can upload it here.", "uQguR/": "All Collectives", "Uqkhct": "Geen virtuele kaarten", "uR+TjD": "Weet u zeker dat u deze uitbetalingsmethode wilt verwijderen?", @@ -4556,8 +4517,6 @@ "VendorsAndOrganizations.Description": "Manage all the external organizations you work with as vendors and quickly surface all the activity between your hosted Collectives and other platform Organizations.", "vEQcqS": "Two factor authentication added", "verificationCriteria": "verificatie criteria", - "VerifyEmailAddress": "Verifieer je e-mailadres", - "VerifyEmailInstructions": "A verification email has been sent to {email}. Click the link to complete submitting this expense. If you have not received the email, please check your spam.", "VErmYl": "Duplicate grant", "Vf1x2A": "Joined the platform on", "VfJsl4": "Beneficiary", @@ -4939,7 +4898,6 @@ "zNBAqh": "This value is an estimate. Please set the exact amount received if known.", "ZNBeE4": "Exact {amount}", "ZNzyMo": "Export taxes and payment processor fees as columns", - "zO+Zv3": "Additional attachments", "zoC8Gb": "The payee is covering the fees", "zOk9pq": "Go to activity log", "ZonfjV": "You cannot edit this collective", diff --git a/lang/pl.json b/lang/pl.json index f7895d5a182..80598af3d26 100644 --- a/lang/pl.json +++ b/lang/pl.json @@ -30,7 +30,6 @@ "+RvjCt": "Karta pomyślnie usunięta", "+S3jp9": "Nowa aktualizacja", "+SSp9V": "We encountered an issue loading this {type,select,section{section}other{page}}. Please reload the page or contact support if the problem persists.", - "+t6c4i": "Instrukcje dotyczące kategorii konta", "+U6ozc": "Typ", "+UdXIM": " utworzone przez GitHub", "+UwJxq": "Przewidywana opłata za gospodarza", @@ -148,13 +147,11 @@ "2u8jmQ": "Włącz ograniczenie 2FA dla wypłat", "2Un6+c": "KYC Information", "2uzHxT": "Importuj plik CSV", - "3135/i": "Waluta wydatków", "322m9e": "Wiadomość musi mieć co najmniej 10 znaków", "32rBNK": "Warunki świadczenia usługi", "34Up+l": "Pokaż więcej", "35Jfcr": "Karty podarunkowe umożliwiają Twoim pracownikom lub członkom społeczności wspieranie projektów open source, które kochają. Dowiedz się więcej.", "37zpJw": "Wyszukaj kraje...", - "38dzz9": "Kategoria wydatku", "38fPNE": "Ustawienia importu transakcji", "3A7J9A": "Nie mogliśmy znaleźć gospodarza, który spełnia wszystkie Twoje kryteria.", "3ABdi3": "Podane konto nie istnieje", @@ -577,7 +574,6 @@ "AzGwgz": "Minimalna kwota dla przelewu dla {selectedCurrency} to {minAmountForSelectedCurrency}", "AZmsFu": " rejected contribution from ", "AzRKUx": "Create Beneficiary", - "b++lom": "Tak, anuluj edytowanie", "B+fVMt": "More than {amount}", "B+NQ+r": " drafted an {expenseDescription} on ", "b07w+D": "Poziom", @@ -1174,7 +1170,6 @@ "CreatedAt": "Data utworzenia", "CreatedBy": "przez {name}", "CreateEvent": "Utwórz wydarzenie", - "CreateExpense.HelpCreateInfo": "Zażądaj płatności od {collective}. Wydatki będą przetwarzane po zatwierdzeniu przez administratora kolektywu. Kwota, opis i nazwa profilu są publiczne, ale załączniki, szczegóły płatności i inne dane osobowe pozostają prywatne.", "CreateExpenseFAQ.getPaid": "Jak mogę otrzymać zapłatę od kolektywu?", "CreateExpenseFAQ.getPaidDetails": "Zgłoś wydatek i podaj informacje dotyczące wypłaty.", "CreateExpenseFAQ.howAreApproved": "Jak zatwierdza się wydatki?", @@ -1297,7 +1292,6 @@ "DhxIpu": "Nowy komentarz do ", "DinF1w": "Kwota netto dla {collective}", "dIoEln": "Total disbursed", - "DismissableHelp.DontShowAgain": "Ok, nie pokazuj ponownie", "DisplayName": "Display Name", "DJnUUS": "This expense has not been approved", "dK5ItS": "By default only fiscal host administrators can submit expenses on behalf of vendors. You can allow other users who submit expenses to collectives you host to also submit expenses on behalf vendors.", @@ -1583,7 +1577,6 @@ "Expense.Direction.Received": "Otrzymane", "Expense.Direction.Submitted": "Zatwierdzone", "expense.draft": "Wersja Robocza", - "Expense.edit": "Edytuj wydatek", "expense.editDetails": "Edit expense details", "expense.editNotes": "Edit notes", "expense.editPaidBy": "Opłata edytowana przez", @@ -1591,15 +1584,12 @@ "expense.editPayee": "Edit payee", "expense.editTitle": "Edit expense title", "expense.editType": "Edit expense type", - "Expense.EnterExpenseTitle": "Tytuł wydatku (Publiczny)", - "Expense.EnterRequestSubject": "Wpisz temat dotacji (Publiczny)", "Expense.GoToPage": "Przejdź do strony wydatków", "expense.hostFeeInCollectiveCurrency": "opłata gospodarza", "expense.incurredAt": "Data", "expense.invite.declined": "Zaproszenie odrzucone", "Expense.InviteIsOnItsWay.Description": "Na adres {email} zostało wysłane zaproszenie do złożenia tego wydatku. Po potwierdzeniu i zakończeniu procesu, wydatek pojawi się na liście wydatków.", "Expense.InvoiceItems": "Pozycje faktury", - "Expense.JoinAndSubmit": "Dołącz i wyślij", "expense.linked": "Wydatek powiązany", "expense.markAsPaid": "Oznacz jako zapłacone", "Expense.MarkAsSpamLabel": "Why are you marking this expense as spam?", @@ -1628,7 +1618,6 @@ "Expense.PayTo": "Zapłata dla", "expense.pending": "Oczekujące", "expense.pickPayoutMethod": "Pick new payout method", - "Expense.PrivacyWarning": "Ta informacja jest publiczna. Nie wpisuj żadnych prywatnych danych w tym polu.", "Expense.PrivateNote": "Notatka prywatna", "Expense.Receipt": "Receipt", "expense.ReceiptRequired": "Wymagany rachunek", @@ -1646,21 +1635,15 @@ "Expense.RequestDetails": "Szczegóły żądania", "Expense.RequestedBy": "Invited by {name}", "expense.requestReApproval.btn": "Request re-approval", - "Expense.SaveExpense": "Save Expense", - "Expense.SaveReceipt": "Zapisz rachunek", "expense.schedule.btn": "Zaplanuj płatność za pomocą {paymentMethod}", "expense.scheduledForPayment": "Zaplanowane płatności", "Expense.SeeDetails": "See expense details", "Expense.SendInvite": "Wyślij zaproszenie", - "Expense.SignUpInfoBox": "Musisz utworzyć konto, aby otrzymać płatność od {collectiveName}, klikając 'Dołącz i wyślij' akceptujesz utworzenie konta w {WebsiteName}.", "expense.spam.notAllowed": "You can't mark your own expenses as spam", "expense.status": "Status", "Expense.Status.Refunded": "Zwrócone", "Expense.Submitted": "Wydatki przesłane", "Expense.SubmittedBy": "Przesłane przez {name}", - "Expense.SuccessPage": "Możesz edytować lub przeglądać aktualizacje na tej stronie.", - "Expense.Summary.Recurring.CheckboxDescription": "Wybierz tę opcję, aby kopia tej faktury była automatycznie przesyłana cyklicznie.", - "Expense.Summary.Recurring.CheckboxTitle": "Czy jest to wydatek cykliczny?", "Expense.type": "Typ", "expense.type": "Typ", "Expense.Type.Charge": "Opłata Karty Wirtualnej", @@ -1677,16 +1660,12 @@ "ExpenseAmount": "Kwota wydatku", "ExpenseApprover": "Expense Approver", "ExpenseForm.AddAttachedFile": "Dodaj nowy dokument", - "ExpenseForm.AddGrantItem": "Dodaj pozycję dotacji", - "ExpenseForm.AddLineItem": "Dodaj nowy element", - "ExpenseForm.AddReceipt": "Dodaj nowy paragon", "ExpenseForm.AddressLabel": "Fizyczny adres", "ExpenseForm.CancelEditExpense": "Anuluj edytowanie", "ExpenseForm.ChooseCountry": "Wybierz kraj", "ExpenseForm.ClearExpenseForm": "Wyczyść formularz", "ExpenseForm.ConfirmCancelEditExpense": "Czy na pewno chcesz anulować edycję?", "ExpenseForm.ConfirmClearExpenseForm": "Czy na pewno chcesz wyczyścić formularz wydatków?", - "ExpenseForm.DescriptionPlaceholder": "Wpisz tutaj tytuł wydatku...", "ExpenseForm.FundingRequestDescription": "Złóż wniosek o dotację dla swojego projektu lub inicjatywy.", "ExpenseForm.GrantSubjectPlaceholder": "e.g., Research, software development, etc.", "ExpenseForm.inviteAdditionalInfo": "Chcesz podać dane do wypłaty, takie jak adres PayPal lub konto bankowe?", @@ -1707,12 +1686,7 @@ "ExpenseForm.SavePayout": "Zachowaj tę informację dla przyszłych wypłat", "ExpenseForm.SignUp.OrgAdminNote": "Musisz być administratorem organizacji, aby móc zgłaszać wydatki.", "ExpenseForm.SignUp.SignIn": "Będziemy używać tego adresu e-mail do utworzenia konta. Jeśli posiadasz już konto {loginLink}.", - "ExpenseForm.StepExpense": "Prześlij jeden lub więcej paragonów", - "ExpenseForm.StepExpenseFundingRequest": "Ustaw szczegóły dotacji", - "ExpenseForm.StepExpenseInvoice": "Ustaw szczegóły faktury", - "ExpenseForm.StepPayeeInvoice": "Informacje o odbiorcy", "ExpenseForm.Submit": "Prześlij wydatki", - "ExpenseForm.SubmitRequest": "Wyślij wniosek", "ExpenseForm.Type.Request": "Wniosek o dotację", "ExpenseFormPayeeStep.PrivateInfo": "Ta informacja jest prywatna", "ExpenseFormPayeeStep.PrivateInfoDetails": "The payout method details are private and can only be viewed by the Payee and the Host admins.", @@ -1747,7 +1721,6 @@ "ExpensesSubmittedByName": "Expenses submitted by {name}", "ExpenseSubmitters": "Expense Submitters", "ExpenseSummary.addNotesLabel": "Dodaj notatki", - "ExpenseSummaryTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} Summary to {collectiveName}", "ExpenseTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} {id} to {collectiveName}", "Explore": "Odkrywaj", "Export.Format": "Eksportuj {format}", @@ -1967,7 +1940,6 @@ "fiscalHosting.youngActivists.description": "Którzy nie mają doświadczenia w zarządzaniu własnym podmiotem prawnym.", "FiscalHostPolicies": "Fiscal Host Policies", "fiscaltos": "warunki sponsoringu finansowego", - "fIsGOi": "Nie, kontynuuj edycję", "FJBnaq": "ID klienta i jego sekret", "FKMNjH": "You won't be able to resume the synchronization in the future.", "FLqc8O": "Wirtualna karta dodana", @@ -3347,7 +3319,6 @@ "PJubm9": "Dzięki śledzeniu finansów i przejrzystości raporty same się piszą", "pKF7TO": "Require the contributor to provide their physical address when they contribute more than:", "Pkorog": "Account under verification", - "Pkq+ZR": "Please make sure that all the expense items in this expense belong to the selected expense category. If needed, you may submit additional items in separate expenses with different expense categories.", "Pkx+Wj": "Udostępnij wyniki", "platform": "Platforma", "platform.explainerVideo": "Film wyjaśniający", @@ -3381,8 +3352,6 @@ "PreviewFeatures.CollectiveOverview.Welcome.Description": "Stworzyliśmy tę przestrzeń, abyś mógł być na bieżąco ze wszystkim, co dzieje się w Twoich Zbiórkach, daj nam znać, jak możemy ją ulepszyć!", "PreviewFeatures.CollectiveOverview.Welcome.Title": "Witamy w nowym Przeglądzie Zbiórek", "PreviewFeatures.crowdfundingRedesignDescription": "Be part of the crowdfunding redesign effort and get access to previews of new crowdfunding and profile pages. Experience enhanced profile pages with separate fundraising and storytelling views, clearer relationships between collectives and their projects, improved goal tracking, and better collective narratives that showcase your impact and long-term sustainability.{newLine}{newLine}Check out the blog post for more details.", - "PreviewFeatures.inlineEditExpenseDescription": "Edit expense details directly in the Dashboard without navigating to separate pages.", - "PreviewFeatures.inlineEditExpenseTitle": "Inline Expense Editing", "PreviewFeatures.keyboardShortcutsDescription": "Navigate the expense flow more efficiently with keyboard shortcuts. Speed up your workflow and reduce mouse dependency for common actions.", "PreviewFeatures.keyboardShortcutsExpenseDetailsAttachments": "{leftKey} and {rightKey} to navigate through attachments.", "PreviewFeatures.keyboardShortcutsExpenseDetailsClose": "{key} to close Expense details.", @@ -3396,8 +3365,6 @@ "PreviewFeatures.keyboardShortcutsExpenseListSecurity": "{key} to check Security alerts for selected Expense.", "PreviewFeatures.keyboardShortcutsTitle": "Keyboard Shortcuts", "PreviewFeatures.LimitedAccess": "Limited preview", - "PreviewFeatures.newExpenseFlowDescription": "Experience an improved expense submission flow in the Dashboard with better user experience, clearer navigation, and enhanced form validation.", - "PreviewFeatures.newExpenseFlowTitle": "New Expense Submission Flow", "PreviewFeatures.publicBeta": "Public beta", "PreviewFeatures.searchCommandDescription": "Discover a new way to search for collectives, transactions, expenses, and more through an intuitive command menu interface. Access information faster with powerful search capabilities.", "PreviewFeatures.searchCommandTitle": "Search Command Menu", @@ -3455,7 +3422,6 @@ "pricing.start.feeNote": "Nie będziesz w stanie mógł naliczać od zbiorów ani ustalać żadnych opłat dla gospodarzy.", "pricing.title": "Nasza struktura cenowa", "pricing.unlimitedCollectives": "Nieograniczona liczba zbiórek", - "privacypolicy": "polityka prywatności", "Private": "Private", "PrivateCommentsMessage.Allowed": "Twoje komentarze są prywatne.", "PrivateCommentsMessage.AllowedDetails": "Expenses comments are private, because they sometimes contain confidential information such as payment details. Only the expense submitter, the admins and the accountants can see them.", @@ -4247,7 +4213,6 @@ "TopContributors.Individuals": "Osoby prywatne", "TopContributors.Organizations": "Organizacje", "tos": "warunki korzystania", - "TOSAndPrivacyPolicyAgreement": "Zgadzam się z {toslink} i {privacylink} Open Collective.", "total": "total", "TotalAmount": "Kwota łączna", "TotalAmountWithoutFee": "Kwota łączna (bez opłat)", @@ -4487,14 +4452,10 @@ "UpgradePlanCTA.title.DISABLED": "Upgrade Required", "UpgradePlanCTA.title.UNSUPPORTED": "Feature not supported for your account", "UpgradePlanCTA.upgradeButton": "Upgrade your plan", - "UploadDocumentation": "Prześlij dokumentację", - "UploadDocumentationDescription": "Jeśli chcesz dołączyć jakąkolwiek dokumentację, możesz ją przesłać tutaj.", "uploadImage.isDragActive": "Upuść jak jest gorący 🔥", "uploadImage.isDragReject": "🚫 Ten typ pliku nie jest akceptowany", "uploadImage.isUploading": "Wczytywanie zdjęcia...", "uploadImage.sizeRejected": "Rozdzielczość obrazu musi być pomiędzy {minResolution} a {maxResolution}. Rozmiar pliku musi być mniejszy niż {maxFileSize}.", - "UploadInvoice": "Prześlij fakturę", - "UploadInvoiceDescription": "Jeśli masz już dokument księgowy, możesz go załadować tutaj.", "uQguR/": "Wszystkie zbiórki", "Uqkhct": "No virtual cards", "uR+TjD": "Are you sure you want to delete this payout method?", @@ -4556,8 +4517,6 @@ "VendorsAndOrganizations.Description": "Manage all the external organizations you work with as vendors and quickly surface all the activity between your hosted Collectives and other platform Organizations.", "vEQcqS": "Dodano uwierzytelnianie dwuskładnikowe", "verificationCriteria": "kryteria weryfikacji", - "VerifyEmailAddress": "Zweryfikuj swój adres e-mail", - "VerifyEmailInstructions": "E-mail weryfikacyjny został wysłany na adres {email}. Kliknij link, aby zakończyć przesyłanie tego wydatku. Jeśli nie otrzymałeś e-maila, sprawdź swój spam.", "VErmYl": "Duplicate grant", "Vf1x2A": "Joined the platform on", "VfJsl4": "Beneficiary", @@ -4939,7 +4898,6 @@ "zNBAqh": "This value is an estimate. Please set the exact amount received if known.", "ZNBeE4": "Exactly {amount}", "ZNzyMo": "Export taxes and payment processor fees as columns", - "zO+Zv3": "Additional attachments", "zoC8Gb": "Odbiorca pokrywa koszty opłat", "zOk9pq": "Go to activity log", "ZonfjV": "Nie możesz edytować tej zbiórki", diff --git a/lang/pt-BR.json b/lang/pt-BR.json index 45cb4f4f059..00bebe1b5a8 100644 --- a/lang/pt-BR.json +++ b/lang/pt-BR.json @@ -30,7 +30,6 @@ "+RvjCt": "O cartão de crédito foi excluído", "+S3jp9": "Nova atualização", "+SSp9V": "We encountered an issue loading this {type,select,section{section}other{page}}. Please reload the page or contact support if the problem persists.", - "+t6c4i": "Instruções para a Categoria da Conta", "+U6ozc": "Tipo", "+UdXIM": " criada através do GitHub", "+UwJxq": "Taxas Esperadas de Host", @@ -148,13 +147,11 @@ "2u8jmQ": "Ativar o limite de execução 2FA para pagamentos", "2Un6+c": "KYC Information", "2uzHxT": "Importar CSV", - "3135/i": "Moeda de Despesa", "322m9e": "A mensagem precisa ter pelo menos 10 caracteres", "32rBNK": "Terms of Service", "34Up+l": "Ver mais", "35Jfcr": "Vales-presente incentivam seus funcionários e membros da comunidade a apoiarem os projetos que eles amam. Saiba mais.", "37zpJw": "Search countries...", - "38dzz9": "Categoria de Despesa", "38fPNE": "Configurações de Importação de Transações", "3A7J9A": "Não conseguimos encontrar um Administrador Fiscal com os critérios informados.", "3ABdi3": "Essa conta não existe", @@ -577,7 +574,6 @@ "AzGwgz": "O valor mínimo para transferência para {selectedCurrency} é {minAmountForSelectedCurrency}", "AZmsFu": " rejeitou a contribuição de ", "AzRKUx": "Criar Beneficiário", - "b++lom": "Sim, cancelar a edição", "B+fVMt": "Mais de {amount}", "B+NQ+r": " editou {expenseDescription} on ", "b07w+D": "Nível", @@ -1174,7 +1170,6 @@ "CreatedAt": "Created At", "CreatedBy": "por {name}", "CreateEvent": "Criar evento", - "CreateExpense.HelpCreateInfo": "Solicitar pagamento de {collective}. As despesas serão processadas assim que forem aprovadas por um administrador do coletivo. A quantia, a descrição e o seu nome de perfil são públicos, mas os anexos, dados de pagamento e outras informações pessoais são mantidos como privados.", "CreateExpenseFAQ.getPaid": "Como eu recebo o pagamento de um Coletivo?", "CreateExpenseFAQ.getPaidDetails": "Envie uma despesa e forneça suas informações de pagamento.", "CreateExpenseFAQ.howAreApproved": "Como as despesas são aprovadas?", @@ -1297,7 +1292,6 @@ "DhxIpu": "Novo comentário em ", "DinF1w": "Valor líquido para {collective}", "dIoEln": "Total desembolsado", - "DismissableHelp.DontShowAgain": "Ok, não me mostre novamente", "DisplayName": "Display Name", "DJnUUS": "Essa despesa não foi aprovada", "dK5ItS": "Por padrão, apenas os administradores do host fiscal podem enviar despesas em nome de fornecedores. Você pode permitir que outros usuários que enviam despesas para coletivos que você hospeda também enviem despesas em nome de fornecedores.", @@ -1583,7 +1577,6 @@ "Expense.Direction.Received": "Recebido", "Expense.Direction.Submitted": "Enviado", "expense.draft": "Rascunho", - "Expense.edit": "Editar despesa", "expense.editDetails": "Editar detalhes de despesas", "expense.editNotes": "Edit notes", "expense.editPaidBy": "Edição paga por", @@ -1591,15 +1584,12 @@ "expense.editPayee": "Editar beneficiário", "expense.editTitle": "Editar título da despesa", "expense.editType": "Editar tipo de despesa", - "Expense.EnterExpenseTitle": "Título da despesa (Public)", - "Expense.EnterRequestSubject": "Digite conceder assunto (Public)", "Expense.GoToPage": "Ir para página de despesas", "expense.hostFeeInCollectiveCurrency": "taxa administrativa", "expense.incurredAt": "Data", "expense.invite.declined": "Convite recusado", "Expense.InviteIsOnItsWay.Description": "Um convite para enviar esta despesa foi enviado para {email}. Assim que eles confirmarem e terminarem o processo, ele aparecerá na lista de despesas.", "Expense.InvoiceItems": "Faturas", - "Expense.JoinAndSubmit": "Participe e envie", "expense.linked": "Despesa vinculada", "expense.markAsPaid": "Marcar como paga", "Expense.MarkAsSpamLabel": "Por que você está marcando essa despesa como spam?", @@ -1628,7 +1618,6 @@ "Expense.PayTo": "Pagar para", "expense.pending": "Pendente", "expense.pickPayoutMethod": "Escolher novo método de pagamento", - "Expense.PrivacyWarning": "Esta informação é pública. Não coloque nenhum dado privado neste campo.", "Expense.PrivateNote": "Nota privada", "Expense.Receipt": "Recibo", "expense.ReceiptRequired": "Recibo obrigatório", @@ -1646,21 +1635,15 @@ "Expense.RequestDetails": "Solicitar Detalhes", "Expense.RequestedBy": "Convidado por {name}", "expense.requestReApproval.btn": "Solicitar nova aprovação", - "Expense.SaveExpense": "Salvar Despesa", - "Expense.SaveReceipt": "Salvar recibo", "expense.schedule.btn": "Agendar para pagar com {paymentMethod}", "expense.scheduledForPayment": "Agendado para pagamento", "Expense.SeeDetails": "Ver detalhes da despesa", "Expense.SendInvite": "Enviar Convite", - "Expense.SignUpInfoBox": "Você precisa criar uma conta para receber um pagamento de {collectiveName}, ao clicar em 'Participar e Enviar' você concorda em criar uma conta na Open Collective.", "expense.spam.notAllowed": "Você não pode marcar suas próprias despesas como spam", "expense.status": "Situação", "Expense.Status.Refunded": "Reembolsado", "Expense.Submitted": "Despesa enviada", "Expense.SubmittedBy": "Enviado por {name}", - "Expense.SuccessPage": "Você pode editar ou revisar atualizações nesta página.", - "Expense.Summary.Recurring.CheckboxDescription": "Escolha esta opção para enviar automaticamente uma cópia desta fatura periodicamente.", - "Expense.Summary.Recurring.CheckboxTitle": "Esta é uma despesa recorrente?", "Expense.type": "Tipo", "expense.type": "Tipo", "Expense.Type.Charge": "Cobrança de cartão virtual", @@ -1677,16 +1660,12 @@ "ExpenseAmount": "Valor da despesa", "ExpenseApprover": "Expense Approver", "ExpenseForm.AddAttachedFile": "Adicionar um novo documento", - "ExpenseForm.AddGrantItem": "Adicionar item de concessão", - "ExpenseForm.AddLineItem": "Adicionar novo item", - "ExpenseForm.AddReceipt": "Adicionar novo recibo", "ExpenseForm.AddressLabel": "Endereço físico", "ExpenseForm.CancelEditExpense": "Cancelar Edição", "ExpenseForm.ChooseCountry": "Escolher país", "ExpenseForm.ClearExpenseForm": "Limpar Formulário", "ExpenseForm.ConfirmCancelEditExpense": "Tem certeza de que deseja cancelar as edições?", "ExpenseForm.ConfirmClearExpenseForm": "Tem certeza de que quer apagar o formulário de despesa?", - "ExpenseForm.DescriptionPlaceholder": "Insira o título da despesa aqui...", "ExpenseForm.FundingRequestDescription": "Solicite um subsídio para seu projeto ou iniciativa.", "ExpenseForm.GrantSubjectPlaceholder": "e.g., Research, software development, etc.", "ExpenseForm.inviteAdditionalInfo": "Quer inserir os dados de pagamento, como um endereço do PayPal ou uma conta bancária?", @@ -1707,12 +1686,7 @@ "ExpenseForm.SavePayout": "Salvar estas informações para pagamentos futuros", "ExpenseForm.SignUp.OrgAdminNote": "Você precisa ser um administrador da organização para enviar despesas.", "ExpenseForm.SignUp.SignIn": "Vamos usar este e-mail para criar a sua conta. Se você já tem uma conta, {loginLink}.", - "ExpenseForm.StepExpense": "Enviar um ou vários recibos", - "ExpenseForm.StepExpenseFundingRequest": "Configurar detalhes de concessão", - "ExpenseForm.StepExpenseInvoice": "Configurar detalhes da fatura", - "ExpenseForm.StepPayeeInvoice": "Informações do beneficiário", "ExpenseForm.Submit": "Enviar despesa", - "ExpenseForm.SubmitRequest": "Enviar solicitação", "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.", @@ -1747,7 +1721,6 @@ "ExpensesSubmittedByName": "Expenses submitted by {name}", "ExpenseSubmitters": "Expense Submitters", "ExpenseSummary.addNotesLabel": "Adicionar notas", - "ExpenseSummaryTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} Summary to {collectiveName}", "ExpenseTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} {id} to {collectiveName}", "Explore": "Explorar", "Export.Format": "Exportar {format}", @@ -1967,7 +1940,6 @@ "fiscalHosting.youngActivists.description": "Que podem não ter a experiência de gerir a sua própria entidade jurídica.", "FiscalHostPolicies": "Fiscal Host Policies", "fiscaltos": "termos de patrocínio fiscal", - "fIsGOi": "Não, continue editando", "FJBnaq": "Client ID (ID do cliente) e Client Secret (Chave secreta do cliente)", "FKMNjH": "Você não poderá retomar a sincronização no futuro.", "FLqc8O": "Cartão virtual adicionado", @@ -3347,7 +3319,6 @@ "PJubm9": "O rastreamento financeiro e a transparência significam que os relatórios são escritos sozinhos", "pKF7TO": "Exigir que o contribuinte forneça seu endereço físico quando contribuir com mais de:", "Pkorog": "Account under verification", - "Pkq+ZR": "Certifique-se de que todos os itens de despesa nesta despesa pertencem à categoria de despesa selecionada. Se necessário, você pode enviar itens adicionais em despesas separadas com categorias de despesa diferentes.", "Pkx+Wj": "Compartilhar resultados", "platform": "Plataforma", "platform.explainerVideo": "Vídeo explicativo", @@ -3381,8 +3352,6 @@ "PreviewFeatures.CollectiveOverview.Welcome.Description": "Criamos este espaço para que você fique atento a tudo que acontece em sua Coletividade, por favor, deixe-nos saber como podemos melhorar!", "PreviewFeatures.CollectiveOverview.Welcome.Title": "Bem-vindo à sua nova Visão Geral Coletiva", "PreviewFeatures.crowdfundingRedesignDescription": "Be part of the crowdfunding redesign effort and get access to previews of new crowdfunding and profile pages. Experience enhanced profile pages with separate fundraising and storytelling views, clearer relationships between collectives and their projects, improved goal tracking, and better collective narratives that showcase your impact and long-term sustainability.{newLine}{newLine}Check out the blog post for more details.", - "PreviewFeatures.inlineEditExpenseDescription": "Edit expense details directly in the Dashboard without navigating to separate pages.", - "PreviewFeatures.inlineEditExpenseTitle": "Inline Expense Editing", "PreviewFeatures.keyboardShortcutsDescription": "Navigate the expense flow more efficiently with keyboard shortcuts. Speed up your workflow and reduce mouse dependency for common actions.", "PreviewFeatures.keyboardShortcutsExpenseDetailsAttachments": "{leftKey} and {rightKey} to navigate through attachments.", "PreviewFeatures.keyboardShortcutsExpenseDetailsClose": "{key} to close Expense details.", @@ -3396,8 +3365,6 @@ "PreviewFeatures.keyboardShortcutsExpenseListSecurity": "{key} to check Security alerts for selected Expense.", "PreviewFeatures.keyboardShortcutsTitle": "Keyboard Shortcuts", "PreviewFeatures.LimitedAccess": "Prévia limitada", - "PreviewFeatures.newExpenseFlowDescription": "Experience an improved expense submission flow in the Dashboard with better user experience, clearer navigation, and enhanced form validation.", - "PreviewFeatures.newExpenseFlowTitle": "New Expense Submission Flow", "PreviewFeatures.publicBeta": "Beta pública", "PreviewFeatures.searchCommandDescription": "Discover a new way to search for collectives, transactions, expenses, and more through an intuitive command menu interface. Access information faster with powerful search capabilities.", "PreviewFeatures.searchCommandTitle": "Search Command Menu", @@ -3455,7 +3422,6 @@ "pricing.start.feeNote": "Você não será capaz de cobrar Coletivos ou definir qualquer taxa de administrador.", "pricing.title": "Nossa Estrutura de Preços", "pricing.unlimitedCollectives": "Coletivos ilimitados", - "privacypolicy": "política de privacidade", "Private": "Privado", "PrivateCommentsMessage.Allowed": "Seus comentários são privados.", "PrivateCommentsMessage.AllowedDetails": "Os comentários de despesas são privados porque às vezes contêm informações confidenciais, como detalhes de pagamento. Apenas o remetente da despesa e os administradores podem vê-los.", @@ -4247,7 +4213,6 @@ "TopContributors.Individuals": "Pessoas", "TopContributors.Organizations": "Organizações", "tos": "termos de serviço", - "TOSAndPrivacyPolicyAgreement": "Eu concordo com o {toslink} e {privacylink} da Open Collective.", "total": "total", "TotalAmount": "Valor total", "TotalAmountWithoutFee": "Valor total (sem taxas)", @@ -4487,14 +4452,10 @@ "UpgradePlanCTA.title.DISABLED": "Upgrade Required", "UpgradePlanCTA.title.UNSUPPORTED": "Feature not supported for your account", "UpgradePlanCTA.upgradeButton": "Upgrade your plan", - "UploadDocumentation": "Carregar documentação", - "UploadDocumentationDescription": "Se você deseja incluir qualquer documentação, você pode enviá-la aqui.", "uploadImage.isDragActive": "Solte como se estivesse quente! 🔥", "uploadImage.isDragReject": "Este tipo de arquivo não é aceito", "uploadImage.isUploading": "Carregando imagem...", "uploadImage.sizeRejected": "A resolução da imagem deve estar entre {minResolution} e {maxResolution}. O tamanho do arquivo deve ser menor que {maxFileSize}.", - "UploadInvoice": "Carregar fatura", - "UploadInvoiceDescription": "Se você já tem um documento de fatura, você pode enviá-lo aqui.", "uQguR/": "Todos os Coletivos", "Uqkhct": "Sem cartões virtuais", "uR+TjD": "Tem certeza de que deseja excluir este método de pagamento?", @@ -4556,8 +4517,6 @@ "VendorsAndOrganizations.Description": "Manage all the external organizations you work with as vendors and quickly surface all the activity between your hosted Collectives and other platform Organizations.", "vEQcqS": "Autenticação de dois fatores adicionada", "verificationCriteria": "critérios de verificação", - "VerifyEmailAddress": "Verifique seu endereço de e-mail", - "VerifyEmailInstructions": "Um e-mail de verificação foi enviado para {email}. Clique no link para concluir o envio dessa despesa. Caso não tenha recebido, verifique a caixa de spam.", "VErmYl": "Duplicate grant", "Vf1x2A": "Joined the platform on", "VfJsl4": "Beneficiário", @@ -4939,7 +4898,6 @@ "zNBAqh": "Este valor é uma estimativa. Defina o valor exato recebido, se souber.", "ZNBeE4": "Exatamente {amount}", "ZNzyMo": "Exportar impostos e taxas de processamento como colunas", - "zO+Zv3": "Anexos adicionais", "zoC8Gb": "O beneficiário está arcando com as taxas", "zOk9pq": "Go to activity log", "ZonfjV": "Você não pode editar esse coletivo", diff --git a/lang/pt.json b/lang/pt.json index 4c26f877d8f..f702bf9ee9e 100644 --- a/lang/pt.json +++ b/lang/pt.json @@ -30,7 +30,6 @@ "+RvjCt": "Cartão removido com sucesso", "+S3jp9": "Nova Atualização", "+SSp9V": "We encountered an issue loading this {type,select,section{section}other{page}}. Please reload the page or contact support if the problem persists.", - "+t6c4i": "Instruções para a Categoria da Conta", "+U6ozc": "Tipo", "+UdXIM": " criada através do GitHub", "+UwJxq": "Expected Host Fees", @@ -148,13 +147,11 @@ "2u8jmQ": "Enable rolling limit 2FA for payouts", "2Un6+c": "KYC Information", "2uzHxT": "Import CSV", - "3135/i": "Expense Currency", "322m9e": "A mensagem precisa no mínimo de 10 caracteres", "32rBNK": "Terms of Service", "34Up+l": "View more", "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "37zpJw": "Search countries...", - "38dzz9": "Expense Category", "38fPNE": "Transaction Import Settings", "3A7J9A": "We could not find a host that matches all your criteria.", "3ABdi3": "Esta conta não existe", @@ -577,7 +574,6 @@ "AzGwgz": "The minimum amount for transferring to {selectedCurrency} is {minAmountForSelectedCurrency}", "AZmsFu": " rejected contribution from ", "AzRKUx": "Create Beneficiary", - "b++lom": "Yes, cancel editing", "B+fVMt": "More than {amount}", "B+NQ+r": " drafted an {expenseDescription} on ", "b07w+D": "Categoria", @@ -1174,7 +1170,6 @@ "CreatedAt": "Created At", "CreatedBy": "por {name}", "CreateEvent": "Create event", - "CreateExpense.HelpCreateInfo": "Request payment from {collective}. Expenses will be processed once approved by a Collective admin. The amount, description, and your profile name are public, but attachments, payment details, and other personal info is kept private.", "CreateExpenseFAQ.getPaid": "Como faço para receber de um Coletivo?", "CreateExpenseFAQ.getPaidDetails": "Envie uma despesa e forneça suas informações de pagamento.", "CreateExpenseFAQ.howAreApproved": "Como são aprovadas as despesas?", @@ -1297,7 +1292,6 @@ "DhxIpu": "New comment on ", "DinF1w": "Net Amount for {collective}", "dIoEln": "Total disbursed", - "DismissableHelp.DontShowAgain": "Ok, não me mostre de novo", "DisplayName": "Display Name", "DJnUUS": "This expense has not been approved", "dK5ItS": "By default only fiscal host administrators can submit expenses on behalf of vendors. You can allow other users who submit expenses to collectives you host to also submit expenses on behalf vendors.", @@ -1583,7 +1577,6 @@ "Expense.Direction.Received": "Received", "Expense.Direction.Submitted": "Submitted", "expense.draft": "Draft", - "Expense.edit": "Editar despesa", "expense.editDetails": "Edit expense details", "expense.editNotes": "Edit notes", "expense.editPaidBy": "Edit paid by", @@ -1591,15 +1584,12 @@ "expense.editPayee": "Edit payee", "expense.editTitle": "Edit expense title", "expense.editType": "Edit expense type", - "Expense.EnterExpenseTitle": "Expense title (Public)", - "Expense.EnterRequestSubject": "Enter grant subject (Public)", "Expense.GoToPage": "Go to expense page", "expense.hostFeeInCollectiveCurrency": "comissão do organizador", "expense.incurredAt": "Data", "expense.invite.declined": "Invite declined", "Expense.InviteIsOnItsWay.Description": "An invitation to submit this expense has been sent to {email}. Once they confirm and finish the process, it will appear on the expenses list.", "Expense.InvoiceItems": "Invoice items", - "Expense.JoinAndSubmit": "Join and Submit", "expense.linked": "Expense linked", "expense.markAsPaid": "Marcar como pago", "Expense.MarkAsSpamLabel": "Why are you marking this expense as spam?", @@ -1628,7 +1618,6 @@ "Expense.PayTo": "Pay to", "expense.pending": "Pendente", "expense.pickPayoutMethod": "Pick new payout method", - "Expense.PrivacyWarning": "This information is public. Do not put any private details in this field.", "Expense.PrivateNote": "Nota privada", "Expense.Receipt": "Receipt", "expense.ReceiptRequired": "Receipt required", @@ -1646,21 +1635,15 @@ "Expense.RequestDetails": "Request Details", "Expense.RequestedBy": "Invited by {name}", "expense.requestReApproval.btn": "Request re-approval", - "Expense.SaveExpense": "Guardar despesa", - "Expense.SaveReceipt": "Guardar recibo", "expense.schedule.btn": "Schedule to Pay with {paymentMethod}", "expense.scheduledForPayment": "Scheduled for payment", "Expense.SeeDetails": "See expense details", "Expense.SendInvite": "Enviar Convite", - "Expense.SignUpInfoBox": "You need to create an account to receive a payment from {collectiveName}, by clicking 'Join and Submit' you agree to create an account on {WebsiteName}.", "expense.spam.notAllowed": "You can't mark your own expenses as spam", "expense.status": "Estado", "Expense.Status.Refunded": "Refunded", "Expense.Submitted": "Expense submitted", "Expense.SubmittedBy": "Submitted by {name}", - "Expense.SuccessPage": "You can edit or review updates on this page.", - "Expense.Summary.Recurring.CheckboxDescription": "Choose this option to automatically submit a copy of this invoice on a periodic basis.", - "Expense.Summary.Recurring.CheckboxTitle": "Is this a recurring expense?", "Expense.type": "Type", "expense.type": "Tipo", "Expense.Type.Charge": "Virtual Card Charge", @@ -1677,16 +1660,12 @@ "ExpenseAmount": "Expense amount", "ExpenseApprover": "Expense Approver", "ExpenseForm.AddAttachedFile": "Add new document", - "ExpenseForm.AddGrantItem": "Add grant item", - "ExpenseForm.AddLineItem": "Add new item", - "ExpenseForm.AddReceipt": "Add new receipt", "ExpenseForm.AddressLabel": "Physical address", "ExpenseForm.CancelEditExpense": "Cancel Edit", "ExpenseForm.ChooseCountry": "Choose country", "ExpenseForm.ClearExpenseForm": "Clear Form", "ExpenseForm.ConfirmCancelEditExpense": "Are you sure you want to cancel the edits?", "ExpenseForm.ConfirmClearExpenseForm": "Are you sure you want to clear the expense form?", - "ExpenseForm.DescriptionPlaceholder": "Enter expense title here...", "ExpenseForm.FundingRequestDescription": "Request a grant for your project or initiative.", "ExpenseForm.GrantSubjectPlaceholder": "e.g., Research, software development, etc.", "ExpenseForm.inviteAdditionalInfo": "Want to enter payout details, such as a PayPal address or bank account?", @@ -1707,12 +1686,7 @@ "ExpenseForm.SavePayout": "Salvar estas informações para pagamentos futuros", "ExpenseForm.SignUp.OrgAdminNote": "You need to be an admin of the Organization to submit expenses.", "ExpenseForm.SignUp.SignIn": "We will use this email to create your account. If you already have an account {loginLink}.", - "ExpenseForm.StepExpense": "Upload one or multiple receipt", - "ExpenseForm.StepExpenseFundingRequest": "Set grant details", - "ExpenseForm.StepExpenseInvoice": "Set invoice details", - "ExpenseForm.StepPayeeInvoice": "Payee information", "ExpenseForm.Submit": "Enviar despesa", - "ExpenseForm.SubmitRequest": "Submit request", "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.", @@ -1747,7 +1721,6 @@ "ExpensesSubmittedByName": "Expenses submitted by {name}", "ExpenseSubmitters": "Expense Submitters", "ExpenseSummary.addNotesLabel": "Add notes", - "ExpenseSummaryTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} Summary to {collectiveName}", "ExpenseTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} {id} to {collectiveName}", "Explore": "Explore", "Export.Format": "Exportar para {format}", @@ -1967,7 +1940,6 @@ "fiscalHosting.youngActivists.description": "Who may lack the experience to manage their own legal entity.", "FiscalHostPolicies": "Fiscal Host Policies", "fiscaltos": "termos de patrocínio fiscal", - "fIsGOi": "No, continue editing", "FJBnaq": "Client ID and client secret", "FKMNjH": "You won't be able to resume the synchronization in the future.", "FLqc8O": "Virtual card added", @@ -3347,7 +3319,6 @@ "PJubm9": "Financial tracking and transparency means reporting writes itself", "pKF7TO": "Require the contributor to provide their physical address when they contribute more than:", "Pkorog": "Account under verification", - "Pkq+ZR": "Please make sure that all the expense items in this expense belong to the selected expense category. If needed, you may submit additional items in separate expenses with different expense categories.", "Pkx+Wj": "Partilhar resultados", "platform": "Plataforma", "platform.explainerVideo": "Explainer video", @@ -3381,8 +3352,6 @@ "PreviewFeatures.CollectiveOverview.Welcome.Description": "We’ve created this space for you to keep on top of everything happening in your Collective, please let us know how we can make it better!", "PreviewFeatures.CollectiveOverview.Welcome.Title": "Welcome to your new Collective Overview", "PreviewFeatures.crowdfundingRedesignDescription": "Be part of the crowdfunding redesign effort and get access to previews of new crowdfunding and profile pages. Experience enhanced profile pages with separate fundraising and storytelling views, clearer relationships between collectives and their projects, improved goal tracking, and better collective narratives that showcase your impact and long-term sustainability.{newLine}{newLine}Check out the blog post for more details.", - "PreviewFeatures.inlineEditExpenseDescription": "Edit expense details directly in the Dashboard without navigating to separate pages.", - "PreviewFeatures.inlineEditExpenseTitle": "Inline Expense Editing", "PreviewFeatures.keyboardShortcutsDescription": "Navigate the expense flow more efficiently with keyboard shortcuts. Speed up your workflow and reduce mouse dependency for common actions.", "PreviewFeatures.keyboardShortcutsExpenseDetailsAttachments": "{leftKey} and {rightKey} to navigate through attachments.", "PreviewFeatures.keyboardShortcutsExpenseDetailsClose": "{key} to close Expense details.", @@ -3396,8 +3365,6 @@ "PreviewFeatures.keyboardShortcutsExpenseListSecurity": "{key} to check Security alerts for selected Expense.", "PreviewFeatures.keyboardShortcutsTitle": "Keyboard Shortcuts", "PreviewFeatures.LimitedAccess": "Limited preview", - "PreviewFeatures.newExpenseFlowDescription": "Experience an improved expense submission flow in the Dashboard with better user experience, clearer navigation, and enhanced form validation.", - "PreviewFeatures.newExpenseFlowTitle": "New Expense Submission Flow", "PreviewFeatures.publicBeta": "Public beta", "PreviewFeatures.searchCommandDescription": "Discover a new way to search for collectives, transactions, expenses, and more through an intuitive command menu interface. Access information faster with powerful search capabilities.", "PreviewFeatures.searchCommandTitle": "Search Command Menu", @@ -3455,7 +3422,6 @@ "pricing.start.feeNote": "You wont be able to charge Collectives or set any Host Fee.", "pricing.title": "Our Pricing Structure", "pricing.unlimitedCollectives": "Unlimited Collectives", - "privacypolicy": "privacy policy", "Private": "Private", "PrivateCommentsMessage.Allowed": "Os teus comentários são privados.", "PrivateCommentsMessage.AllowedDetails": "Expenses comments are private, because they sometimes contain confidential information such as payment details. Only the expense submitter, the admins and the accountants can see them.", @@ -4247,7 +4213,6 @@ "TopContributors.Individuals": "Indivíduos", "TopContributors.Organizations": "Organizações", "tos": "termos de serviço", - "TOSAndPrivacyPolicyAgreement": "I agree with the {toslink} and {privacylink} of Open Collective.", "total": "total", "TotalAmount": "Total amount", "TotalAmountWithoutFee": "Total amount (without fees)", @@ -4487,14 +4452,10 @@ "UpgradePlanCTA.title.DISABLED": "Upgrade Required", "UpgradePlanCTA.title.UNSUPPORTED": "Feature not supported for your account", "UpgradePlanCTA.upgradeButton": "Upgrade your plan", - "UploadDocumentation": "Upload documentation", - "UploadDocumentationDescription": "If you want to include any documentation, you can upload it here.", "uploadImage.isDragActive": "Me solta, pô 🔥", "uploadImage.isDragReject": "🚫 Este tipo de arquivo não é aceito", "uploadImage.isUploading": "Uploading image...", "uploadImage.sizeRejected": "Image resolution needs to be between {minResolution} and {maxResolution}. File size must be below {maxFileSize}.", - "UploadInvoice": "Upload invoice", - "UploadInvoiceDescription": "If you already have an invoice document, you can upload it here.", "uQguR/": "All Collectives", "Uqkhct": "No virtual cards", "uR+TjD": "Are you sure you want to delete this payout method?", @@ -4556,8 +4517,6 @@ "VendorsAndOrganizations.Description": "Manage all the external organizations you work with as vendors and quickly surface all the activity between your hosted Collectives and other platform Organizations.", "vEQcqS": "Two factor authentication added", "verificationCriteria": "verification criteria", - "VerifyEmailAddress": "Verify your email address", - "VerifyEmailInstructions": "A verification email has been sent to {email}. Click the link to complete submitting this expense. If you have not received the email, please check your spam.", "VErmYl": "Duplicate grant", "Vf1x2A": "Joined the platform on", "VfJsl4": "Beneficiary", @@ -4939,7 +4898,6 @@ "zNBAqh": "This value is an estimate. Please set the exact amount received if known.", "ZNBeE4": "Exactly {amount}", "ZNzyMo": "Export taxes and payment processor fees as columns", - "zO+Zv3": "Additional attachments", "zoC8Gb": "The payee is covering the fees", "zOk9pq": "Go to activity log", "ZonfjV": "You cannot edit this collective", diff --git a/lang/ru.json b/lang/ru.json index e02f2606d99..ee527ea9fd3 100644 --- a/lang/ru.json +++ b/lang/ru.json @@ -30,7 +30,6 @@ "+RvjCt": "Карта успешно удалена", "+S3jp9": "Новое обновление", "+SSp9V": "We encountered an issue loading this {type,select,section{section}other{page}}. Please reload the page or contact support if the problem persists.", - "+t6c4i": "Инструкции по категориям счетов", "+U6ozc": "Тип", "+UdXIM": " создан с помощью GitHub", "+UwJxq": "Ожидаемая плата за услуги представителя", @@ -148,13 +147,11 @@ "2u8jmQ": "Включить двухфакторную аутентификацию с ограниченным периодом действия для выплат", "2Un6+c": "KYC Information", "2uzHxT": "Импортировать CSV", - "3135/i": "Валюта Расходов", "322m9e": "Длина сообщения должна быть не менее 10 символов", "32rBNK": "Пользовательское соглашение", "34Up+l": "Показать еще", "35Jfcr": "Подарочные карты Open Collective дают возможность вашим сотрудникам или членам сообщества поддерживать любимые проекты с открытым исходным кодом. Узнать больше.", "37zpJw": "Поиск стран...", - "38dzz9": "Категория Расходов", "38fPNE": "Настройки транзакционного импорта", "3A7J9A": "Мы не смогли найти поставщика, который соответствует всем вашим критериям.", "3ABdi3": "Эта учетная запись не существует", @@ -577,7 +574,6 @@ "AzGwgz": "The minimum amount for transferring to {selectedCurrency} is {minAmountForSelectedCurrency}", "AZmsFu": " rejected contribution from ", "AzRKUx": "Create Beneficiary", - "b++lom": "Да, отменить изменения", "B+fVMt": "Больше чем {amount}", "B+NQ+r": " drafted an {expenseDescription} on ", "b07w+D": "Уровень", @@ -1174,7 +1170,6 @@ "CreatedAt": "Created At", "CreatedBy": "{name}", "CreateEvent": "Создать событие", - "CreateExpense.HelpCreateInfo": "Запрос платежа от {collective}. Средства будут обработаны после утверждения администратором коллектива. Сумма, описание и имя вашего профиля являются общедоступными, но вложения, платежные реквизиты и другая личная информация останутся конфиденциальными.", "CreateExpenseFAQ.getPaid": "Как мне получить оплату от коллектива?", "CreateExpenseFAQ.getPaidDetails": "Отправьте свои расходы и предоставьте свои платежные данные.", "CreateExpenseFAQ.howAreApproved": "Как утверждаются расходы?", @@ -1297,7 +1292,6 @@ "DhxIpu": "New comment on ", "DinF1w": "Net Amount for {collective}", "dIoEln": "Total disbursed", - "DismissableHelp.DontShowAgain": "Хорошо, больше не показывать это мне", "DisplayName": "Display Name", "DJnUUS": "This expense has not been approved", "dK5ItS": "By default only fiscal host administrators can submit expenses on behalf of vendors. You can allow other users who submit expenses to collectives you host to also submit expenses on behalf vendors.", @@ -1583,7 +1577,6 @@ "Expense.Direction.Received": "Получено", "Expense.Direction.Submitted": "Отправлено", "expense.draft": "Черновик", - "Expense.edit": "Изменить расходы", "expense.editDetails": "Edit expense details", "expense.editNotes": "Edit notes", "expense.editPaidBy": "Edit paid by", @@ -1591,15 +1584,12 @@ "expense.editPayee": "Edit payee", "expense.editTitle": "Edit expense title", "expense.editType": "Edit expense type", - "Expense.EnterExpenseTitle": "Expense title (Public)", - "Expense.EnterRequestSubject": "Enter grant subject (Public)", "Expense.GoToPage": "Перейти на страницу расходов", "expense.hostFeeInCollectiveCurrency": "комиссия представителя", "expense.incurredAt": "Дата", "expense.invite.declined": "Invite declined", "Expense.InviteIsOnItsWay.Description": "An invitation to submit this expense has been sent to {email}. Once they confirm and finish the process, it will appear on the expenses list.", "Expense.InvoiceItems": "Позиции счета", - "Expense.JoinAndSubmit": "Присоединиться и отправить", "expense.linked": "Expense linked", "expense.markAsPaid": "Отметить как выплаченное", "Expense.MarkAsSpamLabel": "Why are you marking this expense as spam?", @@ -1628,7 +1618,6 @@ "Expense.PayTo": "Оплатить", "expense.pending": "В ожидании", "expense.pickPayoutMethod": "Pick new payout method", - "Expense.PrivacyWarning": "This information is public. Do not put any private details in this field.", "Expense.PrivateNote": "Приватная заметка", "Expense.Receipt": "Receipt", "expense.ReceiptRequired": "Требуется чек", @@ -1646,21 +1635,15 @@ "Expense.RequestDetails": "Подробности запроса", "Expense.RequestedBy": "Invited by {name}", "expense.requestReApproval.btn": "Request re-approval", - "Expense.SaveExpense": "Save Expense", - "Expense.SaveReceipt": "Сохранить квитанцию", "expense.schedule.btn": "Schedule to Pay with {paymentMethod}", "expense.scheduledForPayment": "Запланированный платеж", "Expense.SeeDetails": "See expense details", "Expense.SendInvite": "Отправить приглашение", - "Expense.SignUpInfoBox": "You need to create an account to receive a payment from {collectiveName}, by clicking 'Join and Submit' you agree to create an account on {WebsiteName}.", "expense.spam.notAllowed": "You can't mark your own expenses as spam", "expense.status": "Статус", "Expense.Status.Refunded": "Возврат средств произведен", "Expense.Submitted": "Expense submitted", "Expense.SubmittedBy": "Отправлено {name}", - "Expense.SuccessPage": "Вы можете редактировать или просматривать обновления на этой странице.", - "Expense.Summary.Recurring.CheckboxDescription": "Choose this option to automatically submit a copy of this invoice on a periodic basis.", - "Expense.Summary.Recurring.CheckboxTitle": "Is this a recurring expense?", "Expense.type": "Type", "expense.type": "Тип", "Expense.Type.Charge": "Virtual Card Charge", @@ -1677,16 +1660,12 @@ "ExpenseAmount": "Сумма расходов", "ExpenseApprover": "Expense Approver", "ExpenseForm.AddAttachedFile": "Добавить новый документ", - "ExpenseForm.AddGrantItem": "Add grant item", - "ExpenseForm.AddLineItem": "Добавить новую позицию", - "ExpenseForm.AddReceipt": "Добавить чек", "ExpenseForm.AddressLabel": "Физический адрес", "ExpenseForm.CancelEditExpense": "Отменить изменения", "ExpenseForm.ChooseCountry": "Выберите страну", "ExpenseForm.ClearExpenseForm": "Очистить форму", "ExpenseForm.ConfirmCancelEditExpense": "Are you sure you want to cancel the edits?", "ExpenseForm.ConfirmClearExpenseForm": "Вы уверены, что хотите очистить форму расходов?", - "ExpenseForm.DescriptionPlaceholder": "Введите название расхода здесь...", "ExpenseForm.FundingRequestDescription": "Request a grant for your project or initiative.", "ExpenseForm.GrantSubjectPlaceholder": "e.g., Research, software development, etc.", "ExpenseForm.inviteAdditionalInfo": "Want to enter payout details, such as a PayPal address or bank account?", @@ -1707,12 +1686,7 @@ "ExpenseForm.SavePayout": "Сохранить эту информацию для будущих выплат", "ExpenseForm.SignUp.OrgAdminNote": "You need to be an admin of the Organization to submit expenses.", "ExpenseForm.SignUp.SignIn": "We will use this email to create your account. If you already have an account {loginLink}.", - "ExpenseForm.StepExpense": "Upload one or multiple receipt", - "ExpenseForm.StepExpenseFundingRequest": "Set grant details", - "ExpenseForm.StepExpenseInvoice": "Детали счёта", - "ExpenseForm.StepPayeeInvoice": "Информация о получателе", "ExpenseForm.Submit": "Отправить расходы", - "ExpenseForm.SubmitRequest": "Отправить расходы", "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.", @@ -1747,7 +1721,6 @@ "ExpensesSubmittedByName": "Expenses submitted by {name}", "ExpenseSubmitters": "Expense Submitters", "ExpenseSummary.addNotesLabel": "Добавить примечания", - "ExpenseSummaryTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} other {Expense}} Summary to {collectiveName}", "ExpenseTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} other {Expense}} {id} to {collectiveName}", "Explore": "Explore", "Export.Format": "Экспортировать {format}", @@ -1967,7 +1940,6 @@ "fiscalHosting.youngActivists.description": "Who may lack the experience to manage their own legal entity.", "FiscalHostPolicies": "Fiscal Host Policies", "fiscaltos": "terms of fiscal sponsorship", - "fIsGOi": "No, continue editing", "FJBnaq": "Client ID and client secret", "FKMNjH": "You won't be able to resume the synchronization in the future.", "FLqc8O": "Виртуальная карта добавлена", @@ -3347,7 +3319,6 @@ "PJubm9": "Финансовое отслеживание и прозрачность означают, что отчетность составляется сама", "pKF7TO": "Require the contributor to provide their physical address when they contribute more than:", "Pkorog": "Account under verification", - "Pkq+ZR": "Please make sure that all the expense items in this expense belong to the selected expense category. If needed, you may submit additional items in separate expenses with different expense categories.", "Pkx+Wj": "Share results", "platform": "Платформа", "platform.explainerVideo": "Видео о платформе", @@ -3381,8 +3352,6 @@ "PreviewFeatures.CollectiveOverview.Welcome.Description": "We’ve created this space for you to keep on top of everything happening in your Collective, please let us know how we can make it better!", "PreviewFeatures.CollectiveOverview.Welcome.Title": "Welcome to your new Collective Overview", "PreviewFeatures.crowdfundingRedesignDescription": "Be part of the crowdfunding redesign effort and get access to previews of new crowdfunding and profile pages. Experience enhanced profile pages with separate fundraising and storytelling views, clearer relationships between collectives and their projects, improved goal tracking, and better collective narratives that showcase your impact and long-term sustainability.{newLine}{newLine}Check out the blog post for more details.", - "PreviewFeatures.inlineEditExpenseDescription": "Edit expense details directly in the Dashboard without navigating to separate pages.", - "PreviewFeatures.inlineEditExpenseTitle": "Inline Expense Editing", "PreviewFeatures.keyboardShortcutsDescription": "Navigate the expense flow more efficiently with keyboard shortcuts. Speed up your workflow and reduce mouse dependency for common actions.", "PreviewFeatures.keyboardShortcutsExpenseDetailsAttachments": "{leftKey} and {rightKey} to navigate through attachments.", "PreviewFeatures.keyboardShortcutsExpenseDetailsClose": "{key} to close Expense details.", @@ -3396,8 +3365,6 @@ "PreviewFeatures.keyboardShortcutsExpenseListSecurity": "{key} to check Security alerts for selected Expense.", "PreviewFeatures.keyboardShortcutsTitle": "Keyboard Shortcuts", "PreviewFeatures.LimitedAccess": "Limited preview", - "PreviewFeatures.newExpenseFlowDescription": "Experience an improved expense submission flow in the Dashboard with better user experience, clearer navigation, and enhanced form validation.", - "PreviewFeatures.newExpenseFlowTitle": "New Expense Submission Flow", "PreviewFeatures.publicBeta": "Public beta", "PreviewFeatures.searchCommandDescription": "Discover a new way to search for collectives, transactions, expenses, and more through an intuitive command menu interface. Access information faster with powerful search capabilities.", "PreviewFeatures.searchCommandTitle": "Search Command Menu", @@ -3455,7 +3422,6 @@ "pricing.start.feeNote": "You wont be able to charge Collectives or set any Host Fee.", "pricing.title": "Наша ценовая структура", "pricing.unlimitedCollectives": "Unlimited Collectives", - "privacypolicy": "политика конфиденциальности", "Private": "Private", "PrivateCommentsMessage.Allowed": "Ваши комментарии приватны.", "PrivateCommentsMessage.AllowedDetails": "Expenses comments are private, because they sometimes contain confidential information such as payment details. Only the expense submitter, the admins and the accountants can see them.", @@ -4247,7 +4213,6 @@ "TopContributors.Individuals": "Физические лица", "TopContributors.Organizations": "Организации", "tos": "условия использования", - "TOSAndPrivacyPolicyAgreement": "Я согласен с {toslink} и {privacylink} Open Collective.", "total": "total", "TotalAmount": "Итого", "TotalAmountWithoutFee": "Итого (без комиссии)", @@ -4487,14 +4452,10 @@ "UpgradePlanCTA.title.DISABLED": "Upgrade Required", "UpgradePlanCTA.title.UNSUPPORTED": "Feature not supported for your account", "UpgradePlanCTA.upgradeButton": "Upgrade your plan", - "UploadDocumentation": "Загрузить документацию", - "UploadDocumentationDescription": "If you want to include any documentation, you can upload it here.", "uploadImage.isDragActive": "Бросьте, будто горит 🔥", "uploadImage.isDragReject": "🚫 Такой тип файла не принимается", "uploadImage.isUploading": "Uploading image...", "uploadImage.sizeRejected": "Image resolution needs to be between {minResolution} and {maxResolution}. File size must be below {maxFileSize}.", - "UploadInvoice": "Загрузить счет", - "UploadInvoiceDescription": "If you already have an invoice document, you can upload it here.", "uQguR/": "All Collectives", "Uqkhct": "No virtual cards", "uR+TjD": "Are you sure you want to delete this payout method?", @@ -4556,8 +4517,6 @@ "VendorsAndOrganizations.Description": "Manage all the external organizations you work with as vendors and quickly surface all the activity between your hosted Collectives and other platform Organizations.", "vEQcqS": "Two factor authentication added", "verificationCriteria": "verification criteria", - "VerifyEmailAddress": "Подтвердите ваш адрес электронной почты", - "VerifyEmailInstructions": "A verification email has been sent to {email}. Click the link to complete submitting this expense. If you have not received the email, please check your spam.", "VErmYl": "Duplicate grant", "Vf1x2A": "Joined the platform on", "VfJsl4": "Beneficiary", @@ -4939,7 +4898,6 @@ "zNBAqh": "This value is an estimate. Please set the exact amount received if known.", "ZNBeE4": "Exactly {amount}", "ZNzyMo": "Export taxes and payment processor fees as columns", - "zO+Zv3": "Additional attachments", "zoC8Gb": "The payee is covering the fees", "zOk9pq": "Go to activity log", "ZonfjV": "You cannot edit this collective", diff --git a/lang/sk-SK.json b/lang/sk-SK.json index a39901deec7..e4c17f39afd 100644 --- a/lang/sk-SK.json +++ b/lang/sk-SK.json @@ -30,7 +30,6 @@ "+RvjCt": "Karta úspešne vymazaná", "+S3jp9": "Nová aktualizácia", "+SSp9V": "We encountered an issue loading this {type,select,section{section}other{page}}. Please reload the page or contact support if the problem persists.", - "+t6c4i": "Pokyny pre kategóriu účtu", "+U6ozc": "Typ", "+UdXIM": "Účet vytvorený prostredníctvom služby GitHub", "+UwJxq": "Očakávané poplatky hostiteľa", @@ -148,13 +147,11 @@ "2u8jmQ": "Povolenie kĺzavého limitu 2FA pre výplaty", "2Un6+c": "KYC Information", "2uzHxT": "Importovať CSV", - "3135/i": "Mena výdavku", "322m9e": "Správa potrebuje mať aspoň 10 znakov", "32rBNK": "Terms of Service", "34Up+l": "Zobraziť viac", "35Jfcr": "Darčekové karty umožňujú vašim zamestnancom alebo členom komunity podporovať projekty open source, ktoré majú radi. Viac informácií.", "37zpJw": "Search countries...", - "38dzz9": "Kategória výdavkov", "38fPNE": "Nastavenia importu transakcií", "3A7J9A": "Nepodarilo sa nám nájsť hostiteľa, ktorý by spĺňal všetky vaše kritériá.", "3ABdi3": "Tento účet neexistuje", @@ -577,7 +574,6 @@ "AzGwgz": "Minimálna čiastka na prevod do {selectedCurrency} je {minAmountForSelectedCurrency}", "AZmsFu": "Účet odmietol príspevok od ", "AzRKUx": "Create Beneficiary", - "b++lom": "Áno, zrušiť úpravy", "B+fVMt": "Viac ako {amount}", "B+NQ+r": " načrtol(la) výdavok {expenseDescription} na účet ", "b07w+D": "Úroveň", @@ -1174,7 +1170,6 @@ "CreatedAt": "Created At", "CreatedBy": "podľa používateľa {name}", "CreateEvent": "Vytvoriť udalosť", - "CreateExpense.HelpCreateInfo": "Žiadosť o platbu od Kolektívu {collective}. Výdavky budú spracované po schválení správcom Kolektívu. Suma, popis a vaše meno z profilu sú verejné, ale prílohy, údaje o platbe a ďalšie osobné údaje sú súkromné.", "CreateExpenseFAQ.getPaid": "Ako sa môžem nechať vyplatiť od Kolektívu?", "CreateExpenseFAQ.getPaidDetails": "Odošlite výdavok a uveďte vaše platobné údaje.", "CreateExpenseFAQ.howAreApproved": "Ako sa schvaľujú výdavky?", @@ -1297,7 +1292,6 @@ "DhxIpu": "Nový komentár k ", "DinF1w": "Čistá suma pre {collective}", "dIoEln": "Total disbursed", - "DismissableHelp.DontShowAgain": "Dobre, už mi to neukazujte", "DisplayName": "Display Name", "DJnUUS": "This expense has not been approved", "dK5ItS": "V predvolenom nastavení môžu výdavky v mene predajcov predkladať len správcovia fiškálnych hostiteľov. Ostatným používateľom, ktorí predkladajú výdavky kolektívom, ktoré hostíte, môžete povoliť, aby tiež predkladali výdavky v mene predajcov.", @@ -1583,7 +1577,6 @@ "Expense.Direction.Received": "Received", "Expense.Direction.Submitted": "Submitted", "expense.draft": "Draft", - "Expense.edit": "Upraviť výdavok", "expense.editDetails": "Edit expense details", "expense.editNotes": "Edit notes", "expense.editPaidBy": "Edit paid by", @@ -1591,15 +1584,12 @@ "expense.editPayee": "Edit payee", "expense.editTitle": "Edit expense title", "expense.editType": "Edit expense type", - "Expense.EnterExpenseTitle": "Názov výdavku (Verejné)", - "Expense.EnterRequestSubject": "Zadajte predmet grantu (Public)", "Expense.GoToPage": "Prejsť na stránku výdavkov", "expense.hostFeeInCollectiveCurrency": "poplatok hostiteľa", "expense.incurredAt": "Dátum", "expense.invite.declined": "Invite declined", "Expense.InviteIsOnItsWay.Description": "Výzva na predloženie týchto výdavkov bola zaslaná na adresu {email}. Po potvrdení a dokončení procesu sa zobrazí v zozname výdavkov.", "Expense.InvoiceItems": "Položky faktúry", - "Expense.JoinAndSubmit": "Pripojiť sa a odoslať", "expense.linked": "Expense linked", "expense.markAsPaid": "Označiť ako zaplatené", "Expense.MarkAsSpamLabel": "Why are you marking this expense as spam?", @@ -1628,7 +1618,6 @@ "Expense.PayTo": "Príjemca", "expense.pending": "Čakajúce", "expense.pickPayoutMethod": "Pick new payout method", - "Expense.PrivacyWarning": "Tieto informácie sú verejné. Do tohto poľa neuvádzajte žiadne súkromné údaje.", "Expense.PrivateNote": "Súkromná poznámka", "Expense.Receipt": "Receipt", "expense.ReceiptRequired": "Vyžaduje sa príjmový doklad", @@ -1646,21 +1635,15 @@ "Expense.RequestDetails": "Vyžiadať podrobnosti", "Expense.RequestedBy": "Invited by {name}", "expense.requestReApproval.btn": "Request re-approval", - "Expense.SaveExpense": "Save Expense", - "Expense.SaveReceipt": "Uložiť príjmový doklad", "expense.schedule.btn": "Plánovať platbu pomocou {paymentMethod}", "expense.scheduledForPayment": "Naplánovaná platba", "Expense.SeeDetails": "See expense details", "Expense.SendInvite": "Odoslať pozvánku", - "Expense.SignUpInfoBox": "You need to create an account to receive a payment from {collectiveName}, by clicking 'Join and Submit' you agree to create an account on {WebsiteName}.", "expense.spam.notAllowed": "You can't mark your own expenses as spam", "expense.status": "Stav", "Expense.Status.Refunded": "Refunded", "Expense.Submitted": "Predložený výdavok", "Expense.SubmittedBy": "Predkladá {name}", - "Expense.SuccessPage": "Na tejto stránke môžete upravovať alebo kontrolovať aktualizácie.", - "Expense.Summary.Recurring.CheckboxDescription": "Túto možnosť vyberte, ak si želáte pravidelne automaticky odosielať kópiu tejto faktúry.", - "Expense.Summary.Recurring.CheckboxTitle": "Toto je opakujúci sa výdavok?", "Expense.type": "Type", "expense.type": "Typ", "Expense.Type.Charge": "Virtual Card Charge", @@ -1677,16 +1660,12 @@ "ExpenseAmount": "Suma výdavkov", "ExpenseApprover": "Expense Approver", "ExpenseForm.AddAttachedFile": "Pridať nový dokument", - "ExpenseForm.AddGrantItem": "Pridať položku grantu", - "ExpenseForm.AddLineItem": "Pridať novú položku", - "ExpenseForm.AddReceipt": "Pridať nový príjmový doklad", "ExpenseForm.AddressLabel": "Fyzická adresa", "ExpenseForm.CancelEditExpense": "Cancel Edit", "ExpenseForm.ChooseCountry": "Vybrať krajinu", "ExpenseForm.ClearExpenseForm": "Clear Form", "ExpenseForm.ConfirmCancelEditExpense": "Are you sure you want to cancel the edits?", "ExpenseForm.ConfirmClearExpenseForm": "Are you sure you want to clear the expense form?", - "ExpenseForm.DescriptionPlaceholder": "Tu zadajte názov výdavku...", "ExpenseForm.FundingRequestDescription": "Požiadajte o grant na svoj projekt alebo iniciatívu.", "ExpenseForm.GrantSubjectPlaceholder": "e.g., Research, software development, etc.", "ExpenseForm.inviteAdditionalInfo": "Chcete zadať údaje o výplate, napríklad adresu PayPal alebo bankový účet?", @@ -1707,12 +1686,7 @@ "ExpenseForm.SavePayout": "Uložiť tieto informácie pre budúce výplaty", "ExpenseForm.SignUp.OrgAdminNote": "Ak si želáte predložiť výdavky, potrebujete byť správcom organizácie.", "ExpenseForm.SignUp.SignIn": "Tento e-mail použijeme pri vytváraní vášho konta. Ak už máte konto, kliknite sem: {loginLink}.", - "ExpenseForm.StepExpense": "Predložiť jeden alebo viacero príjmových dokladov", - "ExpenseForm.StepExpenseFundingRequest": "Nastaviť podrobnosti o grante", - "ExpenseForm.StepExpenseInvoice": "Nastaviť údaje na faktúre", - "ExpenseForm.StepPayeeInvoice": "Informácie o príjemcovi", "ExpenseForm.Submit": "Predložiť výdavok", - "ExpenseForm.SubmitRequest": "Predložiť požiadavku", "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.", @@ -1747,7 +1721,6 @@ "ExpensesSubmittedByName": "Expenses submitted by {name}", "ExpenseSubmitters": "Expense Submitters", "ExpenseSummary.addNotesLabel": "Pridať poznámky", - "ExpenseSummaryTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} Summary to {collectiveName}", "ExpenseTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} {id} to {collectiveName}", "Explore": "Explore", "Export.Format": "Export do formátu {format}", @@ -1967,7 +1940,6 @@ "fiscalHosting.youngActivists.description": "Tí, ktorí môžu mať nedostatok skúseností s riadením vlastnej právnickej osoby.", "FiscalHostPolicies": "Fiscal Host Policies", "fiscaltos": "podmienky fiškálneho sponzorstva", - "fIsGOi": "Nie, pokračovať v úpravách", "FJBnaq": "Identifikátor a tajomstvo klienta", "FKMNjH": "You won't be able to resume the synchronization in the future.", "FLqc8O": "Virtual card added", @@ -3347,7 +3319,6 @@ "PJubm9": "Finančné sledovanie a transparentnosť znamenajú, že správy sa píšu samé", "pKF7TO": "Require the contributor to provide their physical address when they contribute more than:", "Pkorog": "Account under verification", - "Pkq+ZR": "Please make sure that all the expense items in this expense belong to the selected expense category. If needed, you may submit additional items in separate expenses with different expense categories.", "Pkx+Wj": "Zdieľať výsledok", "platform": "Platforma", "platform.explainerVideo": "Vysvetľujúce video", @@ -3381,8 +3352,6 @@ "PreviewFeatures.CollectiveOverview.Welcome.Description": "We’ve created this space for you to keep on top of everything happening in your Collective, please let us know how we can make it better!", "PreviewFeatures.CollectiveOverview.Welcome.Title": "Welcome to your new Collective Overview", "PreviewFeatures.crowdfundingRedesignDescription": "Be part of the crowdfunding redesign effort and get access to previews of new crowdfunding and profile pages. Experience enhanced profile pages with separate fundraising and storytelling views, clearer relationships between collectives and their projects, improved goal tracking, and better collective narratives that showcase your impact and long-term sustainability.{newLine}{newLine}Check out the blog post for more details.", - "PreviewFeatures.inlineEditExpenseDescription": "Edit expense details directly in the Dashboard without navigating to separate pages.", - "PreviewFeatures.inlineEditExpenseTitle": "Inline Expense Editing", "PreviewFeatures.keyboardShortcutsDescription": "Navigate the expense flow more efficiently with keyboard shortcuts. Speed up your workflow and reduce mouse dependency for common actions.", "PreviewFeatures.keyboardShortcutsExpenseDetailsAttachments": "{leftKey} and {rightKey} to navigate through attachments.", "PreviewFeatures.keyboardShortcutsExpenseDetailsClose": "{key} to close Expense details.", @@ -3396,8 +3365,6 @@ "PreviewFeatures.keyboardShortcutsExpenseListSecurity": "{key} to check Security alerts for selected Expense.", "PreviewFeatures.keyboardShortcutsTitle": "Keyboard Shortcuts", "PreviewFeatures.LimitedAccess": "Limited preview", - "PreviewFeatures.newExpenseFlowDescription": "Experience an improved expense submission flow in the Dashboard with better user experience, clearer navigation, and enhanced form validation.", - "PreviewFeatures.newExpenseFlowTitle": "New Expense Submission Flow", "PreviewFeatures.publicBeta": "Public beta", "PreviewFeatures.searchCommandDescription": "Discover a new way to search for collectives, transactions, expenses, and more through an intuitive command menu interface. Access information faster with powerful search capabilities.", "PreviewFeatures.searchCommandTitle": "Search Command Menu", @@ -3455,7 +3422,6 @@ "pricing.start.feeNote": "Nebudete môcť účtovať Kolektívom ani nastaviť žiadny Poplatok Hostiteľa.", "pricing.title": "Naša cenová štruktúra", "pricing.unlimitedCollectives": "Neobmedzené kolektívy", - "privacypolicy": "zásady ochrany osobných údajov", "Private": "Private", "PrivateCommentsMessage.Allowed": "Vaše komentáre sú súkromné.", "PrivateCommentsMessage.AllowedDetails": "Expenses comments are private, because they sometimes contain confidential information such as payment details. Only the expense submitter, the admins and the accountants can see them.", @@ -4247,7 +4213,6 @@ "TopContributors.Individuals": "Jednotlivci", "TopContributors.Organizations": "Organizácie", "tos": "podmienky poskytovania služieb", - "TOSAndPrivacyPolicyAgreement": "Súhlasím s {toslink} a {privacylink} z Open Collective.", "total": "total", "TotalAmount": "Celková čiastka", "TotalAmountWithoutFee": "Celková čiastka (bez poplatkov)", @@ -4487,14 +4452,10 @@ "UpgradePlanCTA.title.DISABLED": "Upgrade Required", "UpgradePlanCTA.title.UNSUPPORTED": "Feature not supported for your account", "UpgradePlanCTA.upgradeButton": "Upgrade your plan", - "UploadDocumentation": "Odovzdať dokumentáciu", - "UploadDocumentationDescription": "Ak chcete pripojiť akúkoľvek dokumentáciu, môžete ju odovzdať tu.", "uploadImage.isDragActive": "Pustite to ako niečo horúce 🔥", "uploadImage.isDragReject": "🚫 Tento typ súboru nie je povolený", "uploadImage.isUploading": "Uploading image...", "uploadImage.sizeRejected": "Rozlíšenie obrázka musí byť medzi {minResolution} a {maxResolution}. Veľkosť súboru musí byť menšia ako {maxFileSize}.", - "UploadInvoice": "Odovzdať faktúru", - "UploadInvoiceDescription": "Ak už máte dokument s faktúrou, môžete ho odovzdať tu.", "uQguR/": "Všetky kolektívy", "Uqkhct": "No virtual cards", "uR+TjD": "Are you sure you want to delete this payout method?", @@ -4556,8 +4517,6 @@ "VendorsAndOrganizations.Description": "Manage all the external organizations you work with as vendors and quickly surface all the activity between your hosted Collectives and other platform Organizations.", "vEQcqS": "Two factor authentication added", "verificationCriteria": "verification criteria", - "VerifyEmailAddress": "Overiť vašu e-mailovú adresu", - "VerifyEmailInstructions": "Overovací e-mail bol odoslaný na adresu {email}. Kliknutím na odkaz dokončite odoslanie tohto výdavku. Ak ste e-mail nedostali, skontrolujte si, prosím, nevyžiadanú poštu.", "VErmYl": "Duplicate grant", "Vf1x2A": "Joined the platform on", "VfJsl4": "Beneficiary", @@ -4939,7 +4898,6 @@ "zNBAqh": "This value is an estimate. Please set the exact amount received if known.", "ZNBeE4": "Exactly {amount}", "ZNzyMo": "Export taxes and payment processor fees as columns", - "zO+Zv3": "Additional attachments", "zoC8Gb": "Poplatky hradí príjemca", "zOk9pq": "Go to activity log", "ZonfjV": "Tento kolektív nemôžete upravovať", diff --git a/lang/sv-SE.json b/lang/sv-SE.json index e6decb9ffe2..7358e4c247e 100644 --- a/lang/sv-SE.json +++ b/lang/sv-SE.json @@ -30,7 +30,6 @@ "+RvjCt": "Kort borttaget", "+S3jp9": "Ny Uppdatering", "+SSp9V": "We encountered an issue loading this {type,select,section{section}other{page}}. Please reload the page or contact support if the problem persists.", - "+t6c4i": "Account Category Instructions", "+U6ozc": "Typ", "+UdXIM": " skapad genom GitHub", "+UwJxq": "Förvendade Värds kostnader", @@ -148,13 +147,11 @@ "2u8jmQ": "Aktivera rullande gräns 2FA för utbetalningar", "2Un6+c": "KYC Information", "2uzHxT": "Importera CSV", - "3135/i": "Utgiftsvaluta", "322m9e": "Meddelandet måste vara minst 10 tecken långt", "32rBNK": "Terms of Service", "34Up+l": "Visa mer", "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "37zpJw": "Sök länder...", - "38dzz9": "Utgiftskategori", "38fPNE": "Transaction Import Settings", "3A7J9A": "We could not find a host that matches all your criteria.", "3ABdi3": "Detta konto finns inte", @@ -577,7 +574,6 @@ "AzGwgz": "The minimum amount for transferring to {selectedCurrency} is {minAmountForSelectedCurrency}", "AZmsFu": " rejected contribution from ", "AzRKUx": "Create Beneficiary", - "b++lom": "Ja, avbryt redigering", "B+fVMt": "More than {amount}", "B+NQ+r": " drafted an {expenseDescription} on ", "b07w+D": "Nivå", @@ -1174,7 +1170,6 @@ "CreatedAt": "Created At", "CreatedBy": "av {name}", "CreateEvent": "Create event", - "CreateExpense.HelpCreateInfo": "Begär betalning från {collective}. Utgifter kommer att behandlas när de har godkänts av administratören för det kollektivet. Beloppet, beskrivningen och ditt profilnamn är offentliga, men bilagor, betalningsdetaljer och annan personlig information hålls privat.", "CreateExpenseFAQ.getPaid": "Hur får jag betalt från ett kollektiv?", "CreateExpenseFAQ.getPaidDetails": "Skicka in en utgift och ange din betalningsinformation.", "CreateExpenseFAQ.howAreApproved": "Hur godkänns utgifter?", @@ -1297,7 +1292,6 @@ "DhxIpu": "Ny kommentar på ", "DinF1w": "Net Amount for {collective}", "dIoEln": "Total disbursed", - "DismissableHelp.DontShowAgain": "Ok, visa mig inte igen", "DisplayName": "Display Name", "DJnUUS": "This expense has not been approved", "dK5ItS": "By default only fiscal host administrators can submit expenses on behalf of vendors. You can allow other users who submit expenses to collectives you host to also submit expenses on behalf vendors.", @@ -1583,7 +1577,6 @@ "Expense.Direction.Received": "Mottagen", "Expense.Direction.Submitted": "Inskickad", "expense.draft": "Utkast", - "Expense.edit": "Redigera utgift", "expense.editDetails": "Edit expense details", "expense.editNotes": "Edit notes", "expense.editPaidBy": "Edit paid by", @@ -1591,15 +1584,12 @@ "expense.editPayee": "Edit payee", "expense.editTitle": "Redigera utgiftstitel", "expense.editType": "Redigera utgiftstyp", - "Expense.EnterExpenseTitle": "Utgiftens titel (Publik)", - "Expense.EnterRequestSubject": "Ange bidragsämne (offentligt)", "Expense.GoToPage": "Sida för utgifter", "expense.hostFeeInCollectiveCurrency": "värdavgift", "expense.incurredAt": "Datum", "expense.invite.declined": "Invite declined", "Expense.InviteIsOnItsWay.Description": "En inbjudan att skicka in denna kostnad har skickats till {email}. När de bekräftar och avslutar processen, kommer det att visas på listan över utgifter.", "Expense.InvoiceItems": "Fakturaposter", - "Expense.JoinAndSubmit": "Gå med och skicka", "expense.linked": "Expense linked", "expense.markAsPaid": "Markera som betald", "Expense.MarkAsSpamLabel": "Why are you marking this expense as spam?", @@ -1628,7 +1618,6 @@ "Expense.PayTo": "Betala till", "expense.pending": "Pågående", "expense.pickPayoutMethod": "Pick new payout method", - "Expense.PrivacyWarning": "Denna information är offentlig. Lägg inte in några privata detaljer i detta fält.", "Expense.PrivateNote": "Privat anteckning", "Expense.Receipt": "Kvitto", "expense.ReceiptRequired": "Kvitto krävs", @@ -1646,21 +1635,15 @@ "Expense.RequestDetails": "Information om begäran", "Expense.RequestedBy": "Inbjuden av {name}", "expense.requestReApproval.btn": "Request re-approval", - "Expense.SaveExpense": "Save Expense", - "Expense.SaveReceipt": "Spara kvitto", "expense.schedule.btn": "Schemalägg att betala med {paymentMethod}", "expense.scheduledForPayment": "Schemalagd för betalning", "Expense.SeeDetails": "See expense details", "Expense.SendInvite": "Skicka inbjudan", - "Expense.SignUpInfoBox": "You need to create an account to receive a payment from {collectiveName}, by clicking 'Join and Submit' you agree to create an account on {WebsiteName}.", "expense.spam.notAllowed": "You can't mark your own expenses as spam", "expense.status": "Status", "Expense.Status.Refunded": "Återbetald", "Expense.Submitted": "Utgift inskickad", "Expense.SubmittedBy": "Inskickad av {name}", - "Expense.SuccessPage": "Du kan redigera eller granska uppdateringar på denna sida.", - "Expense.Summary.Recurring.CheckboxDescription": "Välj detta alternativ för att automatiskt skicka in en kopia av denna faktura regelbundet.", - "Expense.Summary.Recurring.CheckboxTitle": "Är detta en återkommande kostnad?", "Expense.type": "Typ", "expense.type": "Typ", "Expense.Type.Charge": "Virtual Card Charge", @@ -1677,16 +1660,12 @@ "ExpenseAmount": "Belopp för utgift", "ExpenseApprover": "Expense Approver", "ExpenseForm.AddAttachedFile": "Lägg till ett dokument", - "ExpenseForm.AddGrantItem": "Lägg till bidragsobjekt", - "ExpenseForm.AddLineItem": "Lägg till nytt objekt", - "ExpenseForm.AddReceipt": "Lägg till nytt kvitto", "ExpenseForm.AddressLabel": "Fysisk adress", "ExpenseForm.CancelEditExpense": "Avbryt redigering", "ExpenseForm.ChooseCountry": "Välj land", "ExpenseForm.ClearExpenseForm": "Rensa formulär", "ExpenseForm.ConfirmCancelEditExpense": "Är du säker på att du vill kasta ändringarna?", "ExpenseForm.ConfirmClearExpenseForm": "Är du säker på att du vill rensa formuläret för utgifter?", - "ExpenseForm.DescriptionPlaceholder": "Ange titel på utgifterna här...", "ExpenseForm.FundingRequestDescription": "Begär bidrag för ditt projekt eller initiativ.", "ExpenseForm.GrantSubjectPlaceholder": "e.g., Research, software development, etc.", "ExpenseForm.inviteAdditionalInfo": "Vill du ange betalningsinformation, till exempel en PayPal-adress eller ett bankkonto?", @@ -1707,12 +1686,7 @@ "ExpenseForm.SavePayout": "Spara denna information för framtida utbetalningar", "ExpenseForm.SignUp.OrgAdminNote": "Du måste vara administratör för organisationen för att skicka in utgifter.", "ExpenseForm.SignUp.SignIn": "Vi kommer att använda denna e-post för att skapa ditt konto. Om du redan har ett konto {loginLink}.", - "ExpenseForm.StepExpense": "Ladda upp ett eller flera kvitto", - "ExpenseForm.StepExpenseFundingRequest": "Ange detaljer om bidraget", - "ExpenseForm.StepExpenseInvoice": "Ange fakturainformation", - "ExpenseForm.StepPayeeInvoice": "Betalningsmottagarens information", "ExpenseForm.Submit": "Skicka in utgift", - "ExpenseForm.SubmitRequest": "Skicka begäran", "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.", @@ -1747,7 +1721,6 @@ "ExpensesSubmittedByName": "Expenses submitted by {name}", "ExpenseSubmitters": "Expense Submitters", "ExpenseSummary.addNotesLabel": "Lägg till anteckningar", - "ExpenseSummaryTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} Summary to {collectiveName}", "ExpenseTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} {id} to {collectiveName}", "Explore": "Utforska", "Export.Format": "Exportera {format}", @@ -1967,7 +1940,6 @@ "fiscalHosting.youngActivists.description": "Som kanske saknar erfarenhet att hantera en egen organisation.", "FiscalHostPolicies": "Fiscal Host Policies", "fiscaltos": "villkor för värdskap", - "fIsGOi": "Nej, fortsätt redigera", "FJBnaq": "Klient-ID och klienthemlighet", "FKMNjH": "You won't be able to resume the synchronization in the future.", "FLqc8O": "Virtuellt kort tillagt", @@ -3347,7 +3319,6 @@ "PJubm9": "Spårbarhet och transparens innebär att rapportering skriver sig själv", "pKF7TO": "Require the contributor to provide their physical address when they contribute more than:", "Pkorog": "Account under verification", - "Pkq+ZR": "Please make sure that all the expense items in this expense belong to the selected expense category. If needed, you may submit additional items in separate expenses with different expense categories.", "Pkx+Wj": "Dela resultat", "platform": "Plattform", "platform.explainerVideo": "Videoguide", @@ -3381,8 +3352,6 @@ "PreviewFeatures.CollectiveOverview.Welcome.Description": "We’ve created this space for you to keep on top of everything happening in your Collective, please let us know how we can make it better!", "PreviewFeatures.CollectiveOverview.Welcome.Title": "Welcome to your new Collective Overview", "PreviewFeatures.crowdfundingRedesignDescription": "Be part of the crowdfunding redesign effort and get access to previews of new crowdfunding and profile pages. Experience enhanced profile pages with separate fundraising and storytelling views, clearer relationships between collectives and their projects, improved goal tracking, and better collective narratives that showcase your impact and long-term sustainability.{newLine}{newLine}Check out the blog post for more details.", - "PreviewFeatures.inlineEditExpenseDescription": "Edit expense details directly in the Dashboard without navigating to separate pages.", - "PreviewFeatures.inlineEditExpenseTitle": "Inline Expense Editing", "PreviewFeatures.keyboardShortcutsDescription": "Navigate the expense flow more efficiently with keyboard shortcuts. Speed up your workflow and reduce mouse dependency for common actions.", "PreviewFeatures.keyboardShortcutsExpenseDetailsAttachments": "{leftKey} and {rightKey} to navigate through attachments.", "PreviewFeatures.keyboardShortcutsExpenseDetailsClose": "{key} to close Expense details.", @@ -3396,8 +3365,6 @@ "PreviewFeatures.keyboardShortcutsExpenseListSecurity": "{key} to check Security alerts for selected Expense.", "PreviewFeatures.keyboardShortcutsTitle": "Tangentbordsgenvägar", "PreviewFeatures.LimitedAccess": "Limited preview", - "PreviewFeatures.newExpenseFlowDescription": "Experience an improved expense submission flow in the Dashboard with better user experience, clearer navigation, and enhanced form validation.", - "PreviewFeatures.newExpenseFlowTitle": "New Expense Submission Flow", "PreviewFeatures.publicBeta": "Public beta", "PreviewFeatures.searchCommandDescription": "Discover a new way to search for collectives, transactions, expenses, and more through an intuitive command menu interface. Access information faster with powerful search capabilities.", "PreviewFeatures.searchCommandTitle": "Search Command Menu", @@ -3455,7 +3422,6 @@ "pricing.start.feeNote": "Du kommer inte att kunna debitera kollektiv eller ställa in någon värdavgift.", "pricing.title": "Vår prissättning", "pricing.unlimitedCollectives": "Obegränsade kollektiv", - "privacypolicy": "Integritetspolicy", "Private": "Privat", "PrivateCommentsMessage.Allowed": "Dina kommentarer är privata.", "PrivateCommentsMessage.AllowedDetails": "Expenses comments are private, because they sometimes contain confidential information such as payment details. Only the expense submitter, the admins and the accountants can see them.", @@ -4247,7 +4213,6 @@ "TopContributors.Individuals": "Individer", "TopContributors.Organizations": "Organisationer", "tos": "användarvillkor", - "TOSAndPrivacyPolicyAgreement": "Jag accepterar {toslink} och {privacylink} för Open Collective.", "total": "total", "TotalAmount": "Totalt belopp", "TotalAmountWithoutFee": "Totalt belopp (utan avgifter)", @@ -4487,14 +4452,10 @@ "UpgradePlanCTA.title.DISABLED": "Uppgradering krävs", "UpgradePlanCTA.title.UNSUPPORTED": "Feature not supported for your account", "UpgradePlanCTA.upgradeButton": "Uppgradera din plan", - "UploadDocumentation": "Ladda upp dokumentation", - "UploadDocumentationDescription": "Om du vill inkludera dokumentation kan du ladda upp den här.", "uploadImage.isDragActive": "Drop it like it's hot 🔥", "uploadImage.isDragReject": "🚫 Denna filtyp accepteras inte", "uploadImage.isUploading": "Laddar upp bild...", "uploadImage.sizeRejected": "Bildupplösningen behöver vara mellan {minResolution} och {maxResolution}. Filstorleken måste vara under {maxFileSize}.", - "UploadInvoice": "Ladda upp faktura", - "UploadInvoiceDescription": "Om du redan har en faktura kan du ladda upp det här.", "uQguR/": "Alla kollektiv", "Uqkhct": "Inga virtuella kort", "uR+TjD": "Are you sure you want to delete this payout method?", @@ -4556,8 +4517,6 @@ "VendorsAndOrganizations.Description": "Manage all the external organizations you work with as vendors and quickly surface all the activity between your hosted Collectives and other platform Organizations.", "vEQcqS": "Two factor authentication added", "verificationCriteria": "verifieringskriterier", - "VerifyEmailAddress": "Verifiera din e-postadress", - "VerifyEmailInstructions": "Ett verifieringsmeddelande har skickats till {email}. Klicka på länken för att slutföra denna utgift. Om du inte har fått e-postmeddelandet, vänligen kontrollera din skräppost.", "VErmYl": "Duplicate grant", "Vf1x2A": "Joined the platform on", "VfJsl4": "Beneficiary", @@ -4939,7 +4898,6 @@ "zNBAqh": "This value is an estimate. Please set the exact amount received if known.", "ZNBeE4": "Exakt {amount}", "ZNzyMo": "Export taxes and payment processor fees as columns", - "zO+Zv3": "Additional attachments", "zoC8Gb": "Betalningsmottagaren täcker avgifterna", "zOk9pq": "Gå till aktivitetslogg", "ZonfjV": "Du kan inte redigera detta kollektiv", diff --git a/lang/uk.json b/lang/uk.json index 26a29209032..27486c61891 100644 --- a/lang/uk.json +++ b/lang/uk.json @@ -30,7 +30,6 @@ "+RvjCt": "Картку успішно видалено", "+S3jp9": "Нове оновлення", "+SSp9V": "We encountered an issue loading this {type,select,section{section}other{page}}. Please reload the page or contact support if the problem persists.", - "+t6c4i": "Інструкції щодо категорії рахунків", "+U6ozc": "Тип", "+UdXIM": " створений через GitHub", "+UwJxq": "Очікувана комісія агента", @@ -148,13 +147,11 @@ "2u8jmQ": "Увімкнути обмеження 2FA для виплат", "2Un6+c": "KYC Information", "2uzHxT": "Імпортувати CSV", - "3135/i": "Витрати валюти", "322m9e": "Повідомлення повинно складатися принаймні з 10 символів", "32rBNK": "Terms of Service", "34Up+l": "Показати більше", "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "37zpJw": "Search countries...", - "38dzz9": "Категорія витрат", "38fPNE": "Transaction Import Settings", "3A7J9A": "Ми не змогли знайти агент, який відповідає усім вашим критеріям.", "3ABdi3": "Облікового запису не існує", @@ -577,7 +574,6 @@ "AzGwgz": "Мінімальна сума для переказу в {selectedCurrency} становить {minAmountForSelectedCurrency}", "AZmsFu": " rejected contribution from ", "AzRKUx": "Create Beneficiary", - "b++lom": "Так, скасувати редагування", "B+fVMt": "More than {amount}", "B+NQ+r": " оформив {expenseDescription} на ", "b07w+D": "Рівень", @@ -1174,7 +1170,6 @@ "CreatedAt": "Created At", "CreatedBy": "від {name}", "CreateEvent": "Створити подію", - "CreateExpense.HelpCreateInfo": "Request payment from {collective}. Expenses will be processed once approved by a Collective admin. The amount, description, and your profile name are public, but attachments, payment details, and other personal info is kept private.", "CreateExpenseFAQ.getPaid": "Як мені отримати виплату від Колективу?", "CreateExpenseFAQ.getPaidDetails": "Надішліть свої витрати та надайте ваші платіжні дані.", "CreateExpenseFAQ.howAreApproved": "Як затверджуються витрати?", @@ -1297,7 +1292,6 @@ "DhxIpu": "Новий коментар до ", "DinF1w": "Чиста сума для {collective}", "dIoEln": "Total disbursed", - "DismissableHelp.DontShowAgain": "Добре, більше не показувати мені це", "DisplayName": "Display Name", "DJnUUS": "This expense has not been approved", "dK5ItS": "By default only fiscal host administrators can submit expenses on behalf of vendors. You can allow other users who submit expenses to collectives you host to also submit expenses on behalf vendors.", @@ -1583,7 +1577,6 @@ "Expense.Direction.Received": "Отримано", "Expense.Direction.Submitted": "Надіслано", "expense.draft": "Чернетка", - "Expense.edit": "Редагувати витрату", "expense.editDetails": "Edit expense details", "expense.editNotes": "Edit notes", "expense.editPaidBy": "Edit paid by", @@ -1591,15 +1584,12 @@ "expense.editPayee": "Edit payee", "expense.editTitle": "Edit expense title", "expense.editType": "Edit expense type", - "Expense.EnterExpenseTitle": "Expense title (Public)", - "Expense.EnterRequestSubject": "Enter grant subject (Public)", "Expense.GoToPage": "Перейти на сторінку витрат", "expense.hostFeeInCollectiveCurrency": "комісія фіскального агента", "expense.incurredAt": "Дата", "expense.invite.declined": "Invite declined", "Expense.InviteIsOnItsWay.Description": "An invitation to submit this expense has been sent to {email}. Once they confirm and finish the process, it will appear on the expenses list.", "Expense.InvoiceItems": "Елементи рахунку-фактури", - "Expense.JoinAndSubmit": "Приєднатися та надіслати", "expense.linked": "Expense linked", "expense.markAsPaid": "Позначити оплаченим", "Expense.MarkAsSpamLabel": "Why are you marking this expense as spam?", @@ -1628,7 +1618,6 @@ "Expense.PayTo": "Одержувач", "expense.pending": "Очікування", "expense.pickPayoutMethod": "Pick new payout method", - "Expense.PrivacyWarning": "Ця інформація є публічною. Не вписуйте приватні дані у це поле.", "Expense.PrivateNote": "Приватна примітка", "Expense.Receipt": "Receipt", "expense.ReceiptRequired": "Потрібні квитанції", @@ -1646,21 +1635,15 @@ "Expense.RequestDetails": "Деталі запиту", "Expense.RequestedBy": "Invited by {name}", "expense.requestReApproval.btn": "Request re-approval", - "Expense.SaveExpense": "Save Expense", - "Expense.SaveReceipt": "Зберегти квитанцію", "expense.schedule.btn": "Розклад оплати за допомогою {paymentMethod}", "expense.scheduledForPayment": "Заплановано на оплату", "Expense.SeeDetails": "Див. подробиці витрат", "Expense.SendInvite": "Надіслати запрошення", - "Expense.SignUpInfoBox": "Вам потрібно створити обліковий запис, щоб отримати платіж від {collectiveName}, натиснувши кнопку \"Приєднатися та надіслати\", ви погоджуєтеся на створення облікового запису на {WebsiteName}.", "expense.spam.notAllowed": "You can't mark your own expenses as spam", "expense.status": "Стан", "Expense.Status.Refunded": "Повернено кошти", "Expense.Submitted": "Витрату надіслано", "Expense.SubmittedBy": "Надіслано {name}", - "Expense.SuccessPage": "Ви можете редагувати або переглядати оновлення на цій сторінці.", - "Expense.Summary.Recurring.CheckboxDescription": "Оберіть цю опцію, щоб автоматично надсилати копію цього рахунку-фактури на періодичній основі.", - "Expense.Summary.Recurring.CheckboxTitle": "Це повторювані витрати?", "Expense.type": "Тип", "expense.type": "Тип", "Expense.Type.Charge": "Virtual Card Charge", @@ -1677,16 +1660,12 @@ "ExpenseAmount": "Сума витрат", "ExpenseApprover": "Expense Approver", "ExpenseForm.AddAttachedFile": "Додати новий документ", - "ExpenseForm.AddGrantItem": "Додати елемент гранту", - "ExpenseForm.AddLineItem": "Додати новий елемент", - "ExpenseForm.AddReceipt": "Додати нову квитанцію", "ExpenseForm.AddressLabel": "Фізична адреса", "ExpenseForm.CancelEditExpense": "Скасувати виправлення", "ExpenseForm.ChooseCountry": "Вибрати країну", "ExpenseForm.ClearExpenseForm": "Очистити форму", "ExpenseForm.ConfirmCancelEditExpense": "Ви впевнені, що хочете скасувати правки?", "ExpenseForm.ConfirmClearExpenseForm": "Ви впевнені, що хочете очистити форму витрати?", - "ExpenseForm.DescriptionPlaceholder": "Введіть назву витрати тут...", "ExpenseForm.FundingRequestDescription": "Запросити грант на для вашого проєкту або ініціативи.", "ExpenseForm.GrantSubjectPlaceholder": "e.g., Research, software development, etc.", "ExpenseForm.inviteAdditionalInfo": "Чи бажаєте ви ввести дані для виплати, наприклад, адресу PayPal або банківський рахунок?", @@ -1707,12 +1686,7 @@ "ExpenseForm.SavePayout": "Зберегти ці дані для майбутніх виплат", "ExpenseForm.SignUp.OrgAdminNote": "You need to be an admin of the Organization to submit expenses.", "ExpenseForm.SignUp.SignIn": "Ми використаємо цей email для створення вашого облікового запису. Якщо у вас вже є обліковий запис {loginLink}.", - "ExpenseForm.StepExpense": "Вивантажте одну або кілька квитанцій", - "ExpenseForm.StepExpenseFundingRequest": "Встановити деталі гранту", - "ExpenseForm.StepExpenseInvoice": "Подробиці рахунку", - "ExpenseForm.StepPayeeInvoice": "Дані одержувача", "ExpenseForm.Submit": "Надіслати витрату", - "ExpenseForm.SubmitRequest": "Надіслати запит", "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.", @@ -1747,7 +1721,6 @@ "ExpensesSubmittedByName": "Expenses submitted by {name}", "ExpenseSubmitters": "Expense Submitters", "ExpenseSummary.addNotesLabel": "Додати примітки", - "ExpenseSummaryTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} Summary to {collectiveName}", "ExpenseTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} {id} to {collectiveName}", "Explore": "Explore", "Export.Format": "Експорт {format}", @@ -1967,7 +1940,6 @@ "fiscalHosting.youngActivists.description": "Who may lack the experience to manage their own legal entity.", "FiscalHostPolicies": "Fiscal Host Policies", "fiscaltos": "умови фінансового спонсорства", - "fIsGOi": "Ні, продовжити редагування", "FJBnaq": "Client ID and client secret", "FKMNjH": "You won't be able to resume the synchronization in the future.", "FLqc8O": "Virtual card added", @@ -3347,7 +3319,6 @@ "PJubm9": "Фінансовий контроль та прозорість означає, що звітність пише сама себе", "pKF7TO": "Require the contributor to provide their physical address when they contribute more than:", "Pkorog": "Account under verification", - "Pkq+ZR": "Please make sure that all the expense items in this expense belong to the selected expense category. If needed, you may submit additional items in separate expenses with different expense categories.", "Pkx+Wj": "Поділитися результатами", "platform": "Платформа", "platform.explainerVideo": "Відео з поясненням", @@ -3381,8 +3352,6 @@ "PreviewFeatures.CollectiveOverview.Welcome.Description": "We’ve created this space for you to keep on top of everything happening in your Collective, please let us know how we can make it better!", "PreviewFeatures.CollectiveOverview.Welcome.Title": "Welcome to your new Collective Overview", "PreviewFeatures.crowdfundingRedesignDescription": "Be part of the crowdfunding redesign effort and get access to previews of new crowdfunding and profile pages. Experience enhanced profile pages with separate fundraising and storytelling views, clearer relationships between collectives and their projects, improved goal tracking, and better collective narratives that showcase your impact and long-term sustainability.{newLine}{newLine}Check out the blog post for more details.", - "PreviewFeatures.inlineEditExpenseDescription": "Edit expense details directly in the Dashboard without navigating to separate pages.", - "PreviewFeatures.inlineEditExpenseTitle": "Inline Expense Editing", "PreviewFeatures.keyboardShortcutsDescription": "Navigate the expense flow more efficiently with keyboard shortcuts. Speed up your workflow and reduce mouse dependency for common actions.", "PreviewFeatures.keyboardShortcutsExpenseDetailsAttachments": "{leftKey} and {rightKey} to navigate through attachments.", "PreviewFeatures.keyboardShortcutsExpenseDetailsClose": "{key} to close Expense details.", @@ -3396,8 +3365,6 @@ "PreviewFeatures.keyboardShortcutsExpenseListSecurity": "{key} to check Security alerts for selected Expense.", "PreviewFeatures.keyboardShortcutsTitle": "Keyboard Shortcuts", "PreviewFeatures.LimitedAccess": "Limited preview", - "PreviewFeatures.newExpenseFlowDescription": "Experience an improved expense submission flow in the Dashboard with better user experience, clearer navigation, and enhanced form validation.", - "PreviewFeatures.newExpenseFlowTitle": "New Expense Submission Flow", "PreviewFeatures.publicBeta": "Public beta", "PreviewFeatures.searchCommandDescription": "Discover a new way to search for collectives, transactions, expenses, and more through an intuitive command menu interface. Access information faster with powerful search capabilities.", "PreviewFeatures.searchCommandTitle": "Search Command Menu", @@ -3455,7 +3422,6 @@ "pricing.start.feeNote": "You wont be able to charge Collectives or set any Host Fee.", "pricing.title": "Структура наших цін", "pricing.unlimitedCollectives": "Необмежена кількість колективів", - "privacypolicy": "політика приватності", "Private": "Private", "PrivateCommentsMessage.Allowed": "Ваші коментарі приватні.", "PrivateCommentsMessage.AllowedDetails": "Expenses comments are private, because they sometimes contain confidential information such as payment details. Only the expense submitter, the admins and the accountants can see them.", @@ -4247,7 +4213,6 @@ "TopContributors.Individuals": "Особи", "TopContributors.Organizations": "Організації", "tos": "умови користування", - "TOSAndPrivacyPolicyAgreement": "Я погоджуюсь з {toslink} та {privacylink} Open Collective.", "total": "total", "TotalAmount": "Загальна сума", "TotalAmountWithoutFee": "Загальна сума (без комісії)", @@ -4487,14 +4452,10 @@ "UpgradePlanCTA.title.DISABLED": "Upgrade Required", "UpgradePlanCTA.title.UNSUPPORTED": "Feature not supported for your account", "UpgradePlanCTA.upgradeButton": "Upgrade your plan", - "UploadDocumentation": "Вивантажити документацію", - "UploadDocumentationDescription": "Якщо ви хочете включити будь-яку документацію, ви можете завантажити її звідси.", "uploadImage.isDragActive": "Киньте його наче гаряче 🔥", "uploadImage.isDragReject": "🚫 Неприйнятний тип файлу", "uploadImage.isUploading": "Вивантаження зображення...", "uploadImage.sizeRejected": "Image resolution needs to be between {minResolution} and {maxResolution}. File size must be below {maxFileSize}.", - "UploadInvoice": "Вивантажити рахунок", - "UploadInvoiceDescription": "Якщо у вас уже є рахунок-фактура, його можна завантажити тут.", "uQguR/": "Усі колективи", "Uqkhct": "No virtual cards", "uR+TjD": "Are you sure you want to delete this payout method?", @@ -4556,8 +4517,6 @@ "VendorsAndOrganizations.Description": "Manage all the external organizations you work with as vendors and quickly surface all the activity between your hosted Collectives and other platform Organizations.", "vEQcqS": "Двоетапну перевірку додано", "verificationCriteria": "критерії перевірки", - "VerifyEmailAddress": "Підтвердьте адресу електронної пошти", - "VerifyEmailInstructions": "Електронний лист для перевірки надіслано на {email}. Натисніть на посилання, щоб підтвердити цю витрату. Якщо ви не отримали електронного листа, будь ласка, перевірте ваш спам.", "VErmYl": "Duplicate grant", "Vf1x2A": "Joined the platform on", "VfJsl4": "Beneficiary", @@ -4939,7 +4898,6 @@ "zNBAqh": "This value is an estimate. Please set the exact amount received if known.", "ZNBeE4": "Exactly {amount}", "ZNzyMo": "Export taxes and payment processor fees as columns", - "zO+Zv3": "Additional attachments", "zoC8Gb": "Одержувач сплачує комісію", "zOk9pq": "Go to activity log", "ZonfjV": "Ви не можете редагувати цей колектив", diff --git a/lang/zh.json b/lang/zh.json index df866b3d0fa..eb05afb9e7a 100644 --- a/lang/zh.json +++ b/lang/zh.json @@ -30,7 +30,6 @@ "+RvjCt": "成功删除卡片", "+S3jp9": "新版更新", "+SSp9V": "We encountered an issue loading this {type,select,section{section}other{page}}. Please reload the page or contact support if the problem persists.", - "+t6c4i": "账户类别说明", "+U6ozc": "类型", "+UdXIM": "通过 GitHub 创建的 ", "+UwJxq": "预计托管方费用", @@ -148,13 +147,11 @@ "2u8jmQ": "对付款启动双因素认证", "2Un6+c": "KYC Information", "2uzHxT": "导入 CSV", - "3135/i": "支出货币种类", "322m9e": "消息长度至少需要 10 个字符", "32rBNK": "Terms of Service", "34Up+l": "查看更多", "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "37zpJw": "Search countries...", - "38dzz9": "支出类别", "38fPNE": "交易导入设置", "3A7J9A": "我们无法找到符合你所有标准的发起者。", "3ABdi3": "此帐户不存在", @@ -577,7 +574,6 @@ "AzGwgz": "转账到 {selectedCurrency} 的最低金额是 {minAmountForSelectedCurrency}", "AZmsFu": " rejected contribution from ", "AzRKUx": "Create Beneficiary", - "b++lom": "是的,取消编辑", "B+fVMt": "More than {amount}", "B+NQ+r": " 起草了 {expenseDescription}", "b07w+D": "等级", @@ -1174,7 +1170,6 @@ "CreatedAt": "Created At", "CreatedBy": "由 {name}", "CreateEvent": "创建活动", - "CreateExpense.HelpCreateInfo": "Request payment from {collective}. Expenses will be processed once approved by a Collective admin. The amount, description, and your profile name are public, but attachments, payment details, and other personal info is kept private.", "CreateExpenseFAQ.getPaid": "我该如何从集体中获得报酬?", "CreateExpenseFAQ.getPaidDetails": "提交支出并提供付款信息。", "CreateExpenseFAQ.howAreApproved": "支出是如何获批的?", @@ -1297,7 +1292,6 @@ "DhxIpu": "New comment on ", "DinF1w": "{collective} 的净金额", "dIoEln": "Total disbursed", - "DismissableHelp.DontShowAgain": "好的,不再为我展示", "DisplayName": "Display Name", "DJnUUS": "This expense has not been approved", "dK5ItS": "By default only fiscal host administrators can submit expenses on behalf of vendors. You can allow other users who submit expenses to collectives you host to also submit expenses on behalf vendors.", @@ -1583,7 +1577,6 @@ "Expense.Direction.Received": "已接受", "Expense.Direction.Submitted": "已提交", "expense.draft": "草稿", - "Expense.edit": "编辑支出", "expense.editDetails": "Edit expense details", "expense.editNotes": "Edit notes", "expense.editPaidBy": "Edit paid by", @@ -1591,15 +1584,12 @@ "expense.editPayee": "Edit payee", "expense.editTitle": "Edit expense title", "expense.editType": "Edit expense type", - "Expense.EnterExpenseTitle": "支出标题:(公开)", - "Expense.EnterRequestSubject": "输入资助主题(公开)", "Expense.GoToPage": "前往支出页", "expense.hostFeeInCollectiveCurrency": "托管费用", "expense.incurredAt": "日期", "expense.invite.declined": "Invite declined", "Expense.InviteIsOnItsWay.Description": "已将提交此支出的邀请发送至 {email}。一旦他们确认并完成此进程,就会出现在支出列表。", "Expense.InvoiceItems": "发票项目", - "Expense.JoinAndSubmit": "加入并提交", "expense.linked": "Expense linked", "expense.markAsPaid": "标记为已支付", "Expense.MarkAsSpamLabel": "Why are you marking this expense as spam?", @@ -1628,7 +1618,6 @@ "Expense.PayTo": "付给", "expense.pending": "等待中", "expense.pickPayoutMethod": "Pick new payout method", - "Expense.PrivacyWarning": "此信息是公开的。请不要在此字段输入任何私密细节。", "Expense.PrivateNote": "私密笔记", "Expense.Receipt": "Receipt", "expense.ReceiptRequired": "需要收据", @@ -1646,21 +1635,15 @@ "Expense.RequestDetails": "请求细节", "Expense.RequestedBy": "由 {name} 邀请", "expense.requestReApproval.btn": "请求重新批准", - "Expense.SaveExpense": "保存支出", - "Expense.SaveReceipt": "保存发票", "expense.schedule.btn": "使用 {paymentMethod} 定期付款。", "expense.scheduledForPayment": "计划付款", "Expense.SeeDetails": "查看支出详情", "Expense.SendInvite": "发送邀请", - "Expense.SignUpInfoBox": "You need to create an account to receive a payment from {collectiveName}, by clicking 'Join and Submit' you agree to create an account on {WebsiteName}.", "expense.spam.notAllowed": "You can't mark your own expenses as spam", "expense.status": "状态", "Expense.Status.Refunded": "已退款", "Expense.Submitted": "支出已提交", "Expense.SubmittedBy": "由 {name} 提交", - "Expense.SuccessPage": "You can edit or review updates on this page.", - "Expense.Summary.Recurring.CheckboxDescription": "Choose this option to automatically submit a copy of this invoice on a periodic basis.", - "Expense.Summary.Recurring.CheckboxTitle": "这是定期支出吗?", "Expense.type": "Type", "expense.type": "类型", "Expense.Type.Charge": "Virtual Card Charge", @@ -1677,16 +1660,12 @@ "ExpenseAmount": "支出金额", "ExpenseApprover": "Expense Approver", "ExpenseForm.AddAttachedFile": "添加新文档", - "ExpenseForm.AddGrantItem": "添加赠品项", - "ExpenseForm.AddLineItem": "添加新项目", - "ExpenseForm.AddReceipt": "添加新的收据", "ExpenseForm.AddressLabel": "住址", "ExpenseForm.CancelEditExpense": "取消编辑", "ExpenseForm.ChooseCountry": "选择国家/地区", "ExpenseForm.ClearExpenseForm": "清除表单", "ExpenseForm.ConfirmCancelEditExpense": "你确定想要取消编辑吗?", "ExpenseForm.ConfirmClearExpenseForm": "你确定想要清除支出表单吗?", - "ExpenseForm.DescriptionPlaceholder": "在此输入支出标题...", "ExpenseForm.FundingRequestDescription": "为项目或倡议申请资助。", "ExpenseForm.GrantSubjectPlaceholder": "e.g., Research, software development, etc.", "ExpenseForm.inviteAdditionalInfo": "想要输入付款详情,像 PayPal 地址或银行账户?", @@ -1707,12 +1686,7 @@ "ExpenseForm.SavePayout": "保存此信息用于未来的付款", "ExpenseForm.SignUp.OrgAdminNote": "你需要成为本组织的管理员才能提交支出。", "ExpenseForm.SignUp.SignIn": "我们将使用此邮箱创建你的账号。如果你已经有账号了 {loginLink}。", - "ExpenseForm.StepExpense": "上传一个或多个收据…", - "ExpenseForm.StepExpenseFundingRequest": "设置授予详情", - "ExpenseForm.StepExpenseInvoice": "添加账单信息", - "ExpenseForm.StepPayeeInvoice": "收款人信息", "ExpenseForm.Submit": "提交支出", - "ExpenseForm.SubmitRequest": "提交请求", "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.", @@ -1747,7 +1721,6 @@ "ExpensesSubmittedByName": "Expenses submitted by {name}", "ExpenseSubmitters": "Expense Submitters", "ExpenseSummary.addNotesLabel": "添加笔记", - "ExpenseSummaryTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} Summary to {collectiveName}", "ExpenseTitle": "{type, select, CHARGE {Charge} INVOICE {Invoice} RECEIPT {Receipt} GRANT {Grant} SETTLEMENT {Settlement} PLATFORM_BILLING {Platform bill} other {Expense}} {id} to {collectiveName}", "Explore": "探索", "Export.Format": "导出 {format}", @@ -1967,7 +1940,6 @@ "fiscalHosting.youngActivists.description": "可能缺乏自己管理法律实体经验的人。", "FiscalHostPolicies": "Fiscal Host Policies", "fiscaltos": "财务捐赠条款", - "fIsGOi": "否,继续编辑", "FJBnaq": "客户端 ID 和客户端密钥", "FKMNjH": "You won't be able to resume the synchronization in the future.", "FLqc8O": "已添加虚拟卡", @@ -3347,7 +3319,6 @@ "PJubm9": "Financial tracking and transparency means reporting writes itself", "pKF7TO": "Require the contributor to provide their physical address when they contribute more than:", "Pkorog": "Account under verification", - "Pkq+ZR": "Please make sure that all the expense items in this expense belong to the selected expense category. If needed, you may submit additional items in separate expenses with different expense categories.", "Pkx+Wj": "分享结果", "platform": "平台", "platform.explainerVideo": "解说视频 ", @@ -3381,8 +3352,6 @@ "PreviewFeatures.CollectiveOverview.Welcome.Description": "We’ve created this space for you to keep on top of everything happening in your Collective, please let us know how we can make it better!", "PreviewFeatures.CollectiveOverview.Welcome.Title": "Welcome to your new Collective Overview", "PreviewFeatures.crowdfundingRedesignDescription": "Be part of the crowdfunding redesign effort and get access to previews of new crowdfunding and profile pages. Experience enhanced profile pages with separate fundraising and storytelling views, clearer relationships between collectives and their projects, improved goal tracking, and better collective narratives that showcase your impact and long-term sustainability.{newLine}{newLine}Check out the blog post for more details.", - "PreviewFeatures.inlineEditExpenseDescription": "Edit expense details directly in the Dashboard without navigating to separate pages.", - "PreviewFeatures.inlineEditExpenseTitle": "Inline Expense Editing", "PreviewFeatures.keyboardShortcutsDescription": "Navigate the expense flow more efficiently with keyboard shortcuts. Speed up your workflow and reduce mouse dependency for common actions.", "PreviewFeatures.keyboardShortcutsExpenseDetailsAttachments": "{leftKey} and {rightKey} to navigate through attachments.", "PreviewFeatures.keyboardShortcutsExpenseDetailsClose": "{key} to close Expense details.", @@ -3396,8 +3365,6 @@ "PreviewFeatures.keyboardShortcutsExpenseListSecurity": "{key} to check Security alerts for selected Expense.", "PreviewFeatures.keyboardShortcutsTitle": "Keyboard Shortcuts", "PreviewFeatures.LimitedAccess": "Limited preview", - "PreviewFeatures.newExpenseFlowDescription": "Experience an improved expense submission flow in the Dashboard with better user experience, clearer navigation, and enhanced form validation.", - "PreviewFeatures.newExpenseFlowTitle": "New Expense Submission Flow", "PreviewFeatures.publicBeta": "公测", "PreviewFeatures.searchCommandDescription": "Discover a new way to search for collectives, transactions, expenses, and more through an intuitive command menu interface. Access information faster with powerful search capabilities.", "PreviewFeatures.searchCommandTitle": "Search Command Menu", @@ -3455,7 +3422,6 @@ "pricing.start.feeNote": "你无法收取集体费用或设置任何托管费用", "pricing.title": "我们的价格结构", "pricing.unlimitedCollectives": "无限的集体", - "privacypolicy": "隐私政策", "Private": "Private", "PrivateCommentsMessage.Allowed": "你的评论是私密的。", "PrivateCommentsMessage.AllowedDetails": "Expenses comments are private, because they sometimes contain confidential information such as payment details. Only the expense submitter, the admins and the accountants can see them.", @@ -4247,7 +4213,6 @@ "TopContributors.Individuals": "个人", "TopContributors.Organizations": "组织", "tos": "服务条款", - "TOSAndPrivacyPolicyAgreement": "我同意 Open Collective 的 {toslink} 和 {privacylink}。", "total": "总计", "TotalAmount": "总金额", "TotalAmountWithoutFee": "总金额 (不包括服务费)", @@ -4487,14 +4452,10 @@ "UpgradePlanCTA.title.DISABLED": "Upgrade Required", "UpgradePlanCTA.title.UNSUPPORTED": "Feature not supported for your account", "UpgradePlanCTA.upgradeButton": "Upgrade your plan", - "UploadDocumentation": "上传文档", - "UploadDocumentationDescription": "如果你有想要包括的文档,可以在这里上传它。", "uploadImage.isDragActive": "拖放它就像它很热 🔥", "uploadImage.isDragReject": "🚫 这个文件类型是不被接受的", "uploadImage.isUploading": "正在上传图片…", "uploadImage.sizeRejected": "图像分辨率需要在 {minResolution} 和 {maxResolution} 之间。文件大小需要低于 {maxFileSize}。", - "UploadInvoice": "上传发票", - "UploadInvoiceDescription": "如果你已经有发票文件了,可以在这里上传。", "uQguR/": "所有集体", "Uqkhct": "无虚拟卡", "uR+TjD": "Are you sure you want to delete this payout method?", @@ -4556,8 +4517,6 @@ "VendorsAndOrganizations.Description": "Manage all the external organizations you work with as vendors and quickly surface all the activity between your hosted Collectives and other platform Organizations.", "vEQcqS": "已添加双因素认证", "verificationCriteria": "核查标准", - "VerifyEmailAddress": "验证你的电子邮件地址", - "VerifyEmailInstructions": "验证邮件已发送至 {email}。点击链接完成提交这笔费用。 如果您没有收到电子邮件,请检查你的垃圾邮件。", "VErmYl": "Duplicate grant", "Vf1x2A": "Joined the platform on", "VfJsl4": "Beneficiary", @@ -4939,7 +4898,6 @@ "zNBAqh": "This value is an estimate. Please set the exact amount received if known.", "ZNBeE4": "实际为 {amount}", "ZNzyMo": "Export taxes and payment processor fees as columns", - "zO+Zv3": "Additional attachments", "zoC8Gb": "收款人支付费用", "zOk9pq": "Go to activity log", "ZonfjV": "你不能编辑此集体", diff --git a/lib/collective.js b/lib/collective.js index a170a0e8ab7..623d0651312 100644 --- a/lib/collective.js +++ b/lib/collective.js @@ -197,37 +197,6 @@ export const formatAccountName = (displayName, legalName) => { } }; -/* - * Validate the account holder name against the legal name. Following cases are considered a match, - * - * 1) Punctuation are ignored; "Evil Corp, Inc" and "Evil Corp, Inc." are considered a match. - * 2) Accents are ignored; "François" and "Francois" are considered a match. - * 3) The first name and last name order is ignored; "Benjamin Piouffle" and "Piouffle Benjamin" is considered a match. - */ -export const compareNames = (accountHolderName, legalName) => { - // Ignore 501(c)(3) in both account holder name and legal name - legalName = legalName?.replaceAll('501(c)(3)', '') || ''; - accountHolderName = accountHolderName?.replaceAll('501(c)(3)', '') || ''; - - const namesArray = legalName.trim().split(' '); - let legalNameReversed; - if (namesArray.length === 2) { - const firstName = namesArray[0]; - const lastName = namesArray[1]; - legalNameReversed = `${lastName} ${firstName}`; - } - return !( - accountHolderName.localeCompare(legalName, undefined, { - sensitivity: 'base', - ignorePunctuation: true, - }) && - accountHolderName.localeCompare(legalNameReversed, undefined, { - sensitivity: 'base', - ignorePunctuation: true, - }) - ); -}; - /** * Splits a name in 3 parts: firstName, middle name and lastName. * @warning By nature, this function cannot be 100% accurate and shouldn't be used for automated processes. diff --git a/lib/expenses/index.js b/lib/expenses/index.js index 041e8d93dc8..7aa108deeb4 100644 --- a/lib/expenses/index.js +++ b/lib/expenses/index.js @@ -34,33 +34,6 @@ export const getPayoutProfiles = memoizeOne(loggedInAccount => { export const DEFAULT_SUPPORTED_EXPENSE_TYPES = { GRANT: false, INVOICE: true, RECEIPT: true }; -export const getSupportedExpenseTypes = account => { - if (!account) { - return []; - } - - const host = account.host; - const parent = account.parent || account.parentCollective; - if (account.supportedExpenseTypes || parent?.supportedExpenseTypes) { - // Easy case: the account uses the new supportedExpenseTypes field - return account.supportedExpenseTypes || parent.supportedExpenseTypes; - } else { - // Aggregate all configs, using the order of priority collective > parent > host - const getExpenseTypes = account => omitBy(account?.settings?.expenseTypes, isNull); - const defaultExpenseTypes = DEFAULT_SUPPORTED_EXPENSE_TYPES; - const aggregatedConfig = merge(defaultExpenseTypes, ...[host, parent, account].map(getExpenseTypes)); - return Object.keys(aggregatedConfig).filter(key => aggregatedConfig[key]); // Return only the truthy ones - } -}; - -/** - * Helper to determine whether an expense type is supported by an account - */ -export const isSupportedExpenseType = (account, expenseType) => { - const supportedTypes = getSupportedExpenseTypes(account); - return supportedTypes.includes(expenseType); -}; - /** * Helper to format and combine expense items (with URLs) and attached files into a unified format */ diff --git a/lib/graphql/v1/queries.js b/lib/graphql/v1/queries.js index 87817a2945b..be0502a6cc9 100644 --- a/lib/graphql/v1/queries.js +++ b/lib/graphql/v1/queries.js @@ -311,71 +311,6 @@ export const collectiveSettingsQuery = gqlV1 /* GraphQL */ ` } `; -export const expenseFormPayeeStepCollectivePickerSearchQuery = gqlV1 /* GraphQL */ ` - query ExpenseFormPayeeStepCollectivePickerSearch( - $term: String! - $types: [TypeOfCollective] - $limit: Int - $hostCollectiveIds: [Int] - $parentCollectiveIds: [Int] - $skipGuests: Boolean - $includeArchived: Boolean - $includeVendorsForHostId: Int - $vendorVisibleToAccountIds: [Int] - ) { - search( - term: $term - types: $types - limit: $limit - hostCollectiveIds: $hostCollectiveIds - parentCollectiveIds: $parentCollectiveIds - skipGuests: $skipGuests - includeArchived: $includeArchived - includeVendorsForHostId: $includeVendorsForHostId - vendorVisibleToAccountIds: $vendorVisibleToAccountIds - ) { - id - collectives { - id - type - slug - name - currency - location { - id - address - country - } - imageUrl(height: 64) - hostFeePercent - isActive - isArchived - isHost - host { - id - slug - } - payoutMethods { - legacyId: id - type - name - data - isSaved - } - ... on User { - isTwoFactorAuthEnabled - } - ... on Organization { - isTrustedHost - } - ... on Vendor { - hasPayoutMethod - } - } - } - } -`; - export const unhostAccountCollectivePickerSearchQuery = gqlV1 /* GraphQL */ ` query UnhostAccountCollectivePickerSearch( $term: String! diff --git a/lib/hooks/useKeyboardKey.ts b/lib/hooks/useKeyboardKey.ts index e39ed46bb05..16a456a8751 100644 --- a/lib/hooks/useKeyboardKey.ts +++ b/lib/hooks/useKeyboardKey.ts @@ -141,11 +141,6 @@ export const S = { keyName: 'S', }; -export const E = { - key: 'e', - keyName: 'E', -}; - export const H = { key: 'h', keyName: 'H', diff --git a/lib/i18n/expense.js b/lib/i18n/expense.js index 61c44258596..87887390b2f 100644 --- a/lib/i18n/expense.js +++ b/lib/i18n/expense.js @@ -146,8 +146,3 @@ export const RecurringExpenseIntervals = { quarter: , year: , }; - -export const RecurringIntervalOptions = Object.keys(RecurringExpenseIntervals).reduce( - (values, key) => [...values, { value: key, label: RecurringExpenseIntervals[key] }], - [], -); diff --git a/lib/preview-features.tsx b/lib/preview-features.tsx index 83519b18319..5144074e1ae 100644 --- a/lib/preview-features.tsx +++ b/lib/preview-features.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import { get } from 'lodash'; import { FormattedMessage } from 'react-intl'; import I18nFormatters, { getI18nLink } from '@/components/I18nFormatters'; @@ -11,8 +10,6 @@ import type LoggedInUser from './LoggedInUser'; * A map of keys used for preview features. */ export enum PREVIEW_FEATURE_KEYS { - NEW_EXPENSE_FLOW = 'NEW_EXPENSE_FLOW', - INLINE_EDIT_EXPENSE = 'INLINE_EDIT_EXPENSE', CROWDFUNDING_REDESIGN = 'CROWDFUNDING_REDESIGN', AUTHENTICATED_SSR = 'AUTHENTICATED_SSR', VERCEL_BACKEND = 'VERCEL_BACKEND', @@ -49,7 +46,7 @@ export type PreviewFeature = { const PLATFORM_ACCOUNTS = ['ofico', 'ofitech']; const ENGINEERS = ['znarf', 'betree', 'leokewitz', 'henrique-silva', 'gustavlrsn', 'sudharaka-palamakumbura']; -export const OFICO_MEMBER_ORGANIZATIONS = [ +const OFICO_MEMBER_ORGANIZATIONS = [ 'europe', 'giftcollective', 'oce-foundation-eur', @@ -196,36 +193,6 @@ export const previewFeatures: PreviewFeature[] = [ publicBeta: true, category: Categories.GENERAL, }, - { - key: PREVIEW_FEATURE_KEYS.NEW_EXPENSE_FLOW, - title: , - description: ( - - ), - category: Categories.GENERAL, - publicBeta: false, - enabledByDefaultFor: ['*'], - // Hide if not root and not manually enabled - hide: (loggedInUser: LoggedInUser) => - !loggedInUser.isRoot && - !get(loggedInUser, `collective.settings.earlyAccess.${PREVIEW_FEATURE_KEYS.NEW_EXPENSE_FLOW}`), - }, - { - key: PREVIEW_FEATURE_KEYS.INLINE_EDIT_EXPENSE, - title: , - description: ( - - ), - category: Categories.GENERAL, - publicBeta: true, - enabledByDefaultFor: ['*'], - }, { key: PREVIEW_FEATURE_KEYS.SEARCH_COMMAND, title: , diff --git a/pages/create-expense.js b/pages/create-expense.js index c24324ee6c5..406d90ea71f 100644 --- a/pages/create-expense.js +++ b/pages/create-expense.js @@ -1,62 +1,29 @@ import React from 'react'; import PropTypes from 'prop-types'; import { graphql } from '@apollo/client/react/hoc'; -import { omit, pick } from 'lodash'; import { withRouter } from 'next/router'; -import { FormattedMessage, injectIntl } from 'react-intl'; +import { FormattedMessage } from 'react-intl'; -import { itemHasOCR } from '../components/expenses/lib/ocr'; import hasFeature, { FEATURES } from '../lib/allowed-features'; -import { - expenseSubmissionAllowed, - getCollectivePageMetadata, - getCollectiveTypeForUrl, - isHiddenAccount, -} from '../lib/collective'; -import expenseTypes from '../lib/constants/expenseTypes'; -import { generateNotFoundError, i18nGraphqlException } from '../lib/errors'; -import { getPayoutProfiles } from '../lib/expenses'; -import FormPersister from '../lib/form-persister'; +import { expenseSubmissionAllowed, getCollectivePageMetadata, isHiddenAccount } from '../lib/collective'; +import { generateNotFoundError } from '../lib/errors'; import { gql } from '../lib/graphql/helpers'; -import { PREVIEW_FEATURE_KEYS } from '../lib/preview-features'; import { addParentToURLIfMissing, getCollectivePageCanonicalURL, getCollectivePageRoute } from '../lib/url-helpers'; import UrlQueryHelper from '../lib/UrlQueryHelper'; -import { compose, parseToBoolean } from '../lib/utils'; +import { compose } from '../lib/utils'; import CollectiveNavbar from '../components/collective-navbar'; -import { Dimensions } from '../components/collective-page/_constants'; import { collectiveNavbarFieldsFragment } from '../components/collective-page/graphql/fragments'; import Container from '../components/Container'; import ContainerOverlay from '../components/ContainerOverlay'; import ErrorPage from '../components/ErrorPage'; -import { ConfirmOCRValues } from '../components/expenses/ConfirmOCRValues'; -import CreateExpenseDismissibleIntro from '../components/expenses/CreateExpenseDismissibleIntro'; -import ExpenseForm, { EXPENSE_FORM_STEPS, prepareExpenseForSubmit } from '../components/expenses/ExpenseForm'; -import ExpenseInfoSidebar from '../components/expenses/ExpenseInfoSidebar'; -import ExpenseNotesForm from '../components/expenses/ExpenseNotesForm'; -import ExpenseRecurringForm from '../components/expenses/ExpenseRecurringForm'; -import ExpenseSummary, { SummaryHeader } from '../components/expenses/ExpenseSummary'; -import { - expensePageExpenseFieldsFragment, - loggedInAccountExpensePayoutFieldsFragment, -} from '../components/expenses/graphql/fragments'; -import MobileCollectiveInfoStickyBar from '../components/expenses/MobileCollectiveInfoStickyBar'; -import { Box, Flex } from '../components/Grid'; -import LinkCollective from '../components/LinkCollective'; -import LoadingPlaceholder from '../components/LoadingPlaceholder'; +import { Flex } from '../components/Grid'; import MessageBox from '../components/MessageBox'; import Page from '../components/Page'; import PageFeatureNotSupported from '../components/PageFeatureNotSupported'; import SignInOrJoinFree, { SignInOverlayBackground } from '../components/SignInOrJoinFree'; -import StyledButton from '../components/StyledButton'; -import StyledCard from '../components/StyledCard'; import { SubmitExpenseFlow } from '../components/submit-expense/SubmitExpenseFlow'; -import { Survey, SURVEY_KEY } from '../components/Survey'; -import { toast } from '../components/ui/useToast'; import { withUser } from '../components/UserProvider'; -import { AccountingCategorySelectFieldsFragment } from '@/components/AccountingCategorySelect'; - -const STEPS = { ...EXPENSE_FORM_STEPS, SUMMARY: 'summary' }; const CreateExpensePageUrlQueryHelper = new UrlQueryHelper({ collectiveSlug: { type: 'string' }, @@ -82,107 +49,29 @@ class CreateExpensePage extends React.Component { loadingLoggedInUser: PropTypes.bool, /** from withRouter */ router: PropTypes.object, - /** from injectIntl */ - intl: PropTypes.object, - /** from apollo */ - createExpense: PropTypes.func.isRequired, - /** from apollo */ - draftExpenseAndInviteUser: PropTypes.func.isRequired, /** from apollo */ data: PropTypes.shape({ loading: PropTypes.bool, error: PropTypes.any, refetch: PropTypes.func, - account: PropTypes.shape({ - id: PropTypes.string.isRequired, - parent: PropTypes.object, - name: PropTypes.string.isRequired, - slug: PropTypes.string.isRequired, - description: PropTypes.string, - type: PropTypes.string.isRequired, - twitterHandle: PropTypes.string, - imageUrl: PropTypes.string, - isArchived: PropTypes.bool, - supportedExpenseTypes: PropTypes.array, - expensesTags: PropTypes.arrayOf( - PropTypes.shape({ - id: PropTypes.string.isRequired, - tag: PropTypes.string.isRequired, - }), - ), - host: PropTypes.shape({ - id: PropTypes.string.isRequired, - }), - }), - loggedInAccount: PropTypes.shape({ - adminMemberships: PropTypes.shape({ - nodes: PropTypes.arrayOf( - PropTypes.shape({ - id: PropTypes.string.isRequired, - account: PropTypes.shape({ - id: PropTypes.string.isRequired, - slug: PropTypes.string.isRequired, - name: PropTypes.string, - imageUrl: PropTypes.string, - }), - }), - ), - }), - }), - }).isRequired, // from withData + account: PropTypes.object, + }).isRequired, }; - constructor(props) { - super(props); - this.formTopRef = React.createRef(); - this.state = { - step: STEPS.PAYEE, - expense: null, - isSubmitting: false, - formPersister: null, - isInitialForm: true, - recurring: null, - hasConfirmedOCR: false, - }; - } - - async componentDidMount() { - // Reset form when `resetForm` is passed in the URL - if (this.handleResetForm()) { - return; - } - - // Re-fetch data if user is logged in - if (this.props.LoggedInUser) { - this.props.data.refetch(); - this.initFormPersister(); - } - + componentDidMount() { const { router, data } = this.props; const account = data?.account; addParentToURLIfMissing(router, account, '/expenses/new'); - } - async componentDidUpdate(oldProps, oldState) { - // Reset form when `resetForm` is passed in the URL - if (this.handleResetForm()) { - return; + if (this.props.LoggedInUser) { + this.props.data.refetch(); } + } - // Re-fetch data if user is logged in + componentDidUpdate(oldProps) { if (!oldProps.LoggedInUser && this.props.LoggedInUser) { this.props.data.refetch(); } - - // Reset form persister when data loads or when account changes - if (!this.state.formPersister || oldProps.data?.account?.id !== this.props.data?.account?.id) { - this.initFormPersister(); - } - - // Scroll to top when switching steps - if (oldState.step !== this.state.step && this.formTopRef.current) { - this.formTopRef.current.scrollIntoView({ behavior: 'smooth' }); - } } getPageMetaData(collective) { @@ -195,127 +84,8 @@ class CreateExpensePage extends React.Component { } } - buildFormPersister() { - const { LoggedInUser, data } = this.props; - if (data.account && LoggedInUser) { - return new FormPersister(`expense-${data.account.id}=${LoggedInUser.id}`); - } - } - - handleResetForm() { - const { router } = this.props; - if (parseToBoolean(router.query.resetForm)) { - const formPersister = this.buildFormPersister(); - if (formPersister) { - formPersister.clearValues(); - const query = omit(router.query, ['resetForm']); - const routeAs = router.asPath.split('?')[0]; - return router.push({ pathname: '/create-expense', query }, routeAs, { shallow: true }); - } - } - } - - initFormPersister() { - const formPersister = this.buildFormPersister(); - if (formPersister) { - this.setState({ formPersister }); - } - } - - onFormSubmit = async expense => { - try { - if (expense.payee.isInvite) { - const result = await this.props.draftExpenseAndInviteUser({ - variables: { - account: { id: this.props.data.account.id }, - expense: { - ...prepareExpenseForSubmit(expense), - customData: this.props.customData, - recipientNote: expense.recipientNote?.trim(), - }, - }, - }); - if (this.state.formPersister) { - this.state.formPersister.clearValues(); - } - - // Redirect to the expense page - const legacyExpenseId = result.data.draftExpenseAndInviteUser.legacyId; - const { collectiveSlug, parentCollectiveSlug, data } = this.props; - const parentCollectiveSlugRoute = parentCollectiveSlug ? `${parentCollectiveSlug}/` : ''; - const collectiveType = parentCollectiveSlug ? getCollectiveTypeForUrl(data?.account) : undefined; - const collectiveTypeRoute = collectiveType ? `${collectiveType}/` : ''; - await this.props.router.push({ - pathname: `${parentCollectiveSlugRoute}${collectiveTypeRoute}${collectiveSlug}/expenses/${legacyExpenseId}`, - query: pick(this.props.router.query, ['forceLegacyFlow']), - }); - } else { - this.setState({ expense, step: STEPS.SUMMARY, isInitialForm: false }); - } - } catch (e) { - toast({ - variant: 'error', - message: i18nGraphqlException(this.props.intl, e), - }); - } - }; - - onSummarySubmit = async () => { - try { - this.setState({ isSubmitting: true, error: null }); - const { expense, recurring } = this.state; - const result = await this.props.createExpense({ - variables: { - account: { id: this.props.data.account.id }, - expense: { ...prepareExpenseForSubmit(expense), customData: this.props.customData }, - recurring, - }, - }); - - // Clear local storage backup if expense submitted successfully - if (this.state.formPersister) { - this.state.formPersister.clearValues(); - } - - // Redirect to the expense page - const legacyExpenseId = result.data.createExpense.legacyId; - const { collectiveSlug, parentCollectiveSlug, data } = this.props; - const parentCollectiveSlugRoute = parentCollectiveSlug ? `${parentCollectiveSlug}/` : ''; - const collectiveType = parentCollectiveSlug ? getCollectiveTypeForUrl(data?.account) : undefined; - const collectiveTypeRoute = collectiveType ? `${collectiveType}/` : ''; - await this.props.router.push({ - pathname: `${parentCollectiveSlugRoute}${collectiveTypeRoute}${collectiveSlug}/expenses/${legacyExpenseId}`, - query: pick(this.props.router.query, ['ocr', 'mockImageUpload', 'forceLegacyFlow']), - }); - toast({ - variant: 'success', - title: , - message: this.props.LoggedInUser ? ( - - ) : ( - - ), - duration: 20000, - }); - window.scrollTo(0, 0); - } catch (e) { - toast({ - variant: 'error', - message: i18nGraphqlException(this.props.intl, e), - }); - this.setState({ isSubmitting: false }); - } - }; - - onNotesChanges = e => { - const name = e.target.name; - const value = e.target.value; - this.setState(state => ({ expense: { ...state.expense, [name]: value } })); - }; - render() { const { collectiveSlug, data, LoggedInUser, loadingLoggedInUser, router } = this.props; - const { step } = this.state; if (!data.loading) { if (data.error) { @@ -333,11 +103,6 @@ class CreateExpensePage extends React.Component { } const collective = data.account; - const host = collective && collective.host; - const loggedInAccount = data.loggedInAccount; - const payoutProfiles = getPayoutProfiles(loggedInAccount); - const hasItemsWithOCR = Boolean(this.state.expense?.items?.some(itemHasOCR)); - const mustConfirmOCR = hasItemsWithOCR && !this.state.hasConfirmedOCR; return ( @@ -351,10 +116,9 @@ class CreateExpensePage extends React.Component {
) : !loadingLoggedInUser && !LoggedInUser ? ( - // Not logged in - show sign-in - + - ) : LoggedInUser?.hasPreviewFeatureEnabled(PREVIEW_FEATURE_KEYS.NEW_EXPENSE_FLOW) && - !parseToBoolean(router.query.forceLegacyFlow) ? ( - // Logged in with new expense flow enabled + ) : ( - ) : ( - // Logged in with legacy flow (preview feature disabled) - - - - - - - - {step !== STEPS.SUMMARY ? ( - - ) : ( - {text}, - }} - /> - )} - - {data.loading || loadingLoggedInUser ? ( - - ) : ( - - - {step !== STEPS.SUMMARY ? ( - - ) : ( -
- - - this.setState({ recurring })} - /> - - - -
- {hasItemsWithOCR && ( - this.setState({ hasConfirmedOCR })} - currency={this.state.expense.currency} - /> - )} -
- - this.setState({ step: STEPS.EXPENSE })} - disabled={this.state.isSubmitting} - > - ← - - - {this.state.expense.type === expenseTypes.GRANT ? ( - - ) : ( - - )} - - -
-
- )} -
- )} -
- - - -
-
- -
-
)} ); } } -const hostFieldsFragment = gql` - fragment CreateExpenseHostFields on Host { - id - name - legalName - legacyId - slug - type - expensePolicy - settings - currency - features { - id - MULTI_CURRENCY_EXPENSES - } - location { - id - address - country - } - transferwise { - id - availableCurrencies - } - accountingCategories { - nodes { - id - ...AccountingCategorySelectFields - } - } - policies { - id - EXPENSE_CATEGORIZATION { - requiredForExpenseSubmitters - requiredForCollectiveAdmins - } - } - supportedPayoutMethods - isTrustedHost - } - ${AccountingCategorySelectFieldsFragment} -`; - const createExpensePageQuery = gql` query CreateExpensePage($collectiveSlug: String!) { account(slug: $collectiveSlug, throwIfMissing: false) { @@ -600,19 +197,14 @@ const createExpensePageQuery = gql` isApproved host { id - ...CreateExpenseHostFields } } - # For Hosts with Budget capabilities - ... on Organization { isHost isActive - # NOTE: This will be the account itself in this case host { id - ...CreateExpenseHostFields } } @@ -627,14 +219,8 @@ const createExpensePageQuery = gql` } } } - loggedInAccount { - id - ...LoggedInAccountExpensePayoutFields - } } - ${loggedInAccountExpensePayoutFieldsFragment} - ${hostFieldsFragment} ${collectiveNavbarFieldsFragment} `; @@ -644,46 +230,7 @@ const addCreateExpensePageData = graphql(createExpensePageQuery, { }, }); -const createExpenseMutation = gql` - mutation CreateExpense( - $expense: ExpenseCreateInput! - $account: AccountReferenceInput! - $recurring: RecurringExpenseInput - ) { - createExpense(expense: $expense, account: $account, recurring: $recurring) { - id - ...ExpensePageExpenseFields - } - } - ${expensePageExpenseFieldsFragment} -`; - -const addCreateExpenseMutation = graphql(createExpenseMutation, { - name: 'createExpense', -}); - -const draftExpenseAndInviteUserMutation = gql` - mutation DraftExpenseAndInviteUser($expense: ExpenseInviteDraftInput!, $account: AccountReferenceInput!) { - draftExpenseAndInviteUser(expense: $expense, account: $account) { - id - ...ExpensePageExpenseFields - } - } - ${expensePageExpenseFieldsFragment} -`; - -const addDraftExpenseAndInviteUserMutation = graphql(draftExpenseAndInviteUserMutation, { - name: 'draftExpenseAndInviteUser', -}); - -const addHoc = compose( - withUser, - withRouter, - addCreateExpensePageData, - addCreateExpenseMutation, - addDraftExpenseAndInviteUserMutation, - injectIntl, -); +const addHoc = compose(withUser, withRouter, addCreateExpensePageData); // next.js export // ts-unused-exports:disable-next-line diff --git a/pages/create-grant.tsx b/pages/create-grant.tsx index d023feee626..3d19476d32d 100644 --- a/pages/create-grant.tsx +++ b/pages/create-grant.tsx @@ -10,9 +10,7 @@ import { generateNotFoundError } from '@/lib/errors'; import type { CreateGrantPageQuery } from '@/lib/graphql/types/v2/graphql'; import { ExpenseType } from '@/lib/graphql/types/v2/graphql'; import useLoggedInUser from '@/lib/hooks/useLoggedInUser'; -import { PREVIEW_FEATURE_KEYS } from '@/lib/preview-features'; import { getCollectivePageCanonicalURL, getCollectivePageRoute } from '@/lib/url-helpers'; -import { parseToBoolean } from '@/lib/utils'; import ErrorPage from '@/components/ErrorPage'; import Loading from '@/components/Loading'; @@ -30,7 +28,7 @@ const CreateGrantPageI18n = defineMessages({ function CreateGrantPage(props: Awaited>) { const intl = useIntl(); const router = useRouter(); - const { LoggedInUser, loadingLoggedInUser } = useLoggedInUser(); + const { loadingLoggedInUser } = useLoggedInUser(); const pageMetadata = React.useMemo(() => { if (!props.account) { return null; @@ -53,10 +51,6 @@ function CreateGrantPage(props: Awaited; } else if (!props.account || isHiddenAccount(props.account)) { return ; - } else if (!isGrantPreviewEnabled || !(props.account.supportedExpenseTypes || []).includes(ExpenseType.GRANT)) { + } else if (!(props.account.supportedExpenseTypes || []).includes(ExpenseType.GRANT)) { return ; } @@ -145,7 +139,6 @@ CreateGrantPage.getInitialProps = async (ctx: NextPageContext) => { account, error, queryResult, - newFlowEnabledInUrl: parseToBoolean(ctx.query.newGrantFlowEnabled), }; }; diff --git a/pages/expense.tsx b/pages/expense.tsx index 4eb8e2a5c5d..8b5ec8963a8 100644 --- a/pages/expense.tsx +++ b/pages/expense.tsx @@ -14,7 +14,7 @@ import { addParentToURLIfMissing, getCollectivePageCanonicalURL } from '../lib/u import CollectiveNavbar from '../components/collective-navbar'; import { NAVBAR_CATEGORIES } from '../components/collective-navbar/constants'; import ErrorPage from '../components/ErrorPage'; -import Expense, { EXPENSE_PAGE_POLLING_INTERVAL } from '../components/expenses/Expense'; +import Expense from '../components/expenses/Expense'; import ExpenseInfoSidebar from '../components/expenses/ExpenseInfoSidebar'; import { expensePageQuery } from '../components/expenses/graphql/queries'; import MobileCollectiveInfoStickyBar from '../components/expenses/MobileCollectiveInfoStickyBar'; @@ -37,7 +37,6 @@ const getPropsFromQuery = query => { legacyExpenseId: parseInt(query.ExpenseId), draftKey: query.key || null, collectiveSlug: query.collectiveSlug, - edit: query.edit, }; }; @@ -99,21 +98,10 @@ export default function ExpensePage(props: InferGetServerSidePropsType { - if (isOpen) { - stopPolling?.(); - } else { - startPolling?.(EXPENSE_PAGE_POLLING_INTERVAL); - } - }, - [startPolling, stopPolling], - ); + const { refetch, fetchMore } = queryResult; if (!queryResult.loading) { if (!data || error) { @@ -135,12 +123,7 @@ export default function ExpensePage(props: InferGetServerSidePropsType - + @@ -152,10 +135,7 @@ export default function ExpensePage(props: InferGetServerSidePropsType diff --git a/public/static/images/invoice-animation-static.jpg b/public/static/images/invoice-animation-static.jpg deleted file mode 100644 index fc8289ca27a..00000000000 Binary files a/public/static/images/invoice-animation-static.jpg and /dev/null differ diff --git a/public/static/images/invoice-animation.gif b/public/static/images/invoice-animation.gif deleted file mode 100644 index 9ca48674ab9..00000000000 Binary files a/public/static/images/invoice-animation.gif and /dev/null differ diff --git a/public/static/images/receipt-animation-static.jpg b/public/static/images/receipt-animation-static.jpg deleted file mode 100644 index 0d14d732f4b..00000000000 Binary files a/public/static/images/receipt-animation-static.jpg and /dev/null differ diff --git a/public/static/images/receipt-animation.gif b/public/static/images/receipt-animation.gif deleted file mode 100644 index bdfc6657e22..00000000000 Binary files a/public/static/images/receipt-animation.gif and /dev/null differ diff --git a/test/cypress/integration/27-expenses.test.js b/test/cypress/integration/27-expenses.test.js index 02b39230a5c..e87f414b057 100644 --- a/test/cypress/integration/27-expenses.test.js +++ b/test/cypress/integration/27-expenses.test.js @@ -1,753 +1,13 @@ -import { randomEmail, randomSlug } from '../support/faker'; - -const getReceiptFixture = ({ fileName = 'receipt.jpg' } = {}) => ({ - contents: 'test/cypress/fixtures/images/small-15x15.jpg', - mimeType: 'image/jpeg', - fileName, -}); - describe('Expense flow', () => { describe('new expense when logged out', () => { it('shows the login screen', () => { cy.createHostedCollective().then(collective => { - cy.visit(`/${collective.slug}/expenses/new?forceLegacyFlow=true`); + cy.visit(`/${collective.slug}/expenses/new`); cy.getByDataCy('signIn-form'); }); }); }); - describe('new expense when logged in', () => { - let user, collective; - - before(() => { - cy.createHostedCollective({ name: 'The Best Collective' }).then(c => { - collective = c; - cy.signup({ - user: { name: 'Potatoes Lover' }, - redirect: encodeURIComponent(`/${collective.slug}/expenses/new?ocr=false&forceLegacyFlow=true`), - }).then(u => (user = u)); - }); - }); - - beforeEach(() => { - cy.login({ - email: user.email, - redirect: encodeURIComponent(`/${collective.slug}/expenses/new?ocr=false&forceLegacyFlow=true`), - }); - }); - - it('has a dismissible help message', () => { - cy.getByDataCy('expense-create-help').should('exist'); - cy.getByDataCy('dismiss-expense-create-help').click(); - cy.getByDataCy('expense-create-help').should('not.exist'); - cy.wait(250); // Give some time for the GQL request - cy.reload(); - cy.waitForLoggedIn(); - cy.wait(200); // Give some time to make sure frontend can fully refresh after logged in - cy.getByDataCy('expense-create-help').should('not.exist'); - }); - - it('submits new expense then edit it', () => { - cy.getByDataCy('radio-expense-type-RECEIPT').click(); - // Select Payout Method - cy.getByDataCy('payout-method-select').click(); - cy.contains('[data-cy="select-option"]', 'New custom payout method').click(); - cy.getByDataCy('currency-picker').click(); - cy.contains('[data-cy="select-option"]', 'US Dollar').click(); - cy.getByDataCy('select-content').should('not.exist'); - cy.get('textarea[name="payoutMethod.data.content"]').type('Bank Account: 007'); - cy.wait(100); - cy.getByDataCy('expense-next').click(); - - cy.get('textarea[name="description"]').type('Brussels January team retreat'); - - cy.getByDataCy('expense-summary-btn').should('be.disabled'); - - // Upload 2 files to the multi-files dropzone - cy.getByDataCy('expense-multi-items-dropzone').selectFile( - [getReceiptFixture({ fileName: 'receipt0.jpg' }), getReceiptFixture()], - { action: 'drag-drop' }, - ); - - cy.getByDataCy('expense-attachment-form').should('have.length', 2); - - // Fill info for first attachment - cy.get('input[name="items[0].description"]').type('Fancy restaurant'); - cy.get('input[name="items[0].amountV2"]').type('{selectall}183'); - cy.getByDataCy('expense-currency-picker').click(); - cy.contains('[data-cy="select-option"]', 'US Dollar').click(); - cy.get('input:invalid').should('have.length', 4); // Missing attachment description, amount, and dates - cy.getByDataCy('expense-items-total-amount').should('contain', '--.--'); // amount for second item is missing - - // Try to submit with missing data (should not submit) - cy.getByDataCy('expense-summary-btn').click(); - - // Fill missing info & submit - cy.get('input[name="items[1].description"]').type('Potatoes for the giant raclette'); - cy.get('input[name="items[1].amountV2"]').type('{selectall}92.50'); - cy.getByDataCy('expense-items-total-amount').should('contain', '$275.50'); - cy.get('input:invalid').should('have.length', 2); - cy.get('input[name="items[0].incurredAt"]').type('2021-01-01'); - cy.get('input[name="items[1].incurredAt"]').type('2021-01-01'); - cy.get('input:invalid').should('have.length', 0); - cy.getByDataCy('expense-summary-btn').click(); - - // Check summary - cy.getByDataCy('expense-summary-payee').should('contain', 'Potatoes Lover'); - cy.getByDataCy('expense-summary-collective').should('contain', 'The Best Collective'); - cy.getByDataCy('expense-summary-payout-method-data').should('contain', 'Bank Account: 007'); - cy.getByDataCy('expense-summary-payout-method-type').should('contain', 'Other'); - cy.getByDataCy('expense-items-total-amount').should('contain', '$275.50'); - cy.getByDataCy('expense-summary-items').should('contain', 'Fancy restaurant'); - cy.getByDataCy('expense-summary-items').should('contain', 'Potatoes for the giant raclette'); - - // Submit! - cy.getByDataCy('submit-expense-btn').click(); - cy.contains('[data-cy="toast-notification"]', 'Expense submitted'); - cy.contains('[data-cy="expense-page-content"]', 'Brussels January team retreat'); - cy.getByDataCy('dismiss-toast-btn').click(); - cy.getByDataCy('toast-notification').should('not.exist'); - - // Start editing - cy.getByDataCy('more-actions').click(); - cy.getByDataCy('edit-expense-btn').click({ force: true }); - cy.getByDataCy('expense-next').click(); - cy.get('textarea[name="description"]').type(' edited'); - cy.get('input[name="items[0].description"]').type(' but not too expensive'); - cy.get('input[name="items[0].amountV2"]').type('{selectall}111'); - // Add new item - cy.getByDataCy('expense-add-item-btn').click(); - cy.get('input[name="items[2].description"]').type('Some more delicious stuff'); - cy.get('input[name="items[2].amountV2"]').type('{selectall}34'); - cy.get('input[name="items[2].incurredAt"]').type('2021-01-01'); - cy.getByDataCy('items[2].url-dropzone').selectFile(getReceiptFixture({ fileName: 'receipt2.jpg' }), { - action: 'drag-drop', - }); - - // Change payee - use a new organization - cy.getByDataCy('expense-back').click(); - cy.getByDataCy('payout-method-select').click(); - cy.contains('[data-cy="select-option"]', 'New custom payout method').click(); - cy.getByDataCy('currency-picker').click(); - cy.contains('[data-cy="select-option"]', 'US Dollar').click(); - cy.getByDataCy('select-content').should('not.exist'); - cy.get('textarea[name="payoutMethod.data.content"]').type('Bank Account: 007'); - cy.getByDataCy('expense-next').click(); - cy.getByDataCy('expense-currency-picker').click(); - cy.contains('[data-cy="select-option"]', 'US Dollar').click(); - cy.get('[data-cy="attachment-url-field"] [data-loading=true]').should('have.length', 0); - cy.getByDataCy('expense-summary-btn').click(); - cy.getByDataCy('save-expense-btn').click(); - cy.getByDataCy('save-expense-btn').should('not.exist'); // wait for form to be submitted - - // Check final expense page - cy.contains('[data-cy="expense-page-content"]', 'Brussels January team retreat edited'); - cy.getByDataCy('expense-summary-payee').should('contain', 'Potatoes Lover'); - cy.getByDataCy('expense-summary-collective').should('contain', 'The Best Collective'); - cy.getByDataCy('expense-summary-payout-method-data').should('contain', 'Bank Account: 007'); - cy.getByDataCy('expense-summary-payout-method-type').should('contain', 'Other'); - cy.getByDataCy('expense-items-total-amount').should('contain', '$237.50'); - cy.getByDataCy('expense-summary-items').should('contain', 'Fancy restaurant'); - cy.getByDataCy('expense-summary-items').should('contain', 'Potatoes for the giant raclette'); - cy.getByDataCy('expense-summary-items').should('contain', 'Some more delicious stuff'); - }); - - it('can use OCR', () => { - cy.login({ - email: user.email, - redirect: encodeURIComponent( - `/${collective.slug}/expenses/new?ocr=true&mockImageUpload=false&forceLegacyFlow=true`, - ), // Add query param to enable OCR and disable mock image upload (since they don't have OCR) - }); - cy.getByDataCy('radio-expense-type-RECEIPT').click(); - cy.getByDataCy('payout-method-select').click(); - cy.contains('[data-cy="select-option"]', 'New custom payout method').click(); - cy.getByDataCy('currency-picker').click(); - cy.contains('[data-cy="select-option"]', 'US Dollar').click(); - cy.getByDataCy('select-content').should('not.exist'); - cy.get('textarea[name="payoutMethod.data.content"]').type('Bank Account: 007'); - cy.getByDataCy('expense-next').click(); - cy.get('textarea[name="description"]').type('An Expense with OCR enabled'); - - // Upload 2 files to the multi-files dropzone - cy.getByDataCy('expense-multi-items-dropzone').selectFile( - [getReceiptFixture({ fileName: 'receipt0.jpg' }), getReceiptFixture({ fileName: 'receipt1.jpg' })], - { action: 'drag-drop' }, - ); - - // Should have 2 loading items with filenames as placeholders - cy.getByDataCy('expense-attachment-form').should('have.length', 2); - cy.get('[data-cy="attachment-url-field"] [data-loading=true]').should('have.length', 2); - cy.get('input[name="items[0].description"]').should('have.attr', 'placeholder', 'receipt0.jpg'); - cy.get('input[name="items[1].description"]').should('have.attr', 'placeholder', 'receipt1.jpg'); - - // When uploading is done... - cy.get('[data-cy="attachment-url-field"] [data-loading=true]', { timeout: 15000 }).should('not.exist'); - - // Check date, amount - cy.get('input[name="items[0].incurredAt"]').should('have.value', '2023-08-01'); - cy.get('input[name="items[0].amountV2"]').should('have.value', '65'); - cy.get('input[name="items[1].incurredAt"]').should('have.value', '2023-08-01'); - cy.get('input[name="items[1].amountV2"]').should('have.value', '65'); - - // Set descriptions - cy.get('input[name="items[0].description"]').type('A custom description'); - - cy.contains('[data-cy="expense-attachment-form"]:eq(1)', 'Suggested: TestMerchant invoice'); - cy.get('[data-cy="expense-attachment-form"]:eq(1) [data-cy="btn-use-suggested-description"]').click(); - cy.get('input[name="items[1].description"]').should('have.value', 'TestMerchant invoice'); - - // Add a third item (prefilled) - cy.getByDataCy('expense-add-item-btn').click(); - cy.getByDataCy('expense-attachment-form').should('have.length', 3); - cy.get('input[name="items[2].description"]').type('A third item'); - cy.get('input[name="items[2].amountV2"]').type('{selectall}100'); - cy.get('input[name="items[2].incurredAt"]').type('2021-01-01'); - cy.getByDataCy('items[2].url-dropzone').selectFile(getReceiptFixture({ fileName: 'receipt2.jpg' }), { - action: 'drag-drop', - }); - cy.get('[data-cy="expense-attachment-form"]:eq(2) [data-loading=true]').should('exist'); - - // When uploading is done... - cy.get('[data-cy="expense-attachment-form"]:eq(2) [data-loading=true]').should('not.exist'); - - // Values should not be overriden - cy.get('input[name="items[2].description"]').should('have.value', 'A third item'); - cy.get('input[name="items[2].amountV2"]').should('have.value', '100.00'); - cy.get('input[name="items[2].incurredAt"]').should('have.value', '2021-01-01'); - - // Check mismatch warnings - cy.contains('Please verify the dates and amounts before proceeding.'); - cy.get('[data-cy="expense-attachment-form"]:eq(2) [data-cy="mismatch-warning"]').should('have.length', 2); - cy.get('input[name="items[1].amountV2"]').type('{selectall}7').blur(); - cy.get('[data-cy="expense-attachment-form"]:eq(1) [data-cy="mismatch-warning"]').should('have.length', 1); - - // Confirm mismatches on the final step - cy.get('[data-cy="attachment-url-field"] [data-loading=true]', { timeout: 15000 }).should('not.exist'); - cy.getByDataCy('expense-summary-btn').click(); - cy.getByDataCy('submit-expense-btn').should('be.disabled'); - cy.contains('label[for="confirm-expense-ocr-values"]', 'I have confirmed the date and amount.').click(); - cy.getByDataCy('submit-expense-btn').should('not.be.disabled'); - cy.getByDataCy('submit-expense-btn').click(); - cy.contains('[data-cy="toast-notification"]', 'Expense submitted'); - cy.contains('[data-cy="expense-items-total-amount"]', '$172.00'); - cy.contains('[data-cy="expense-summary-items"]', 'A custom description'); - cy.contains('[data-cy="expense-summary-items"]', 'TestMerchant invoice'); - cy.contains('[data-cy="expense-summary-items"]', 'A third item'); - }); - - it('can play with the exchange rate', () => { - cy.login({ - email: user.email, - redirect: encodeURIComponent( - `/${collective.slug}/expenses/new?ocr=true&mockImageUpload=false&forceLegacyFlow=true`, - ), // Add query param to enable OCR and disable mock image upload (since they don't have OCR) - }); - cy.getByDataCy('radio-expense-type-RECEIPT').click(); - cy.getByDataCy('payout-method-select').click(); - cy.contains('[data-cy="select-option"]', 'New custom payout method').click(); - cy.getByDataCy('currency-picker').click(); - cy.contains('[data-cy="select-option"]', 'US Dollar').click(); - cy.getByDataCy('select-content').should('not.exist'); - cy.get('textarea[name="payoutMethod.data.content"]').type('Bank Account: 007'); - cy.getByDataCy('expense-next').click(); - cy.get('textarea[name="description"]').type('An Expense with multi-currencies on items + OCR'); - - // Set the expense currency to EUR - cy.getByDataCy('expense-currency-picker').click(); - cy.contains('[data-cy="select-option"]', 'Euro').click(); - - // Upload a file to the multi-files dropzone - cy.getByDataCy('expense-multi-items-dropzone').selectFile( - [getReceiptFixture({ fileName: 'receipt0.jpg' })], // A receipt in USD - { action: 'drag-drop' }, - ); - - // Check date, amount match the return from OCR - cy.get('input[name="items[0].description"]').type('A custom description'); - cy.get('input[name="items[0].incurredAt"]').should('have.value', '2023-08-01'); - cy.getByDataCy('items[0].amountV2-amount-currency-picker').should('contain', 'USD'); - cy.get('input[name="items[0].amountV2"]').should('have.value', '65'); - cy.getByDataCy('items[0].amountV2-amount-converted').should('contain', '= EUR 🇪🇺'); - cy.get('input[name="items[0].amountV2-amount-converted-input"]').should('have.value', '71.50'); - cy.getByDataCy('items[0].amountV2-exchange-rate').should('contain', '1 USD = ~1.1 EUR'); - cy.getByDataCy('items[0].amountV2-exchange-rate').realHover(); - cy.getByDataCy('items[0].amountV2-exchange-rate-tooltip') - .should('contain', '1 USD = 1.1 EUR') - .should('contain', 'Source: Open Collective') - .should('contain', 'Acquired on: August 1, 2023'); - - // Play with the FX rate: value too high - cy.get('input[name="items[0].amountV2-amount-converted-input"]').click(); - cy.getByDataCy('items[0].amountV2-exchange-rate-tooltip').should('not.exist'); - cy.get('input[name="items[0].amountV2-amount-converted-input"]').type('{selectall}{backspace}'); - cy.get('input[name="items[0].amountV2-amount-converted-input"]').type('{selectall}100').blur(); - cy.get('input[name="items[0].amountV2-amount-converted-input"]').should('have.value', '100.00'); - cy.get('input[name="items[0].amountV2-amount-converted-input"]').should('have.attr', 'min', '64.35'); - cy.get('input[name="items[0].amountV2-amount-converted-input"]').should('have.attr', 'max', '78.65'); - cy.get('input[name="items[0].amountV2"]').should('have.value', '65'); // This one doesn't change - cy.getByDataCy('items[0].amountV2-exchange-rate').should('contain', '1 USD = 1.5384615 EUR'); - cy.getByDataCy('items[0].amountV2-exchange-rate').realHover(); - cy.getByDataCy('items[0].amountV2-exchange-rate-tooltip') - .should('contain', '1 USD = 1.5384615 EUR') - .should('contain', 'Source: User') - .should( - 'contain', - "This exchange rate is too different from the one in our records (1.1) for that date and won't be accepted.", - ); - - // With a values that's too low, but still accepted - cy.get('input[name="items[0].amountV2-amount-converted-input"]').click(); - cy.getByDataCy('items[0].amountV2-exchange-rate-tooltip').should('not.exist'); - cy.get('input[name="items[0].amountV2-amount-converted-input"]').type('{selectall}64.88').blur(); - cy.get('input[name="items[0].amountV2"]').should('have.value', '65'); // This one doesn't change - cy.get('input[name="items[0].amountV2-amount-converted-input"]').should('have.value', '64.88'); - cy.get('input[name="items[0].amountV2-amount-converted-input"]').should('have.attr', 'min', '64.35'); - cy.get('input[name="items[0].amountV2-amount-converted-input"]').should('have.attr', 'max', '78.65'); - cy.getByDataCy('items[0].amountV2-exchange-rate').should('contain', '1 USD = 0.9981539 EUR'); - cy.getByDataCy('items[0].amountV2-exchange-rate').realHover(); - cy.getByDataCy('items[0].amountV2-exchange-rate-tooltip') - .should('contain', '1 USD = 0.9981539 EUR') - .should('contain', 'Source: User') - .should('contain', 'This exchange rate is notably different from the one in our records (1.1) for this date.'); - - // Add another item with custom values - cy.getByDataCy('expense-add-item-btn').click(); - cy.getByDataCy('items[0].amountV2-exchange-rate-tooltip').should('not.exist'); - cy.getByDataCy('items[1].amountV2-amount-currency-picker').should('contain', 'EUR'); // Currency is inherited from expense by default - cy.get('input[name="items[1].description"]').type('A second item'); - cy.get('input[name="items[1].amountV2"]').type('{selectall}100'); - cy.get('input[name="items[1].amountV2"]').blur(); - cy.getByDataCy('items[1].url-dropzone').selectFile(getReceiptFixture({ fileName: 'receipt2.jpg' }), { - action: 'drag-drop', - }); - cy.getByDataCy('items[1].amountV2-amount-currency-picker').should('contain', 'EUR'); // Amount/currency shouldn't change - cy.get('input[name="items[1].amountV2"]').should('have.value', '100.00'); - cy.get('[data-cy="mismatch-warning"]').should('have.length', 1); // But there should be warning about mismatch - cy.get('[data-cy="mismatch-warning"] button').realHover(); - cy.get('[data-cy="mismatch-warning-tooltip"]').should( - 'contain', - 'This currency does not match the one scanned from the document (USD)', - ); - // Check what happens when we change the currency to USD - cy.getByDataCy('items[1].amountV2-amount-currency-picker').click(); - cy.contains('[data-cy="select-option"]', 'US Dollar').click(); - cy.get('[data-cy="mismatch-warning"] button').realHover({ position: 'center' }); - cy.get('[data-cy="mismatch-warning-tooltip"]').should( - 'contain', - 'The amount does not match the one scanned from the document (USD $65.00)', - ); - // Go back to EUR and make sure all amounts are correct - cy.getByDataCy('items[1].amountV2-amount-currency-picker').click(); - cy.contains('[data-cy="select-option"]', 'Euro').click(); - cy.get('input[name="items[1].amountV2"]').should('have.value', '100.00'); - cy.getByDataCy('expense-items-total-amount').should('contain', '€164.88'); - - // Submit! - cy.getByDataCy('expense-summary-btn').click(); - cy.contains('label[for="confirm-expense-ocr-values"]', 'I have confirmed the date and amount.').click(); - cy.getByDataCy('submit-expense-btn').click(); - - // Check submitted - cy.contains('[data-cy="toast-notification"]', 'Expense submitted'); - cy.contains('[data-cy="expense-items-total-amount"]', '€164.88'); - cy.getByDataCy('expense-items-total-amount').should('contain', '€164.88'); - cy.get('[data-cy="expense-summary-item-amount"]:eq(0)').should('contain', '€64.88 EUR'); - cy.get('[data-cy="expense-summary-item-amount"]:eq(0)').should('contain', '$65.00 USD'); - // Warning should be on the summary page - cy.get('[data-cy="expense-summary-item-amount"] [data-cy="tooltip-trigger"]').realHover(); - cy.getByDataCy('tooltip-content') - .should('contain', '1 USD = 0.9981539 EUR') - .should('contain', 'Source: User') - .should('contain', 'This exchange rate is notably different from the one in our records (1.1) for this date.'); - - // Edit - cy.getByDataCy('more-actions').click(); - cy.getByDataCy('edit-expense-btn').click({ force: true }); - cy.getByDataCy('expense-next').click(); - cy.getByDataCy('expense-add-item-btn').click(); - cy.getByDataCy('expense-attachment-form').should('have.length', 3); - cy.getByDataCy('items[2].url-dropzone').selectFile(getReceiptFixture({ fileName: 'receipt2.jpg' }), { - action: 'drag-drop', - }); - cy.get('[data-cy="attachment-url-field"] [data-loading=true]').should('have.length', 1); - cy.get('[data-cy="attachment-url-field"] [data-loading=true]').should('have.length', 0); - cy.contains('[data-cy="attachment-url-field"]', 'Replace'); - cy.get('input[name="items[2].description"]').type('A third item'); - cy.get('input[name="items[2].amountV2"]').type('{selectall}100'); - cy.getByDataCy('items[2].amountV2-amount-currency-picker').click(); - cy.contains('[data-cy="select-option"]', 'AED').click(); // FX rate will still default to 1.1 => 110€ - cy.getByDataCy('expense-items-total-amount').should('contain', '€274.88'); - cy.getByDataCy('expense-summary-btn').click(); - cy.contains('label[for="confirm-expense-ocr-values"]', 'I have confirmed the date and amount.').click(); - cy.getByDataCy('save-expense-btn').click(); - cy.getByDataCy('save-expense-btn').should('not.exist'); // wait for form to be submitted - - // Check submitted - cy.contains('[data-cy="expense-items-total-amount"]', '€274.88'); - }); - - // This can happen if you start with an invoice then switch to receipts - it('should prevent submitting receipts if missing items', () => { - cy.getByDataCy('radio-expense-type-INVOICE').click(); - cy.getByDataCy('payout-method-select').click(); - cy.contains('[data-cy="select-option"]', 'New custom payout method').click(); - cy.getByDataCy('currency-picker').click(); - cy.contains('[data-cy="select-option"]', 'US Dollar').click(); - cy.getByDataCy('select-content').should('not.exist'); - cy.getByDataCy('country-select').click(); - cy.contains('[data-cy="select-option"]', 'Algeria').click(); - cy.get('input[data-cy="address-address1"]').type('Street Name, 123'); - cy.get('input[data-cy="address-city"]').type('Citycitycity'); - cy.get('textarea[name="payoutMethod.data.content"]').type('Bank Account: 007'); - cy.getByDataCy('expense-next').click(); - // Fill the form with valid data - cy.get('textarea[name="description"]').type('March invoice'); - cy.get('input[name="items[0].description"]').type('Peeling potatoes'); - cy.getByDataCy('expense-currency-picker').click(); - cy.contains('[data-cy="select-option"]', 'US Dollar').click(); - cy.get('input[name="items[0].amountV2"]').type('{selectall}4200'); - cy.get('input[name="items[0].incurredAt"]').type('2021-03-01'); - - // Switch to receipt and acknowledge error - cy.getByDataCy('radio-expense-type-RECEIPT').click(); - cy.getByDataCy('expense-next').click(); - cy.getByDataCy('expense-summary-btn').click(); - cy.getByDataCy('attachment-url-field').should('contain', 'Receipt required'); - }); - - describe('submit on behalf', () => { - it('can invite an existing user to submit an expense and edit the submitted draft', () => { - cy.getByDataCy('radio-expense-type-INVOICE').click(); - - cy.getByDataCy('select-expense-payee').click(); - cy.get('input#input-payee').type('pia'); - cy.get('#react-select-input-payee-option-0-0').contains('pia').click(); - cy.getByDataCy('expense-next').click(); - // TODO: Make sure there's no payout method input visible - - cy.get('textarea[name="description"]').type('Service Invoice'); - cy.get('input[name="items[0].description"]').type('Item 1'); - cy.get('input[name="items[0].amountV2"]').type('{selectall}4200'); - cy.get('input[name="items[0].incurredAt"]').type('2021-01-01'); - - cy.getByDataCy('expense-summary-btn').click(); - cy.wait(500); - - cy.getByDataCy('expense-status-msg').should('contain', 'Draft'); - cy.getByDataCy('expense-draft-banner').should('contain', 'Your invite is on its way'); - cy.getByDataCy('expense-draft-banner').should( - 'contain', - `An invitation to submit this expense has been sent to`, - ); - - // Edits the Draft - cy.getByDataCy('more-actions').click(); - cy.getByDataCy('edit-expense-btn').should('exist'); // wait for form to be submitted - cy.getByDataCy('edit-expense-btn').click({ force: true }); - cy.get('textarea[name="invoiceInfo"]').type('VAT ES 123123'); - cy.getByDataCy('expense-next').click(); - cy.get('textarea[name="description"]').type('{selectall}Edited Service Invoice'); - cy.get('input[name="items[0].amountV2"]').type('{selectall}420'); - - cy.getByDataCy('expense-summary-btn').click(); - cy.getByDataCy('save-expense-btn').click(); - cy.getByDataCy('save-expense-btn').should('not.exist'); // wait for form to be submitted - - cy.getByDataCy('expense-status-msg').should('contain', 'Draft'); - cy.getByDataCy('expense-description').should('contain', 'Edited Service Invoice'); - cy.getByDataCy('expense-summary-invoice-info').should('contain', 'VAT ES 123123'); - cy.getByDataCy('expense-items-total-amount').should('contain', '$420.00'); - }); - - it('can invite a third-party user to submit an expense', () => { - const inviteeEmail = randomEmail(); - cy.getByDataCy('radio-expense-type-INVOICE').click(); - - cy.getByDataCy('select-expense-payee').click(); - cy.getByDataCy('collective-picker-invite-button').click(); - cy.get('input[name="payee.name"]').type('Nicolas Cage'); - cy.get('input[name="payee.email"]').type(inviteeEmail); - cy.get('[data-cy="expense-next"]').click(); - - cy.get('textarea[name="description"]').type('Service Invoice'); - cy.get('input[name="items[0].amountV2"]').type('{selectall}4200'); - cy.get('input[name="items[0].incurredAt"]').type('2021-01-01'); - - cy.getByDataCy('expense-summary-btn').click(); - cy.wait(500); - - cy.getByDataCy('expense-status-msg').should('contain', 'Draft'); - cy.getByDataCy('expense-draft-banner').should('contain', 'Your invite is on its way'); - cy.getByDataCy('expense-draft-banner').should( - 'contain', - `An invitation to submit this expense has been sent to ${inviteeEmail}`, - ); - cy.getByDataCy('expense-author').should('contain', 'Invited by'); - cy.getByDataCy('expense-summary-payee').should('contain', 'Nicolas Cage'); - - // Log out and submit as invitee... - cy.url({ log: true }) - .then(_url => { - const [, collective, expenseId] = _url.match(/\/([\w-]+)\/expenses\/(\w+)/); - return { collective, expenseId }; - }) - .as('createdExpense'); - cy.logout(); - cy.reload(); - - cy.get('@createdExpense').then(createdExpense => { - cy.visit(`/${createdExpense.collective}/expenses/${createdExpense.expenseId}?key=draft-key`); - }); - - cy.getByDataCy('country-select').click(); - cy.contains('[data-cy="select-option"]', 'Algeria').click(); - cy.get('[data-cy="address-address1"]').type('Street Name, 123'); - cy.get('[data-cy="address-city"]').type('City'); - - cy.getByDataCy('payout-method-select').click(); - cy.contains('[data-cy="select-option"]', 'New custom payout method').click(); - cy.getByDataCy('currency-picker').click(); - cy.contains('[data-cy="select-option"]', 'US Dollar').click(); - cy.getByDataCy('select-content').should('not.exist'); - cy.get('textarea[name="payoutMethod.data.content"]').type('make it rain'); - - cy.getByDataCy('expense-next').click(); - - cy.get('input[name="items[0].description"]').type('That service'); - cy.getByDataCy('expense-currency-picker').click(); - cy.contains('[data-cy="select-option"]', 'US Dollar').click(); - cy.get('input[name="items[0].amountV2"]').type('{selectall}4200'); - cy.getByDataCy('expense-summary-btn').click(); - cy.get('[data-cy="checkbox-tos"] [data-cy="custom-checkbox"]').click(); - cy.getByDataCy('save-expense-btn').click(); - cy.wait(500); - cy.getByDataCy('expense-status-msg').should('contain', 'Pending'); - cy.getByDataCy('expense-author').should('contain', 'Submitted by'); - cy.getByDataCy('expense-summary-payee').should('contain', 'Nicolas Cage'); - cy.getByDataCy('expense-summary-collective').should('contain', 'The Best Collective'); - cy.getByDataCy('expense-summary-payout-method-data').should('contain', '********'); - - cy.get('@createdExpense').then(createdExpense => { - cy.login({ - email: inviteeEmail, - redirect: `/${createdExpense.collective}/expenses/${createdExpense.expenseId}`, - }); - }); - - cy.getByDataCy('expense-summary-payout-method-data').should('contain', 'make it rain'); - }); - - it('can invite a third-party organization to submit an expense', () => { - const inviteeEmail = randomEmail(); - const slug = randomSlug(); - cy.getByDataCy('radio-expense-type-INVOICE').click(); - - cy.getByDataCy('select-expense-payee').click(); - cy.getByDataCy('collective-picker-invite-button').click(); - cy.getByDataCy('payee-type-org').click(); - cy.get('input[name="payee.organization.name"]').type(slug); - cy.get('input[name="payee.organization.description"]').type('We make movies.'); - cy.get('input[name="payee.organization.website"]').type('http://hollywood.com'); - cy.get('input[name="payee.name"]').type('Willem Dafoe'); - cy.get('input[name="payee.email"]').type(inviteeEmail); - cy.get('[data-cy="expense-next"]').click(); - - cy.get('textarea[name="description"]').type('Service Invoice'); - cy.get('input[name="items[0].amountV2"]').type('{selectall}4200'); - cy.get('input[name="items[0].incurredAt"]').type('2021-01-01'); - - cy.getByDataCy('expense-summary-btn').click(); - cy.wait(500); - - cy.getByDataCy('expense-status-msg').should('contain', 'Draft'); - cy.getByDataCy('expense-draft-banner').should('contain', 'Your invite is on its way'); - cy.getByDataCy('expense-draft-banner').should( - 'contain', - `An invitation to submit this expense has been sent to ${inviteeEmail}`, - ); - cy.getByDataCy('expense-author').should('contain', 'Invited by'); - cy.getByDataCy('expense-summary-payee').should('contain', slug); - - // Log out and submit as invitee... - cy.url({ log: true }) - .then(_url => { - const [, collective, expenseId] = _url.match(/\/([\w-]+)\/expenses\/(\w+)/); - return { collective, expenseId }; - }) - .as('createdExpense'); - - cy.visit('/'); - cy.logout(); - cy.reload(); - cy.get('@createdExpense').then(createdExpense => { - cy.visit(`/${createdExpense.collective}/expenses/${createdExpense.expenseId}?key=draft-key`); - }); - - cy.getByDataCy('country-select').click(); - cy.contains('[data-cy="select-option"]', 'Algeria').click(); - cy.get('[data-cy="address-address1"]').type('Street Name, 123'); - cy.get('[data-cy="address-city"]').type('City'); - - cy.getByDataCy('payout-method-select').click(); - cy.contains('[data-cy="select-option"]', 'New custom payout method').click(); - cy.getByDataCy('currency-picker').click(); - cy.contains('[data-cy="select-option"]', 'US Dollar').click(); - cy.getByDataCy('select-content').should('not.exist'); - - cy.get('textarea[name="payoutMethod.data.content"]').type('make it rain'); - - cy.getByDataCy('expense-next').click(); - - cy.get('input[name="items[0].description"]').type('That service'); - cy.getByDataCy('expense-currency-picker').click(); - cy.contains('[data-cy="select-option"]', 'US Dollar').click(); - cy.get('input[name="items[0].amountV2"]').type('{selectall}4200'); - cy.getByDataCy('expense-summary-btn').click(); - cy.get('[data-cy="checkbox-tos"] [data-cy="custom-checkbox"]').click(); - cy.getByDataCy('save-expense-btn').click(); - cy.wait(500); - cy.getByDataCy('expense-status-msg').should('contain', 'Pending'); - - cy.get('@createdExpense').then(createdExpense => { - cy.login({ - email: inviteeEmail, - redirect: `/${createdExpense.collective}/expenses/${createdExpense.expenseId}`, - }); - }); - - cy.getByDataCy('expense-status-msg').should('contain', 'Pending'); - cy.getByDataCy('expense-author').should('contain', 'Submitted by'); - cy.getByDataCy('expense-summary-payee').should('contain', slug); - cy.getByDataCy('expense-summary-collective').should('contain', 'The Best Collective'); - cy.getByDataCy('expense-summary-payout-method-data').should('contain', 'make it rain'); - }); - }); - }); - - describe('new expense with taxes', () => { - let collective; - - before(() => { - cy.createHostedCollective().then(c => (collective = c)); - }); - - it('can submit with VAT', () => { - // Activate VAT for collective - cy.editCollective({ - id: collective.id, - location: { country: 'BE' }, - settings: { VAT: { type: 'OWN', number: 'FRXX999999999' } }, - }); - - cy.login({ redirect: encodeURIComponent(`/${collective.slug}/expenses/new?forceLegacyFlow=true`) }); - cy.getByDataCy('radio-expense-type-INVOICE').click(); - - // ---- 1. Submit expense with VAT ---- - - // Fill payee / payout method - cy.getByDataCy('payout-method-select').click(); - cy.contains('[data-cy="select-option"]', 'New custom payout method').click(); - cy.getByDataCy('currency-picker').click(); - cy.contains('[data-cy="select-option"]', 'US Dollar').click(); - cy.getByDataCy('select-content').should('not.exist'); - cy.get('textarea[name="payoutMethod.data.content"]').type('Bank Account: 007'); - cy.getByDataCy('country-select').click(); - cy.contains('[data-cy="select-option"]', 'Algeria').click(); - cy.get('input[data-cy="address-address1"]').type('Street Name, 123'); - cy.get('input[data-cy="address-city"]').type('Citycitycity'); - cy.wait(100); - cy.getByDataCy('expense-next').click(); - - // Fill details - cy.get('textarea[name="description"]').type('Brussels January team retreat'); - cy.get('input[name="items[0].description"]').type('TShirts'); - cy.get('input[name="items[0].amountV2"]').type('{selectall}112'); - cy.get('input[name="items[0].incurredAt"]').type('2021-01-01'); - cy.getByDataCy('expense-currency-picker').click(); - cy.contains('[data-cy="select-option"]', 'US Dollar').click(); - cy.getByDataCy('expense-add-item-btn').click(); - cy.get('input[name="items[1].description"]').type('Potatoes for the giant raclette'); - cy.get('input[name="items[1].amountV2"]').type('{selectall}75.5'); - cy.get('input[name="items[1].incurredAt"]').type('2021-01-01'); - - // Need to fill in the tax rate before we can go next - cy.get('input[name="taxes.0.idNumber"]').should('not.have.attr', 'required'); // Not required if the rate is not set or 0 - cy.getByDataCy('expense-items-total-amount').should('contain', '--.--'); - cy.getByDataCy('tax-VAT-expense-amount-line').should('contain', '--.--'); - cy.getByDataCy('expense-summary-btn').click(); - cy.get('input:invalid').should('have.length', 1); - - // Breakdown should be correct - cy.get('input[name="taxes.0.rate"]').type('5.5'); - cy.get('input[name="taxes.0.idNumber"]').should('have.attr', 'required', 'required'); // Required if the rate is a positive number - cy.get('input[name="taxes.0.idNumber"]').type('FRXX999999999'); - cy.getByDataCy('expense-invoiced-amount').should('contain', '$187.50'); - cy.getByDataCy('tax-VAT-expense-amount-line').should('contain', '$10.31'); - cy.getByDataCy('expense-items-total-amount').should('contain', '$197.81'); - - // Check summary - cy.getByDataCy('expense-summary-btn').click(); - cy.getByDataCy('expense-invoiced-amount').should('contain', '$187.50'); - cy.getByDataCy('tax-VAT-expense-amount-line').should('contain', '$10.31'); - cy.getByDataCy('expense-items-total-amount').should('contain', '$197.81'); - - // Submit! - cy.getByDataCy('submit-expense-btn').click(); - cy.contains('[data-cy="toast-notification"]', 'Expense submitted'); - cy.getByDataCy('expense-invoiced-amount').should('contain', '$187.50'); - cy.getByDataCy('tax-VAT-expense-amount-line').should('contain', '$10.31'); - cy.getByDataCy('expense-items-total-amount').should('contain', '$197.81'); - - // ---- 2. Edit VAT rate ---- - - // Start editing - cy.getByDataCy('more-actions').click(); - cy.getByDataCy('edit-expense-btn').click({ force: true }); - cy.getByDataCy('expense-next').click(); - - // Add new item - cy.getByDataCy('expense-currency-picker').click(); - cy.contains('[data-cy="select-option"]', 'US Dollar').click(); - cy.getByDataCy('expense-add-item-btn').click(); - cy.get('input[name="items[2].description"]').type('Some more delicious stuff'); - cy.get('input[name="items[2].amountV2"]').type('{selectall}34'); - cy.get('input[name="items[2].incurredAt"]').type('2021-01-01'); - cy.getByDataCy('expense-invoiced-amount').should('contain', '$221.50'); - cy.getByDataCy('tax-VAT-expense-amount-line').should('contain', '$12.18'); - cy.getByDataCy('expense-items-total-amount').should('contain', '$233.68'); - - // Change tax rate - cy.get('input[name="taxes.0.rate"]').type('{selectall}17.7'); - cy.getByDataCy('expense-summary-btn').click(); - cy.getByDataCy('save-expense-btn').click(); - cy.getByDataCy('save-expense-btn').should('not.exist'); // wait for form to be submitted - - // Check final expense page - cy.getByDataCy('expense-invoiced-amount').should('contain', '$221.50'); - cy.getByDataCy('tax-VAT-expense-amount-line').should('contain', '$39.21'); - cy.getByDataCy('expense-items-total-amount').should('contain', '$260.71'); - - // ---- 3. Remove VAT ---- - // Start editing - cy.get('[data-cy="more-actions"]:visible').click(); - cy.getByDataCy('edit-expense-btn').click({ force: true }); - cy.getByDataCy('expense-next').click(); - - // Disable VAT - cy.getByDataCy('checkbox-tax-VAT').click(); - cy.getByDataCy('expense-currency-picker').click(); - cy.contains('[data-cy="select-option"]', 'US Dollar').click(); - cy.getByDataCy('expense-summary-btn').click(); - cy.getByDataCy('save-expense-btn').click(); - cy.getByDataCy('save-expense-btn').should('not.exist'); // wait for form to be submitted - - // Check final expense page - cy.getByDataCy('expense-items-total-amount').should('contain', '$221.50'); - cy.getByDataCy('expense-invoiced-amount').should('not.exist'); // No breakdown if there's no taxes - }); - }); - describe('Actions on expense', () => { let collective; let user; @@ -835,7 +95,7 @@ describe('Expense flow', () => { cy.getByDataCy('receipt-expense-policy-input').click().type('this is my test expense policy'); cy.getByDataCy('submit-policy-btn').click(); cy.checkToast({ variant: 'success', message: 'Policies updated successfully' }); - cy.visit(`${expenseUrl}?forceLegacyFlow=true`); + cy.visit(expenseUrl); cy.get('[data-cy="submit-expense-dropdown"]:visible:first').click(); cy.getByDataCy('expense-policy-html').contains('this is my test expense policy'); }); @@ -847,7 +107,7 @@ describe('Expense flow', () => { cy.getByDataCy('submit-policy-btn').click(); cy.checkToast({ variant: 'success', message: 'Policies updated successfully' }); cy.createProject({ userEmail: user.email, collective }).then(project => { - cy.visit(`/${project.slug}/expenses/new?forceLegacyFlow=true`); + cy.visit(`/${project.slug}/expenses/new`); cy.getByDataCy('expense-policy-html').contains('this is my test expense policy'); }); }); diff --git a/test/cypress/integration/27-new-expense-flow.test.ts b/test/cypress/integration/27-new-expense-flow.test.ts index dd341c835e2..007e4741026 100644 --- a/test/cypress/integration/27-new-expense-flow.test.ts +++ b/test/cypress/integration/27-new-expense-flow.test.ts @@ -78,7 +78,7 @@ function commonScenarios(expenseType: 'invoice' | 'reimbursement') { cy.wrap(userSlug).as('userSlug'); cy.signup({ user: { name: userSlug, email: `oc-test-${userSlug}@opencollective.com` }, - redirect: `/dashboard/${userSlug}/submitted-expenses?newExpenseFlowEnabled=true`, + redirect: `/dashboard/${userSlug}/submitted-expenses`, }); cy.get('@userSlug').then(userSlug => @@ -354,7 +354,6 @@ function getExpenseInviteEmailLink(to: string) { const expenseLink = $html(`a:contains("OK, let's go!")`); const href = expenseLink.attr('href'); const parsedUrl = new URL(href); - parsedUrl.searchParams.set('newExpenseFlowEnabled', 'true'); return `${parsedUrl.pathname}${parsedUrl.search.toString()}`; }); } @@ -619,7 +618,7 @@ function commonEditScenario(expenseType: 'invoice' | 'reimbursement') { const userSlug = randomSlug(); cy.signup({ user: { name: userSlug, email: `oc-test-${userSlug}@opencollective.com` }, - redirect: `/dashboard/${userSlug}/submitted-expenses?newExpenseFlowEnabled=true`, + redirect: `/dashboard/${userSlug}/submitted-expenses`, }); cy.location('pathname').should('eq', `/dashboard/${userSlug}/submitted-expenses`, { @@ -641,7 +640,7 @@ function commonEditScenario(expenseType: 'invoice' | 'reimbursement') { }, }).then(createdExpense => { expense = createdExpense; - cy.visit(`/apex/expenses/${expense.legacyId}?newExpenseFlowEnabled=true`); + cy.visit(`/apex/expenses/${expense.legacyId}`); }); }); diff --git a/test/cypress/integration/32-grant-submission.test.ts b/test/cypress/integration/32-grant-submission.test.ts index 0cda0c6525f..b8e1f98546c 100644 --- a/test/cypress/integration/32-grant-submission.test.ts +++ b/test/cypress/integration/32-grant-submission.test.ts @@ -22,7 +22,7 @@ describe('Grant Submission Flow', () => { it('should allow users to submit a grant application', function () { // Go to the grant submission page with the new flow enabled - cy.visit(`/${this.collective.slug}/grants/new?newGrantFlowEnabled=true`); + cy.visit(`/${this.collective.slug}/grants/new`); // Verify the page header shows the correct collective name cy.contains(`Grant request to ${this.collective.name}`).should('be.visible'); @@ -95,7 +95,7 @@ describe('Grant Submission Flow', () => { }); it('should allow host admin to create beneficiary', function () { - cy.login({ email: this.hostAdmin.email, redirect: `/${this.collective.slug}/grants/new?newGrantFlowEnabled=true` }); + cy.login({ email: this.hostAdmin.email, redirect: `/${this.collective.slug}/grants/new` }); cy.contains('button', 'Proceed').click(); cy.get('#WHO_WILL_RECEIVE_FUNDS').within(() => { cy.contains('A beneficiary').click(); @@ -157,7 +157,7 @@ describe('Grant Submission Flow', () => { this.hostAdmin.email, ); - cy.login({ email: this.hostAdmin.email, redirect: `/${this.collective.slug}/grants/new?newGrantFlowEnabled=true` }); + cy.login({ email: this.hostAdmin.email, redirect: `/${this.collective.slug}/grants/new` }); cy.contains('button', 'Proceed').click(); cy.get('#WHO_WILL_RECEIVE_FUNDS').within(() => { cy.contains('A beneficiary').click(); @@ -222,7 +222,7 @@ describe('Grant Submission Flow', () => { this.hostAdmin.email, ); - cy.login({ email: this.hostAdmin.email, redirect: `/${this.collective.slug}/grants/new?newGrantFlowEnabled=true` }); + cy.login({ email: this.hostAdmin.email, redirect: `/${this.collective.slug}/grants/new` }); cy.contains('button', 'Proceed').click(); cy.get('#WHO_WILL_RECEIVE_FUNDS').within(() => { cy.contains('A beneficiary').click();