Skip to content

Commit

Permalink
Merge branch 'main' into add-pending-filter-to-unrepied-host-applicat…
Browse files Browse the repository at this point in the history
…ions-tab
  • Loading branch information
hdiniz authored Sep 30, 2024
2 parents c8d5328 + edba6f1 commit ae5774b
Show file tree
Hide file tree
Showing 43 changed files with 376 additions and 220 deletions.
38 changes: 0 additions & 38 deletions components/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import type { BorderProps } from 'styled-system';
import { border, color, layout, space } from 'styled-system';

import { CollectiveType, defaultImage } from '../lib/constants/collectives';
import type { Account } from '../lib/graphql/types/v2/graphql';
import { getAvatarBorderRadius, getCollectiveImage } from '../lib/image-utils';

import type { FlexProps } from './Grid';
Expand Down Expand Up @@ -177,42 +176,5 @@ export const GuestAvatar = avatarProps => {
return <StyledAvatar {...avatarProps} type={CollectiveType.USER} src={defaultImage.GUEST} />;
};

export const StackedAvatars = ({
accounts = [],
imageSize,
maxDisplayedAvatars = 3,
}: {
accounts: Partial<Account>[];
imageSize: number;
maxDisplayedAvatars?: number;
}) => {
const width = `${imageSize}px`;
const marginLeft = `-${imageSize / 3}px`;
const displayed = accounts.length > maxDisplayedAvatars ? accounts.slice(0, maxDisplayedAvatars - 1) : accounts;
const left = accounts.length - displayed.length;
return (
<div className="flex items-center">
{displayed.map(account => (
<div key={account.id || account.slug} className="flex items-center first:!ml-0" style={{ marginLeft }}>
<Avatar
collective={account}
radius={imageSize}
displayTitle={true}
className="border border-solid border-white"
/>
</div>
))}
{left ? (
<div
className="flex items-center justify-center rounded-full bg-blue-50 text-[11px] font-semibold text-blue-400 first:ml-0"
style={{ width, height: width, marginLeft }}
>
+{left}
</div>
) : null}
</div>
);
};

/** @component */
export default Avatar;
32 changes: 14 additions & 18 deletions components/NewsAndUpdatesModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,18 @@ import StyledLink from './StyledLink';
import { P, Span } from './Text';

