Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions components/AccountingCategorySelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
45 changes: 7 additions & 38 deletions components/collective-navbar/ActionsMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -227,33 +212,17 @@ const CollectiveNavbarActionsMenu = ({
<Box as="ul" p={0} m={0} minWidth={184}>
{callsToAction.hasSubmitExpense && (
<MenuItem isHiddenOnMobile={hiddenActionForNonMobile === NAVBAR_ACTION_TYPE.SUBMIT_EXPENSE}>
{isNewExpenseFlowEnabled ? (
<StyledLink onClick={onOpenSubmitExpenseModalClick}>
<Container p={ITEM_PADDING}>
<Receipt size="20px" />
<FormattedMessage id="ExpenseForm.Submit" defaultMessage="Submit expense" />
</Container>
</StyledLink>
) : (
<StyledLink
data-cy="submit-expense-dropdown"
as={Link}
href={`${getCollectivePageRoute(collective)}/expenses/new?forceLegacyFlow=true`}
>
<Container p={ITEM_PADDING}>
<Receipt size="20px" />
<FormattedMessage id="ExpenseForm.Submit" defaultMessage="Submit expense" />
</Container>
</StyledLink>
)}
<StyledLink onClick={onOpenSubmitExpenseModalClick}>
<Container p={ITEM_PADDING}>
<Receipt size="20px" />
<FormattedMessage id="ExpenseForm.Submit" defaultMessage="Submit expense" />
</Container>
</StyledLink>
</MenuItem>
)}
{callsToAction.hasRequestGrant && (
<MenuItem py={1} isHiddenOnMobile={hiddenActionForNonMobile === NAVBAR_ACTION_TYPE.REQUEST_GRANT}>
<StyledLink
as={Link}
href={`${getCollectivePageRoute(collective)}/${isNewGrantFlowEnabled ? 'grants' : 'expenses'}/new`}
>
<StyledLink as={Link} href={`${getCollectivePageRoute(collective)}/grants/new`}>
<Container p={ITEM_PADDING}>
<MoneyCheckAlt size="20px" />
<FormattedMessage id="ExpenseForm.Type.Request" defaultMessage="Request Grant" />
Expand Down
49 changes: 6 additions & 43 deletions components/collective-navbar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -362,7 +352,7 @@ const getMainAction = (
return {
type: NAVBAR_ACTION_TYPE.REQUEST_GRANT,
component: (
<Link href={`${getCollectivePageRoute(collective)}/${isNewGrantFlowEnabled ? 'grants' : 'expenses'}/new`}>
<Link href={`${getCollectivePageRoute(collective)}/grants/new`}>
<ActionButton tabIndex="-1">
<MoneyCheckAlt size="1em" />
<Span ml={2}>
Expand All @@ -375,22 +365,13 @@ const getMainAction = (
} else if (callsToAction.includes('hasSubmitExpense')) {
return {
type: NAVBAR_ACTION_TYPE.SUBMIT_EXPENSE,
component: isNewExpenseFlowEnabled ? (
component: (
<ActionButton tabIndex="-1" onClick={onOpenSubmitExpenseModalClick}>
<Receipt size="1em" />
<Span ml={2}>
<FormattedMessage id="menu.submitExpense" defaultMessage="Submit Expense" />
</Span>
</ActionButton>
) : (
<Link href={`${getCollectivePageRoute(collective)}/expenses/new`} data-cy="submit-expense-dropdown">
<ActionButton tabIndex="-1">
<Receipt size="1em" />
<Span ml={2}>
<FormattedMessage id="menu.submitExpense" defaultMessage="Submit Expense" />
</Span>
</ActionButton>
</Link>
),
};
} else if (callsToAction.includes('hasManageSubscriptions')) {
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down
14 changes: 3 additions & 11 deletions components/dashboard/sections/expenses/PaymentRequests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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<FilterValues> = useMemo(
() => [
Expand Down Expand Up @@ -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 (
Expand All @@ -194,11 +188,9 @@ const PaymentRequests = ({ accountSlug }: DashboardSectionProps) => {
/>
}
actions={
hasNewSubmitExpenseFlow ? (
<Button onClick={() => setIsExpenseFlowOpen(true)} size="sm" className="gap-1">
<FormattedMessage defaultMessage="New expense" id="pNn/g+" />
</Button>
) : null
<Button onClick={() => setIsExpenseFlowOpen(true)} size="sm" className="gap-1">
<FormattedMessage defaultMessage="New expense" id="pNn/g+" />
</Button>
}
/>
{isSelfHosted && (
Expand Down
14 changes: 3 additions & 11 deletions components/dashboard/sections/expenses/ReceivedExpenses.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 (
Expand All @@ -154,11 +148,9 @@ const ReceivedExpenses = ({ accountSlug }: DashboardSectionProps) => {
title={<FormattedMessage defaultMessage="Received Expenses" id="1c0Y31" />}
description={<FormattedMessage defaultMessage="Expenses submitted to your account." id="0I3Lbj" />}
actions={
hasNewSubmitExpenseFlow ? (
<Button onClick={() => setIsExpenseFlowOpen(true)} size="sm" className="gap-1">
<FormattedMessage defaultMessage="New expense" id="pNn/g+" />
</Button>
) : null
<Button onClick={() => setIsExpenseFlowOpen(true)} size="sm" className="gap-1">
<FormattedMessage defaultMessage="New expense" id="pNn/g+" />
</Button>
}
/>
{isSelfHosted && (
Expand Down
26 changes: 10 additions & 16 deletions components/dashboard/sections/expenses/SubmittedExpenses.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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 (
Expand All @@ -81,18 +77,16 @@ const SubmittedExpenses = ({ accountSlug }: DashboardSectionProps) => {
<FormattedMessage defaultMessage="Expenses that you have submitted to other accounts." id="aKfm6V" />
}
actions={
hasNewSubmitExpenseFlow ? (
<Button
onClick={() => {
setDuplicateExpenseId(null);
setIsExpenseFlowOpen(true);
}}
size="sm"
className="gap-1"
>
<FormattedMessage defaultMessage="New expense" id="pNn/g+" />
</Button>
) : null
<Button
onClick={() => {
setDuplicateExpenseId(null);
setIsExpenseFlowOpen(true);
}}
size="sm"
className="gap-1"
>
<FormattedMessage defaultMessage="New expense" id="pNn/g+" />
</Button>
}
/>
<Filterbar {...queryFilter} meta={filterMeta} />
Expand Down
8 changes: 2 additions & 6 deletions components/dashboard/sections/expenses/actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -427,11 +427,7 @@ export function useExpenseActions<T extends ExpenseQueryNode>({
}
};

// 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([
Expand Down
Loading