Skip to content

Commit

Permalink
Merge branch 'main' into provide-signed-url-for-uploaded-expense-files
Browse files Browse the repository at this point in the history
  • Loading branch information
hdiniz authored Dec 3, 2024
2 parents 0d39296 + 89dd6fc commit d680677
Show file tree
Hide file tree
Showing 125 changed files with 9,201 additions and 5,724 deletions.
2 changes: 1 addition & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
"arrowParens": "avoid",
"printWidth": 120,
"plugins": ["prettier-plugin-tailwindcss"],
"tailwindFunctions": ["clsx", "cn"]
"tailwindFunctions": ["clsx", "cn", "cva"]
}
2 changes: 1 addition & 1 deletion components/AvatarWithLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Avatar from './Avatar';
import LinkCollective from './LinkCollective';

type AvatarWithLinkProps = {
account: Pick<Account, 'name' | 'type' | 'isIncognito' | 'slug' | 'imageUrl'>;
account: Pick<Account, 'name' | 'type' | 'slug' | 'imageUrl'> & { isIncognito?: boolean };
secondaryAccount?: Partial<Account> | null;
/** The size in pixels */
size: number;
Expand Down
6 changes: 3 additions & 3 deletions components/PayoutMethodLabel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export function PayoutMethodLabel(props: PayoutMethodLabelProps) {
} else if (pm.data?.details?.clabe) {
label = `Clabe ${pm.data.details.clabe}`;
} else if (pm.data?.details?.bankgiroNumber) {
label = `BankGiro ${pm.data.details.bankgiroNumber}`;
label = `Bankgiro ${pm.data.details.bankgiroNumber}`;
} else if (pm.data?.accountHolderName && pm.data?.currency) {
label = `${pm.data.accountHolderName} (${pm.data.currency})`;
}
Expand All @@ -83,11 +83,11 @@ export function PayoutMethodLabel(props: PayoutMethodLabelProps) {

if (props.showIcon) {
return (
<span className="whitespace-nowrap">
<div className="flex items-center gap-2 whitespace-nowrap">
<PayoutMethodIcon payoutMethod={pm} />
&nbsp;
{label}
</span>
</div>
);
}

Expand Down
32 changes: 18 additions & 14 deletions components/StyledDropzone.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ const StyledDropzone = ({
previewSize = size,
limit = undefined,
kind = null,
showReplaceAction = true,
...props
}: StyledDropzoneProps) => {
const { toast } = useToast();
Expand Down Expand Up @@ -189,7 +190,7 @@ const StyledDropzone = ({
size={size}
error={error}
>
<input name={name} {...getInputProps()} />
<input name={name} disabled={props.disabled} {...getInputProps()} />
{isLoading || isUploading || isUploadingWithGraphQL ? (
<Container
position="relative"
Expand Down Expand Up @@ -320,19 +321,21 @@ const StyledDropzone = ({
) : typeof value === 'string' ? (
<React.Fragment>
<UploadedFilePreview size={previewSize || size} url={value} border="none" />
<ReplaceContainer
onClick={dropProps.onClick}
role="button"
tabIndex={0}
onKeyDown={event => {
if (event.key === 'Enter') {
event.preventDefault();
dropProps.onClick(null);
}
}}
>
<FormattedMessage id="Image.Replace" defaultMessage="Replace" />
</ReplaceContainer>
{showReplaceAction && (
<ReplaceContainer
onClick={dropProps.onClick}
role="button"
tabIndex={0}
onKeyDown={event => {
if (event.key === 'Enter') {
event.preventDefault();
dropProps.onClick(null);
}
}}
>
<FormattedMessage id="Image.Replace" defaultMessage="Replace" />
</ReplaceContainer>
)}
</React.Fragment>
) : value instanceof File ? (
<LocalFilePreview size={previewSize || size} file={value} alignItems="center" />
Expand Down Expand Up @@ -418,6 +421,7 @@ type StyledDropzoneProps = Omit<ContainerProps, 'accept' | 'children' | 'ref' |
UploadingComponent?: React.ComponentType;
/** When isMulti is true, limit the number of files that can be uploaded */
limit?: number;
showReplaceAction?: boolean;
} & (
| {
/** Collect File only, do not upload files */
Expand Down
2 changes: 1 addition & 1 deletion components/StyledInputFormikField.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ const StyledInputFormikField = ({
StyledInputFormikField.propTypes = {
name: PropTypes.string.isRequired,
validate: PropTypes.func,
isFastField: PropTypes.func,
isFastField: PropTypes.bool,
children: PropTypes.func,
/** the label's 'for' attribute to be used as the 'name' and 'id' for the input */
htmlFor: PropTypes.string,
Expand Down
2 changes: 1 addition & 1 deletion components/UploadedFilePreview.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ const UploadedFilePreview = ({
}

const getContainerAttributes = () => {
if (isPrivate) {
if (isPrivate || !url) {
return { as: 'div' };
} else if (isText || !openFileViewer) {
return { href: url, target: '_blank', rel: 'noopener noreferrer', as: url.startsWith('/') ? Link : StyledLink };
Expand Down
45 changes: 34 additions & 11 deletions components/collective-navbar/ActionsMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ 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 ActionButton from '../ActionButton';
Expand Down Expand Up @@ -174,11 +177,21 @@ const StyledChevronDown = styled(ChevronDown)`

const ITEM_PADDING = '11px 14px';

const CollectiveNavbarActionsMenu = ({ collective, callsToAction = {}, hiddenActionForNonMobile, LoggedInUser }) => {
const CollectiveNavbarActionsMenu = ({
collective,
callsToAction = {},
hiddenActionForNonMobile,
LoggedInUser,
onOpenSubmitExpenseModalClick = () => {},
}) => {
const enabledCTAs = Object.keys(pickBy(callsToAction, Boolean));
const isEmpty = enabledCTAs.length < 1;
const hasOnlyOneHiddenCTA = enabledCTAs.length === 1 && hiddenActionForNonMobile === enabledCTAs[0];

const isNewExpenseFlowEnabled =
LoggedInUser?.hasPreviewFeatureEnabled(PREVIEW_FEATURE_KEYS.NEW_EXPENSE_FLOW) &&
!isSupportedExpenseType(collective, ExpenseType.GRANT);

// Do not render the menu if there are no available CTAs
if (isEmpty) {
return null;
Expand Down Expand Up @@ -226,16 +239,25 @@ const CollectiveNavbarActionsMenu = ({ collective, callsToAction = {}, hiddenAct
)}
{callsToAction.hasSubmitExpense && (
<MenuItem isHiddenOnMobile={hiddenActionForNonMobile === NAVBAR_ACTION_TYPE.SUBMIT_EXPENSE}>
<StyledLink
data-cy="submit-expense-dropdown"
as={Link}
href={`${getCollectivePageRoute(collective)}/expenses/new`}
>
<Container p={ITEM_PADDING}>
<Receipt size="20px" />
<FormattedMessage id="ExpenseForm.Submit" defaultMessage="Submit expense" />
</Container>
</StyledLink>
{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`}
>
<Container p={ITEM_PADDING}>
<Receipt size="20px" />
<FormattedMessage id="ExpenseForm.Submit" defaultMessage="Submit expense" />
</Container>
</StyledLink>
)}
</MenuItem>
)}
{callsToAction.hasRequestGrant && (
Expand Down Expand Up @@ -418,6 +440,7 @@ CollectiveNavbarActionsMenu.propTypes = {
}).isRequired,
hiddenActionForNonMobile: PropTypes.oneOf(Object.values(NAVBAR_ACTION_TYPE)),
LoggedInUser: PropTypes.object,
onOpenSubmitExpenseModalClick: PropTypes.func,
};

export default CollectiveNavbarActionsMenu;
44 changes: 40 additions & 4 deletions components/collective-navbar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ import EXPENSE_TYPE from '../../lib/constants/expenseTypes';
import roles from '../../lib/constants/roles';
import { isSupportedExpenseType } from '../../lib/expenses';
import { API_V2_CONTEXT, 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 ActionButton from '../ActionButton';
Expand All @@ -40,6 +42,7 @@ import LinkCollective from '../LinkCollective';
import LoadingPlaceholder from '../LoadingPlaceholder';
import StyledButton from '../StyledButton';
import { fadeIn } from '../StyledKeyframes';
import { SubmitExpenseFlow } from '../submit-expense/SubmitExpenseFlow';
import { Span } from '../Text';

import CollectiveNavbarActionsMenu from './ActionsMenu';
Expand Down Expand Up @@ -310,7 +313,13 @@ const getDefaultCallsToActions = (
/**
* Returns the main CTA that should be displayed as a button outside of the action menu in this component.
*/
const getMainAction = (collective, callsToAction, LoggedInUser) => {
const getMainAction = (
collective,
callsToAction,
LoggedInUser,
isNewExpenseFlowEnabled = false,
onOpenSubmitExpenseModalClick = () => {},
) => {
if (!collective || !callsToAction) {
return null;
}
Expand Down Expand Up @@ -366,7 +375,14 @@ const getMainAction = (collective, callsToAction, LoggedInUser) => {
} else if (callsToAction.includes('hasSubmitExpense')) {
return {
type: NAVBAR_ACTION_TYPE.SUBMIT_EXPENSE,
component: (
component: isNewExpenseFlowEnabled ? (
<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`}>
<ActionButton tabIndex="-1">
<Receipt size="1em" />
Expand Down Expand Up @@ -459,8 +475,14 @@ const CollectiveNavbar = ({
skip: !collective?.slug || !LoggedInUser,
});

const [isSubmitExpenseModalOpen, setIsSubmitExpenseModalOpen] = React.useState(false);

const loading = isLoading || dataLoading;

const isNewExpenseFlowEnabled =
LoggedInUser?.hasPreviewFeatureEnabled(PREVIEW_FEATURE_KEYS.NEW_EXPENSE_FLOW) &&
!isSupportedExpenseType(collective, ExpenseType.GRANT);

const isAllowedAddFunds = Boolean(data?.account?.permissions?.addFunds?.allowed);
const sections = React.useMemo(() => {
return sectionsFromParent || getFilteredSectionsForCollective(collective, isAdmin, isHostAdmin);
Expand All @@ -478,9 +500,14 @@ const CollectiveNavbar = ({
...callsToAction,
};
const actionsArray = Object.keys(pickBy(callsToAction, Boolean));
const mainAction = getMainAction(collective, actionsArray, LoggedInUser);
const mainAction = getMainAction(collective, actionsArray, LoggedInUser, isNewExpenseFlowEnabled, () =>
setIsSubmitExpenseModalOpen(true),
);
const secondAction =
actionsArray.length === 2 && getMainAction(collective, without(actionsArray, mainAction?.type), LoggedInUser);
actionsArray.length === 2 &&
getMainAction(collective, without(actionsArray, mainAction?.type), LoggedInUser, isNewExpenseFlowEnabled, () =>
setIsSubmitExpenseModalOpen(true),
);
const navbarRef = useRef();
const mainContainerRef = useRef();

Expand Down Expand Up @@ -620,6 +647,7 @@ const CollectiveNavbar = ({
)}
{!loading && (
<CollectiveNavbarActionsMenu
onOpenSubmitExpenseModalClick={() => setIsSubmitExpenseModalOpen(true)}
collective={collective}
callsToAction={callsToAction}
hiddenActionForNonMobile={mainAction?.type}
Expand All @@ -643,6 +671,14 @@ const CollectiveNavbar = ({
)}
</NavbarContentContainer>
</NavBarContainer>
{isSubmitExpenseModalOpen && (
<SubmitExpenseFlow
onClose={() => {
setIsSubmitExpenseModalOpen(false);
}}
submitExpenseTo={collective?.slug}
/>
)}
</Fragment>
);
};
Expand Down
23 changes: 23 additions & 0 deletions components/crowdfunding-redesign/AccountsList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';

import { AccountsSublist } from './AccountsSublist';

export default function AccountsList({ data, queryFilter, metric }) {
const currency = data?.account?.[metric.id]?.current?.currency;

const meta = {
queryFilter,
currency: currency,
isAmount: !!metric.amount,
metric,
};

return (
<div className="space-y-8">
<AccountsSublist label="Main account" type="COLLECTIVE" data={data} metric={metric} meta={meta} />
<AccountsSublist label="Projects" type="PROJECT" data={data} metric={metric} meta={meta} />

<AccountsSublist label="Events" type="EVENT" data={data} metric={metric} meta={meta} />
</div>
);
}
Loading

0 comments on commit d680677

Please sign in to comment.