const newsAndUpdatesQuery = gql`
query ChangelogUpdates($limit: Int) {
account(slug: "opencollective") {
id
updates(
orderBy: { field: PUBLISHED_AT, direction: DESC }
onlyChangelogUpdates: true
onlyPublishedUpdates: true
limit: $limit
) {
nodes {
query ChangelogUpdates {
updates(orderBy: { field: PUBLISHED_AT, direction: DESC }, onlyChangelogUpdates: true, limit: 5) {
nodes {
id
slug
publishedAt
title
html
summary
account {
id
slug
publishedAt
title
html
summary
}
}
}
Expand All @@ -43,7 +39,7 @@ const renderStyledCarousel = (data, loading, error, onClose) => {
if (loading === false && data) {
return (
<StyledCarousel contentPosition="left">
{data.account.updates.nodes.map(update => (
{data.updates.nodes.map(update => (
<div key={update.id} className="px-3">
<span className="text-sm text-muted-foreground">
<FormattedDate value={update.publishedAt} day="numeric" month="long" year="numeric" />
Expand All @@ -65,7 +61,7 @@ const renderStyledCarousel = (data, loading, error, onClose) => {
<StyledLink
onClick={onClose}
as={Link}
href={`/opencollective/updates/${update.slug}`}
href={`/${update.account?.slug}/updates/${update.slug}`}
className="text-blue-800"
fontSize="14px"
display="flex"
Expand All @@ -90,7 +86,7 @@ const renderStyledCarousel = (data, loading, error, onClose) => {
<StyledLink
onClick={onClose}
as={Link}
href={`/opencollective/updates/${update.slug}`}
href={`/${update.account?.slug}/updates/${update.slug}`}
fontSize="14px"
display="flex"
className="text-blue-800"
Expand Down Expand Up @@ -142,7 +138,7 @@ const NewsAndUpdatesModal = ({ open, setOpen }) => {
</DialogHeader>
<Separator className="my-3" />
<div className="px-0 pb-6">
<Query query={newsAndUpdatesQuery} variables={{ limit: 5 }} context={API_V2_CONTEXT}>
<Query query={newsAndUpdatesQuery} context={API_V2_CONTEXT}>
{({ data, loading, error }) => renderStyledCarousel(data, loading, error, onClose)}
</Query>
</div>
Expand Down
56 changes: 56 additions & 0 deletions components/StackedAvatars.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from 'react';

import type { Account } from '../lib/graphql/types/v2/graphql';

import Avatar from './Avatar';
import LinkCollective from './LinkCollective';

const StackedAvatars = ({
accounts = [],
imageSize,
maxDisplayedAvatars = 3,
withHoverCard,
}: {
accounts: Partial<Account>[];
imageSize: number;
maxDisplayedAvatars?: number;
withHoverCard?: boolean | { includeAdminMembership: boolean };
}) => {
const width = `${imageSize}px`;
const marginLeft = `-${imageSize / 3}px`;
const displayed = accounts.length > maxDisplayedAvatars ? accounts.slice(0, maxDisplayedAvatars - 1) : accounts;
const left = accounts.length - displayed.length;
return (
<div className="flex items-center">
{displayed.map(account => (
<div key={account.id || account.slug} className="flex items-center first:!ml-0" style={{ marginLeft }}>
<LinkCollective
collective={account}
withHoverCard={Boolean(withHoverCard)}
hoverCardProps={{
includeAdminMembership:
typeof withHoverCard === 'object' && withHoverCard.includeAdminMembership ? account.slug : false,
}}
>
<Avatar
collective={account}
radius={imageSize}
displayTitle={true}
className="border border-solid border-white"
/>
</LinkCollective>
</div>
))}
{left ? (
<div
className="flex items-center justify-center rounded-full bg-blue-50 text-[11px] font-semibold text-blue-400 first:ml-0"
style={{ width, height: width, marginLeft }}
>
+{left}
</div>
) : null}
</div>
);
};

export default StackedAvatars;
98 changes: 54 additions & 44 deletions components/budget/ExpenseBudgetItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import useLoggedInUser from '../../lib/hooks/useLoggedInUser';
import { PREVIEW_FEATURE_KEYS } from '../../lib/preview-features';
import { AmountPropTypeShape } from '../../lib/prop-types';
import { toPx } from '../../lib/theme/helpers';
import { getCollectivePageRoute, getDashboardRoute } from '../../lib/url-helpers';
import { getCollectivePageRoute } from '../../lib/url-helpers';
import { shouldDisplayExpenseCategoryPill } from '../expenses/lib/accounting-categories';

import { AccountHoverCard } from '../AccountHoverCard';
Expand All @@ -39,6 +39,7 @@ import CommentIcon from '../icons/CommentIcon';
import Link from '../Link';
import LinkCollective from '../LinkCollective';
import LoadingPlaceholder from '../LoadingPlaceholder';
import StackedAvatars from '../StackedAvatars';
import StyledButton from '../StyledButton';
import StyledLink from '../StyledLink';
import Tags from '../Tags';
Expand All @@ -55,7 +56,6 @@ const DetailColumnHeader = styled.div`
letter-spacing: 0.6px;
text-transform: uppercase;
color: #c4c7cc;
margin-bottom: 2px;
`;

const ButtonsContainer = styled.div.attrs({ 'data-cy': 'expense-actions' })`
Expand Down Expand Up @@ -137,6 +137,7 @@ const ExpenseBudgetItem = ({
const isViewingExpenseInHostContext = isLoggedInUserExpenseHostAdmin && !isLoggedInUserExpenseAdmin;
const hasKeyboardShortcutsEnabled = LoggedInUser?.hasPreviewFeatureEnabled(PREVIEW_FEATURE_KEYS.KEYBOARD_SHORTCUTS);
const lastComment = expense?.lastComment?.nodes?.[0];
const approvedBy = expense?.approvedBy?.length > 0 ? expense.approvedBy : null;

return (
<ExpenseContainer
Expand Down Expand Up @@ -184,7 +185,6 @@ const ExpenseBudgetItem = ({
<FormattedMessage id="Expense.GoToPage" defaultMessage="Go to expense page" />
)}
</TooltipContent>

<TooltipTrigger asChild>
<span>
<StyledLink
Expand Down Expand Up @@ -397,15 +397,16 @@ const ExpenseBudgetItem = ({
)}
</Flex>
</Flex>
<Flex flexWrap="wrap" justifyContent="space-between" alignItems="center" mt={2}>
<Box mt={2}>
{/* <Flex flexWrap="wrap" justifyContent="space-between" alignItems="center" mt={2}> */}
<div className="mt-2 flex flex-col justify-between xl:flex-row">
<div className="w-full sm:w-auto">
{isAdminView || isSubmitterView ? (
<Flex>
<Box mr={[3, 4]}>
<div className="mx-4 grid w-full grid-cols-2 gap-x-6 gap-y-1 sm:mx-0 sm:grid-flow-col sm:gap-y-0">
<div>
<DetailColumnHeader>
<FormattedMessage id="expense.payoutMethod" defaultMessage="payout method" />
</DetailColumnHeader>
<Box mt="6px">
<div className="flex h-6 items-center">
<PayoutMethodTypeWithIcon
isLoading={isLoading}
type={expense.payoutMethod?.type}
Expand All @@ -414,24 +415,24 @@ const ExpenseBudgetItem = ({
fontWeight="normal"
color="black.700"
/>
</Box>
</Box>
</div>
</div>
{Boolean(expense.reference) && (
<Box mr={[3, 4]}>
<div>
<DetailColumnHeader>
<FormattedMessage id="Expense.Reference" defaultMessage="Reference" />
</DetailColumnHeader>
{isLoading ? (
<LoadingPlaceholder height={15} width={90} />
) : (
<div className="mt-[4px] text-[11px]">
<div className="text-[11px]">
<TruncatedTextWithTooltip value={expense.reference} length={10} truncatePosition="middle" />
</div>
)}
</Box>
</div>
)}
{nbAttachedFiles > 0 && (
<Box mr={[3, 4]}>
<div>
<DetailColumnHeader>
<FormattedMessage id="Expense.Attachments" defaultMessage="Attachments" />
</DetailColumnHeader>
Expand All @@ -447,6 +448,7 @@ const ExpenseBudgetItem = ({
px={2}
ml={-2}
isBorderless
textAlign="left"
>
<MaximizeIcon size={10} />
&nbsp;&nbsp;
Expand All @@ -457,34 +459,14 @@ const ExpenseBudgetItem = ({
/>
</StyledButton>
)}
</Box>
)}
{Boolean(expense.account?.hostAgreements?.totalCount) && (
<Box mr={[3, 4]}>
<DetailColumnHeader>
<FormattedMessage defaultMessage="Host Agreements" id="kq2gKV" />
</DetailColumnHeader>
<div className="mt-[7px] text-[11px]">
<StyledLink
as={Link}
color="black.700"
href={`${getDashboardRoute(host, 'host-agreements')}?account=${expense.account.slug}`}
>
<FormattedMessage
defaultMessage="{count, plural, one {# agreement} other {# agreements}}"
id="7FgO5b"
values={{ count: expense.account.hostAgreements.totalCount }}
/>
</StyledLink>
</div>
</Box>
</div>
)}
{lastComment && (
<Box mr={[3, 4]}>
<div>
<DetailColumnHeader>
<FormattedMessage defaultMessage="Last Comment" id="gSNApa" />
</DetailColumnHeader>
<div className="pt-[2px] text-[11px]">
<div className="text-[11px]">
<LinkCollective
collective={lastComment.fromAccount}
className="flex items-center gap-2 font-medium text-slate-700 hover:text-slate-700 hover:underline"
Expand All @@ -494,15 +476,34 @@ const ExpenseBudgetItem = ({
<Avatar collective={lastComment.fromAccount} radius={24} /> {lastComment.fromAccount.name}
</LinkCollective>
</div>
</Box>
</div>
)}
</Flex>
{approvedBy && expense.status === ExpenseStatus.APPROVED && !expense.onHold && (
<div>
<DetailColumnHeader>
<FormattedMessage defaultMessage="Approved By" id="JavAWD" />
</DetailColumnHeader>
<div className="text-[11px]">
<StackedAvatars
accounts={approvedBy}
imageSize={24}
withHoverCard={{ includeAdminMembership: true }}
/>
</div>
</div>
)}
</div>
) : (
<Tags expense={expense} canEdit={get(expense, 'permissions.canEditTags', false)} />
<div className="mt-2">
<Tags expense={expense} canEdit={get(expense, 'permissions.canEditTags', false)} />
</div>
)}
</Box>
</div>
{showProcessActions && expense?.permissions && !isExpensePaidOrRejected && (
<ButtonsContainer>
<div
data-cy="expense-actions"
className="mt-5 flex w-full flex-col items-stretch gap-2 sm:mt-2 sm:w-auto sm:flex-row sm:items-start sm:justify-end"
>
<ProcessExpenseButtons
host={host}
isViewingExpenseInHostContext={isViewingExpenseInHostContext}
Expand All @@ -513,9 +514,9 @@ const ExpenseBudgetItem = ({
onSuccess={onProcess}
enableKeyboardShortcuts={selected && hasKeyboardShortcutsEnabled}
/>
</ButtonsContainer>
</div>
)}
</Flex>
</div>
{showFilesViewerModal && (
<FilesViewerModal
files={files}
Expand Down Expand Up @@ -559,6 +560,7 @@ ExpenseBudgetItem.propTypes = {
amountInAccountCurrency: AmountPropTypeShape,
currency: PropTypes.string.isRequired,
permissions: PropTypes.object,
onHold: PropTypes.bool,
accountingCategory: PropTypes.object,
items: PropTypes.arrayOf(PropTypes.object),
requiredLegalDocuments: PropTypes.arrayOf(PropTypes.string),
Expand Down Expand Up @@ -600,6 +602,14 @@ ExpenseBudgetItem.propTypes = {
id: PropTypes.string.isRequired,
}),
}),
approvedBy: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
slug: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
}),
),
lastComment: PropTypes.shape({
nodes: PropTypes.arrayOf(
PropTypes.shape({
Expand Down
Loading

0 comments on commit ae5774b

Please sign in to comment.