diff --git a/components/transactions/Transactions.js b/components/transactions/Transactions.js deleted file mode 100644 index 12cbec7d1b7..00000000000 --- a/components/transactions/Transactions.js +++ /dev/null @@ -1,278 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { useQuery } from '@apollo/react-hooks'; -import { mapValues } from 'lodash'; -import { useRouter } from 'next/router'; -import { FormattedMessage } from 'react-intl'; - -import { getErrorFromGraphqlException } from '../../lib/errors'; -import { API_V2_CONTEXT, gqlV2 } from '../../lib/graphql/helpers'; -import { Router } from '../../server/pages'; - -import { parseAmountRange } from '../expenses/filters/ExpensesAmountFilter'; -import { getDateRangeFromPeriod } from '../expenses/filters/ExpensesDateFilter'; -import { Box, Flex } from '../Grid'; -import Link from '../Link'; -import MessageBox from '../MessageBox'; -import Pagination from '../Pagination'; -import SearchBar from '../SearchBar'; -import StyledHr from '../StyledHr'; -import { H1 } from '../Text'; - -import TransactionsDownloadCSV from './TransactionsDownloadCSV'; -import TransactionsDownloadInvoices from './TransactionsDownloadInvoices'; -import TransactionsFilters from './TransactionsFilters'; -import TransactionsList from './TransactionsList'; - -const transactionsQuery = gqlV2/* GraphQL */ ` - query Transactions( - $slug: String! - $limit: Int! - $offset: Int! - $type: TransactionType - $minAmount: Int - $maxAmount: Int - $dateFrom: ISODateTime - $searchTerm: String - ) { - transactions( - account: { slug: $slug } - limit: $limit - offset: $offset - type: $type - minAmount: $minAmount - maxAmount: $maxAmount - dateFrom: $dateFrom - searchTerm: $searchTerm - ) { - totalCount - offset - limit - nodes { - id - uuid - amount { - currency - valueInCents - } - netAmount { - currency - valueInCents - } - platformFee { - currency - valueInCents - } - paymentProcessorFee { - currency - valueInCents - } - hostFee { - currency - valueInCents - } - type - description - createdAt - isRefunded - toAccount { - id - name - slug - type - imageUrl - ... on Collective { - host { - name - slug - type - } - } - } - fromAccount { - id - name - slug - type - imageUrl - } - paymentMethod { - type - } - order { - id - status - } - expense { - id - status - tags - type - legacyId - comments { - totalCount - } - payoutMethod { - type - } - account { - slug - } - createdByAccount { - slug - } - } - } - } - } -`; - -const EXPENSES_PER_PAGE = 15; - -const getVariablesFromQuery = query => { - const amountRange = parseAmountRange(query.amount); - const [dateFrom] = getDateRangeFromPeriod(query.period); - return { - offset: parseInt(query.offset) || 0, - limit: parseInt(query.limit) || EXPENSES_PER_PAGE, - type: query.type, - status: query.status, - tags: query.tag ? [query.tag] : undefined, - minAmount: amountRange[0] && amountRange[0] * 100, - maxAmount: amountRange[1] && amountRange[1] * 100, - payoutMethodType: query.payout, - dateFrom, - searchTerm: query.searchTerm, - }; -}; - -const Transactions = ({ collective, LoggedInUser }) => { - const { query } = useRouter() || {}; - const { data, error, loading, variables } = useQuery(transactionsQuery, { - variables: { slug: collective.slug, ...getVariablesFromQuery(query) }, - context: API_V2_CONTEXT, - }); - const hasFilters = React.useMemo( - () => - Object.entries(query).some(([key, value]) => { - return !['view', 'offset', 'limit', 'slug'].includes(key) && value; - }), - [query], - ); - - const isHostAdmin = LoggedInUser?.isHostAdmin(collective); - const isCollectiveAdmin = LoggedInUser?.canEditCollective(collective); - const canDownloadInvoices = - isHostAdmin || (isCollectiveAdmin && (collective.type === 'ORGANIZATION' || collective.type === 'USER')); - - return ( - - -

- -

- - Router.pushRoute('transactions', { ...query, searchTerm, offset: null })} - /> - -
- - - - Router.pushRoute('transactions', { - ...query, - ...queryParams, - offset: null, - }) - } - /> - - {canDownloadInvoices && ( - - - - )} - - - - {error ? ( - - {getErrorFromGraphqlException(error).message} - - ) : !loading && !data.transactions?.nodes.length ? ( - - {hasFilters ? ( - null), - collectiveSlug: collective.slug, - view: 'transactions', - }} - > - {text} - - ); - }, - }} - /> - ) : ( - - )} - - ) : ( - - - - - - - )} -
- ); -}; - -Transactions.propTypes = { - /** Collective */ - collective: PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired, - slug: PropTypes.string.isRequired, - type: PropTypes.string.isRequired, - currency: PropTypes.string.isRequired, - platformFeePercent: PropTypes.number, - }).isRequired, - /** @ignore from injectIntl */ - intl: PropTypes.object, - LoggedInUser: PropTypes.object, -}; - -export default Transactions; diff --git a/pages/transactions.js b/pages/transactions.js index bddb00990f2..b350a172e51 100644 --- a/pages/transactions.js +++ b/pages/transactions.js @@ -1,23 +1,143 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { get } from 'lodash'; +import { graphql } from '@apollo/react-hoc'; +import { get, mapValues } from 'lodash'; +import { FormattedMessage } from 'react-intl'; import styled from 'styled-components'; import { CollectiveType } from '../lib/constants/collectives'; +import { getErrorFromGraphqlException } from '../lib/errors'; +import { API_V2_CONTEXT, gqlV2 } from '../lib/graphql/helpers'; import { addCollectiveCoverData } from '../lib/graphql/queries'; +import { Router } from '../server/pages'; import Body from '../components/Body'; import { Sections } from '../components/collective-page/_constants'; import CollectiveNavbar from '../components/CollectiveNavbar'; import Container from '../components/Container'; import ErrorPage from '../components/ErrorPage'; +import { parseAmountRange } from '../components/expenses/filters/ExpensesAmountFilter'; +import { getDateRangeFromPeriod } from '../components/expenses/filters/ExpensesDateFilter'; import Footer from '../components/Footer'; +import { Box, Flex } from '../components/Grid'; import Header from '../components/Header'; +import Link from '../components/Link'; import Loading from '../components/Loading'; +import MessageBox from '../components/MessageBox'; import Page from '../components/Page'; -import Transactions from '../components/transactions/Transactions'; +import Pagination from '../components/Pagination'; +import SearchBar from '../components/SearchBar'; +import StyledHr from '../components/StyledHr'; +import { H1 } from '../components/Text'; +import TransactionsDownloadCSV from '../components/transactions/TransactionsDownloadCSV'; +import TransactionsDownloadInvoices from '../components/transactions/TransactionsDownloadInvoices'; +import TransactionsFilters from '../components/transactions/TransactionsFilters'; +import TransactionsList from '../components/transactions/TransactionsList'; import { withUser } from '../components/UserProvider'; +const transactionsQuery = gqlV2/* GraphQL */ ` + query Transactions( + $slug: String! + $limit: Int! + $offset: Int! + $type: TransactionType + $minAmount: Int + $maxAmount: Int + $dateFrom: ISODateTime + $searchTerm: String + ) { + transactions( + account: { slug: $slug } + limit: $limit + offset: $offset + type: $type + minAmount: $minAmount + maxAmount: $maxAmount + dateFrom: $dateFrom + searchTerm: $searchTerm + ) { + totalCount + offset + limit + nodes { + id + uuid + amount { + currency + valueInCents + } + netAmount { + currency + valueInCents + } + platformFee { + currency + valueInCents + } + paymentProcessorFee { + currency + valueInCents + } + hostFee { + currency + valueInCents + } + type + description + createdAt + isRefunded + toAccount { + id + name + slug + type + imageUrl + ... on Collective { + host { + name + slug + type + } + } + } + fromAccount { + id + name + slug + type + imageUrl + } + paymentMethod { + type + } + order { + id + status + } + expense { + id + status + tags + type + legacyId + comments { + totalCount + } + payoutMethod { + type + } + account { + slug + } + createdByAccount { + slug + } + } + } + } + } +`; + const TransactionPageWrapper = styled.div` display: flex; flex-direction: column; @@ -27,15 +147,36 @@ const TransactionPageWrapper = styled.div` } `; +const EXPENSES_PER_PAGE = 15; + +const getVariablesFromQuery = query => { + const amountRange = parseAmountRange(query.amount); + const [dateFrom] = getDateRangeFromPeriod(query.period); + return { + offset: parseInt(query.offset) || 0, + limit: parseInt(query.limit) || EXPENSES_PER_PAGE, + type: query.type, + status: query.status, + tags: query.tag ? [query.tag] : undefined, + minAmount: amountRange[0] && amountRange[0] * 100, + maxAmount: amountRange[1] && amountRange[1] * 100, + payoutMethodType: query.payout, + dateFrom, + searchTerm: query.searchTerm, + }; +}; + class TransactionsPage extends React.Component { - static getInitialProps({ query: { collectiveSlug } }) { - return { slug: collectiveSlug }; + static async getInitialProps({ query: { collectiveSlug, ...query } }) { + return { slug: collectiveSlug, query }; } static propTypes = { slug: PropTypes.string, // from getInitialProps, for addCollectiveCoverData data: PropTypes.object.isRequired, // from withData + transactionsData: PropTypes.object, LoggedInUser: PropTypes.object, + query: PropTypes.object, }; constructor(props) { @@ -61,17 +202,24 @@ class TransactionsPage extends React.Component { } render() { - const { LoggedInUser } = this.props; + const { LoggedInUser, query, transactionsData, data, slug } = this.props; const collective = get(this.props, 'data.Collective') || this.state.Collective; + const { transactions, error, loading, variables } = transactionsData; + const hasFilters = Object.entries(query).some(([key, value]) => { + return !['view', 'offset', 'limit', 'slug'].includes(key) && value; + }); + const canDownloadInvoices = + (LoggedInUser?.isHostAdmin(collective) || LoggedInUser?.canEditCollective(collective)) && + (collective.type === 'ORGANIZATION' || collective.type === 'USER'); - if (!collective && this.props.data.loading) { + if (!collective && data.loading) { return ( ); } else if (!collective) { - return ; + return ; } return ( @@ -90,14 +238,98 @@ class TransactionsPage extends React.Component { }} /> - - + + +

+ +

+ + Router.pushRoute('transactions', { ...query, searchTerm, offset: null })} + /> + +
+ + + + Router.pushRoute('transactions', { + ...query, + ...queryParams, + collectiveSlug: slug, + offset: null, + }) + } + /> + + {canDownloadInvoices && ( + + + + )} + + + + {error ? ( + + {getErrorFromGraphqlException(error).message} + + ) : !loading && !transactions?.nodes?.length ? ( + + {hasFilters ? ( + null), + collectiveSlug: collective.slug, + view: 'transactions', + }} + > + {text} + + ); + }, + }} + /> + ) : ( + + )} + + ) : ( + + + + + + + )} +