From 7c21deab2f82e76a7eb6a621ea322059b0614af5 Mon Sep 17 00:00:00 2001 From: Benjamin Piouffle Date: Thu, 30 Jan 2025 10:42:09 +0100 Subject: [PATCH] styles(GiftCards): modernize admin UI (#10944) --- components/CreateGiftCardsForm.js | 18 +- components/CreateGiftCardsSuccess.js | 29 +- components/GiftCard.tsx | 2 +- components/GiftCardDetails.js | 72 ++-- components/dashboard/DashboardSection.tsx | 8 +- components/edit-collective/Form.js | 5 +- .../edit-collective/sections/GiftCards.js | 267 ------------ .../edit-collective/sections/GiftCards.tsx | 404 ++++++++++++++++++ .../sections/ManagePaymentMethods.tsx | 2 + components/gift-cards/CollectiveCard.tsx | 2 +- components/ui/Pagination.tsx | 2 +- graphql.config.js | 2 +- lang/ca.json | 2 +- lang/cs.json | 2 +- lang/de.json | 2 +- lang/en.json | 2 +- lang/es.json | 2 +- lang/fr.json | 2 +- lang/he.json | 2 +- lang/it.json | 2 +- lang/ja.json | 2 +- lang/ko.json | 2 +- lang/nl.json | 2 +- lang/pl.json | 2 +- lang/pt-BR.json | 2 +- lang/pt.json | 2 +- lang/ru.json | 2 +- lang/sk-SK.json | 2 +- lang/sv-SE.json | 2 +- lang/uk.json | 2 +- lang/zh.json | 2 +- lib/LoggedInUser.ts | 2 +- lib/custom_typings/GraphQLV1.ts | 33 ++ lib/custom_typings/GraphQLV1Collective.ts | 14 - .../integration/09-giftcards-admin.test.js | 2 +- test/cypress/support/commands.js | 3 +- 36 files changed, 525 insertions(+), 380 deletions(-) delete mode 100644 components/edit-collective/sections/GiftCards.js create mode 100644 components/edit-collective/sections/GiftCards.tsx create mode 100644 lib/custom_typings/GraphQLV1.ts delete mode 100644 lib/custom_typings/GraphQLV1Collective.ts diff --git a/components/CreateGiftCardsForm.js b/components/CreateGiftCardsForm.js index aa592a999af..0ff8f68f64e 100644 --- a/components/CreateGiftCardsForm.js +++ b/components/CreateGiftCardsForm.js @@ -14,6 +14,7 @@ import { isPrepaid } from '../lib/constants/payment-methods'; import { gqlV1 } from '../lib/graphql/helpers'; import { compose, reportValidityHTML5 } from '../lib/utils'; +import { Button } from './ui/Button'; import CollectivePicker from './CollectivePicker'; import Container from './Container'; import CreateGiftCardsSuccess from './CreateGiftCardsSuccess'; @@ -23,7 +24,6 @@ import Link from './Link'; import Loading from './Loading'; import MessageBox from './MessageBox'; import PaymentMethodSelect from './PaymentMethodSelect'; -import StyledButton from './StyledButton'; import StyledCheckbox from './StyledCheckbox'; import StyledInput from './StyledInput'; import StyledInputAmount from './StyledInputAmount'; @@ -311,17 +311,9 @@ class CreateGiftCardsForm extends Component { const count = this.getGiftCardsCount(); const enable = this.isSubmitEnabled(); return ( - + ); } @@ -329,12 +321,12 @@ class CreateGiftCardsForm extends Component { return ( - + ); diff --git a/components/CreateGiftCardsSuccess.js b/components/CreateGiftCardsSuccess.js index efed638efab..e32104b1d67 100644 --- a/components/CreateGiftCardsSuccess.js +++ b/components/CreateGiftCardsSuccess.js @@ -9,9 +9,9 @@ import styled from 'styled-components'; import { giftCardsDownloadUrl } from '../lib/url-helpers'; import { getWebsiteUrl } from '../lib/utils'; +import { Button } from './ui/Button'; import FileDownloader from './FileDownloader'; import { Box, Flex } from './Grid'; -import StyledButton from './StyledButton'; import StyledInput from './StyledInput'; import { P } from './Text'; @@ -83,17 +83,10 @@ export default class CreateGiftCardsSuccess extends React.Component { - - -   + {this.props.cards.length < 300 && ( {({ loading, downloadFile }) => ( - - -   + - + )} )} diff --git a/components/GiftCard.tsx b/components/GiftCard.tsx index 02f0390c90c..a22755ccc92 100644 --- a/components/GiftCard.tsx +++ b/components/GiftCard.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { Clock } from 'lucide-react'; import { FormattedDate, FormattedMessage } from 'react-intl'; -import type { GraphQLV1Collective } from '../lib/custom_typings/GraphQLV1Collective'; +import type { GraphQLV1Collective } from '../lib/custom_typings/GraphQLV1'; import type { Currency as GraphQLCurrency } from '../lib/graphql/types/v2/schema'; import CollectiveCard from './gift-cards/CollectiveCard'; diff --git a/components/GiftCardDetails.js b/components/GiftCardDetails.js index 847b52f2a3c..b8ade55e4a9 100644 --- a/components/GiftCardDetails.js +++ b/components/GiftCardDetails.js @@ -9,11 +9,12 @@ import styled, { withTheme } from 'styled-components'; import { formatCurrency } from '../lib/currency-utils'; import GiftCard from './icons/GiftCard'; +import { Button } from './ui/Button'; +import { Collapsible, CollapsibleContent, CollapsibleTrigger } from './ui/Collapsible'; import Avatar from './Avatar'; import Container from './Container'; import { Box, Flex } from './Grid'; import Link from './Link'; -import StyledButton from './StyledButton'; import { Span } from './Text'; const DetailsColumnHeader = styled.span` @@ -184,43 +185,40 @@ class GiftCardDetails extends React.Component { )} {/* Infos + details column */} - - - {this.renderValue()}{' '} - - - - - - {isExpired && ( - - | - - - )} - | - this.toggleExpended()} - px={1} - > - {this.state.expended ? ( - - ) : ( - + + + + {this.renderValue()}{' '} + + + + + + {isExpired && ( + + | + + )} - - - - {this.state.expended && this.renderDetails()} - + | + + + + + + {this.renderDetails()} + + ); } diff --git a/components/dashboard/DashboardSection.tsx b/components/dashboard/DashboardSection.tsx index 1b431f40294..9ba75b7b607 100644 --- a/components/dashboard/DashboardSection.tsx +++ b/components/dashboard/DashboardSection.tsx @@ -163,7 +163,9 @@ const DashboardSection = ({ account, isLoading, section, subpath }) => { return (
- {SECTION_LABELS[section] && } + {SECTION_LABELS[section] && section !== ALL_SECTIONS.GIFT_CARDS && ( + + )}
@@ -187,7 +189,9 @@ const DashboardSection = ({ account, isLoading, section, subpath }) => { //
- {SECTION_LABELS[section] && } + {SECTION_LABELS[section] && section !== ALL_SECTIONS.GIFT_CARDS && ( + + )}
diff --git a/components/edit-collective/Form.js b/components/edit-collective/Form.js index 64b133af00e..f68090aeed9 100644 --- a/components/edit-collective/Form.js +++ b/components/edit-collective/Form.js @@ -26,6 +26,7 @@ import InputField from '../InputField'; import Link from '../Link'; import StyledButton from '../StyledButton'; import StyledLink from '../StyledLink'; +import { Button } from '../ui/Button'; // Actions import Archive from './actions/Archive'; @@ -445,10 +446,10 @@ class EditCollectiveForm extends React.Component { flexWrap="wrap" > - + !value || !picked.includes(key)); - } - - renderFilters(onlyConfirmed) { - let selected = 'all'; - if (onlyConfirmed) { - selected = 'redeemed'; - } - if (onlyConfirmed === false) { - selected = 'pending'; - } - - const query = this.getQueryParams(['filter', 'batch']); - return ( - - {({ item, isSelected }) => ( - -

- {item === 'all' && } - {item === 'redeemed' && } - {item === 'pending' && } -

- - )} -
- ); - } - - renderNoGiftCardMessage(onlyConfirmed) { - if (onlyConfirmed === undefined) { - return ( - - - - ); - } else if (onlyConfirmed) { - return ; - } else { - return ; - } - } - - /** Get batch options for select. First option is always "No batch" */ - getBatchesOptions = memoizeOne((batches, selected, intl) => { - if (!batches || batches.length < 2) { - return [[], null]; - } else { - const options = [ - { label: intl.formatMessage(messages.allBatches), value: undefined }, - ...batches.map(batch => ({ - label: `${batch.name || intl.formatMessage(messages.notBatched)} (${batch.count})`, - value: batch.name || NOT_BATCHED_KEY, - })), - ]; - - return [options, options.find(option => option.value === selected)]; - } - }); - - render() { - const { data, collectiveSlug, intl } = this.props; - const queryResult = get(data, 'Collective.createdGiftCards', {}); - const onlyConfirmed = get(data, 'variables.isConfirmed'); - const batches = get(data, 'Collective.giftCardsBatches'); - const { offset, limit, total, paymentMethods = [] } = queryResult; - const lastGiftCard = last(paymentMethods); - const [batchesOptions, selectedOption] = this.getBatchesOptions(batches, get(data, 'variables.batch'), intl); - - return ( - - - - {this.renderFilters(onlyConfirmed)} - - - - - {' '} - - - - - - {batchesOptions.length > 1 && ( - - - this.props.router.push({ - pathname: `/dashboard/${collectiveSlug}/gift-cards`, - query: this.getQueryParams(['filter', 'batch'], { batch: value }), - }) - } - defaultValue={selectedOption} - /> - - )} - - {data.loading ? ( - - ) : ( -
- {paymentMethods.length === 0 && ( - - {this.renderNoGiftCardMessage(onlyConfirmed)} - - )} - {paymentMethods.map(v => ( -
- - {v !== lastGiftCard &&
} -
- ))} - {total > limit && ( - - - - )} -
- )} -
- ); - } -} - -const GIFT_CARDS_PER_PAGE = 15; - -const getIsConfirmedFromFilter = filter => { - if (filter === undefined || filter === 'all') { - return undefined; - } - return filter === 'redeemed'; -}; - -/** A query to get the gift cards created by a collective. Must be authenticated. */ -const giftCardsQuery = gqlV1/* GraphQL */ ` - query EditCollectiveGiftCards($collectiveId: Int, $isConfirmed: Boolean, $limit: Int, $offset: Int, $batch: String) { - Collective(id: $collectiveId) { - id - giftCardsBatches { - id - name - count - } - createdGiftCards(isConfirmed: $isConfirmed, limit: $limit, offset: $offset, batch: $batch) { - offset - limit - total - paymentMethods { - id - uuid - currency - name - service - type - batch - data - initialBalance - monthlyLimitPerMember - balance - expiryDate - isConfirmed - createdAt - description - collective { - id - slug - imageUrl - type - name - } - } - } - } - } -`; - -const getGiftCardsVariablesFromProps = ({ collectiveId, router, limit }) => ({ - collectiveId, - isConfirmed: getIsConfirmedFromFilter(router.query.filter), - batch: router.query.batch === NOT_BATCHED_KEY ? null : router.query.batch, - offset: Number(router.query.offset) || 0, - limit: limit || GIFT_CARDS_PER_PAGE, -}); - -const addGiftCardsData = graphql(giftCardsQuery, { - options: props => ({ - variables: getGiftCardsVariablesFromProps(props), - fetchPolicy: 'network-only', - }), -}); - -export default withRouter(injectIntl(addGiftCardsData(GiftCards))); diff --git a/components/edit-collective/sections/GiftCards.tsx b/components/edit-collective/sections/GiftCards.tsx new file mode 100644 index 00000000000..03c5ca6a2b7 --- /dev/null +++ b/components/edit-collective/sections/GiftCards.tsx @@ -0,0 +1,404 @@ +import React, { useCallback } from 'react'; +import { useQuery } from '@apollo/client'; +import { Add } from '@styled-icons/material/Add'; +import { get, last, omitBy } from 'lodash'; +import memoizeOne from 'memoize-one'; +import { useRouter } from 'next/router'; +import type { IntlShape } from 'react-intl'; +import { FormattedMessage, useIntl } from 'react-intl'; + +import { gqlV1 } from '../../../lib/graphql/helpers'; +import type { GraphQLV1PaymentMethod } from '@/lib/custom_typings/GraphQLV1'; + +import { getI18nLink } from '@/components/I18nFormatters'; + +import { ALL_SECTIONS, SECTION_LABELS } from '../../dashboard/constants'; +import DashboardHeader from '../../dashboard/DashboardHeader'; +import GiftCardDetails from '../../GiftCardDetails'; +import Link from '../../Link'; +import Loading from '../../Loading'; +import Tabs from '../../Tabs'; +import { Button } from '../../ui/Button'; +import { + Pagination, + PaginationButton, + PaginationContent, + PaginationEllipsis, + PaginationItem, + PaginationNext, + PaginationPrevious, +} from '../../ui/Pagination'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../ui/Select'; + +interface GiftCardBatch { + id: string; + name: string; + count: number; +} + +interface GiftCardsQueryData { + offset: number; + limit: number; + total: number; + paymentMethods: GraphQLV1PaymentMethod[]; +} + +interface QueryResult { + Collective: { + id: number; + giftCardsBatches: GiftCardBatch[]; + createdGiftCards: GiftCardsQueryData; + }; +} + +interface BatchOption { + label: string; + value: string; +} + +interface FilterTab { + id: string; + label: React.ReactNode; +} + +const messages = { + notBatched: { + id: 'giftCards.notBatched', + defaultMessage: 'Not batched', + }, + allBatches: { + id: 'giftCards.batches.all', + defaultMessage: 'All batches', + }, +}; + +const NOT_BATCHED_KEY = '__not-batched__'; +const GIFT_CARDS_PER_PAGE = 15; + +const getIsConfirmedFromFilter = (filter: string | undefined): boolean | undefined => { + if (filter === undefined || filter === 'all') { + return undefined; + } + return filter === 'redeemed'; +}; + +const giftCardsQuery = gqlV1/* GraphQL */ ` + query EditCollectiveGiftCards($collectiveId: Int, $isConfirmed: Boolean, $limit: Int, $offset: Int, $batch: String) { + Collective(id: $collectiveId) { + id + giftCardsBatches { + id + name + count + } + createdGiftCards(isConfirmed: $isConfirmed, limit: $limit, offset: $offset, batch: $batch) { + offset + limit + total + paymentMethods { + id + uuid + currency + name + service + type + batch + data + initialBalance + monthlyLimitPerMember + balance + expiryDate + isConfirmed + createdAt + description + collective { + id + slug + imageUrl + type + name + } + } + } + } + } +`; + +interface GiftCardsPaginationProps { + currentOffset: number; + total: number; + limit: number; + collectiveSlug: string; + getQueryParams: (picked: string[], newParams: Record) => Record; + router: ReturnType; +} + +const GiftCardsPagination: React.FC = ({ + currentOffset, + total, + limit, + collectiveSlug, + getQueryParams, + router, +}) => { + const currentPage = Math.floor(currentOffset / limit) + 1; + const totalPages = Math.ceil(total / limit); + const maxVisiblePages = 5; + + const getPageNumbers = () => { + const pages: number[] = []; + let startPage = Math.max(1, currentPage - Math.floor(maxVisiblePages / 2)); + const endPage = Math.min(totalPages, startPage + maxVisiblePages - 1); + + if (endPage - startPage + 1 < maxVisiblePages) { + startPage = Math.max(1, endPage - maxVisiblePages + 1); + } + + for (let i = startPage; i <= endPage; i++) { + pages.push(i); + } + + return pages; + }; + + const goToPage = (page: number) => { + const newOffset = (page - 1) * limit; + router.push({ + pathname: `/dashboard/${collectiveSlug}/gift-cards`, + query: getQueryParams(['filter', 'batch', 'offset'], { offset: newOffset }), + }); + }; + + const pages = getPageNumbers(); + return ( + + + {currentPage > 1 && ( + + goToPage(currentPage - 1)} /> + + )} + {pages[0] > 1 && ( + + + goToPage(1)}>1 + + {pages[0] > 2 && ( + + + + )} + + )} + {pages.map(page => ( + + goToPage(page)}> + {page} + + + ))} + {pages[pages.length - 1] < totalPages && ( + + {pages[pages.length - 1] < totalPages - 1 && ( + + + + )} + + goToPage(totalPages)}>{totalPages} + + + )} + + currentPage < totalPages && goToPage(currentPage + 1)} + disabled={currentPage === totalPages} + /> + + + + ); +}; + +interface GiftCardsProps { + collectiveId: number; + collectiveSlug: string; + limit?: number; +} + +const filterTabs: FilterTab[] = [ + { id: 'all', label: }, + { id: 'redeemed', label: }, + { id: 'pending', label: }, +] as const; + +const GiftCards: React.FC = ({ collectiveId, collectiveSlug, limit }) => { + const intl = useIntl(); + const router = useRouter(); + const { filter, batch, offset } = router.query; + + const getQueryParams = useCallback( + (picked: string[], newParams: Record) => { + return omitBy({ ...router.query, ...newParams }, (value, key) => !value || !picked.includes(key)); + }, + [router.query], + ); + + const { data, loading } = useQuery(giftCardsQuery, { + variables: { + collectiveId, + isConfirmed: getIsConfirmedFromFilter(filter as string | undefined), + batch: batch === NOT_BATCHED_KEY ? null : batch, + offset: Number(offset) || 0, + limit: limit || GIFT_CARDS_PER_PAGE, + }, + fetchPolicy: 'network-only', + }); + + const getBatchesOptions = memoizeOne( + ( + batches: GiftCardBatch[] | undefined, + selectedBatch: string | undefined, + intl: IntlShape, + ): [BatchOption[], BatchOption | null] => { + if (!batches || batches.length < 2) { + return [[], null]; + } else { + const options: BatchOption[] = [ + { label: intl.formatMessage(messages.allBatches), value: 'all' }, + ...batches.map(batch => ({ + label: `${batch.name || intl.formatMessage(messages.notBatched)} (${batch.count})`, + value: batch.name || NOT_BATCHED_KEY, + })), + ]; + + return [options, options.find(option => option.value === selectedBatch) || null]; + } + }, + ); + + const renderNoGiftCardMessage = (onlyConfirmed: boolean | undefined) => { + if (onlyConfirmed === undefined) { + return ( + + + + ); + } else if (onlyConfirmed) { + return ; + } else { + return ; + } + }; + + const queryResult = get(data, 'Collective.createdGiftCards', {}) as GiftCardsQueryData; + const onlyConfirmed = get(data, 'variables.isConfirmed') as boolean | undefined; + const batches = get(data, 'Collective.giftCardsBatches') as GiftCardBatch[]; + const { limit: resultLimit, total, paymentMethods = [] } = queryResult; + const lastGiftCard = last(paymentMethods); + const [batchesOptions] = getBatchesOptions(batches, get(data, 'variables.batch'), intl); + + const handleTabChange = useCallback( + (value: string) => { + router.push({ + pathname: `/dashboard/${collectiveSlug}/gift-cards`, + query: getQueryParams(['filter', 'batch'], { filter: value }), + }); + }, + [collectiveSlug, getQueryParams, router], + ); + + return ( +
+
+ Learn more.', + id: '35Jfcr', + }, + { + LearnMoreLink: getI18nLink({ + href: 'https://docs.opencollective.com/help/financial-contributors/organizations/gift-cards', + openInNewTab: true, + }), + }, + )} + /> + + + +
+
+
+ +
+ {batchesOptions.length > 1 && ( +
+ +
+ )} +
+ {loading ? ( + + ) : ( +
+ {paymentMethods.length === 0 ? ( +
{renderNoGiftCardMessage(onlyConfirmed)}
+ ) : ( + + {paymentMethods.map(v => ( +
+ + {v !== lastGiftCard &&
} +
+ ))} + {total > resultLimit && ( +
+ +
+ )} +
+ )} +
+ )} +
+ ); +}; + +export default GiftCards; diff --git a/components/edit-collective/sections/ManagePaymentMethods.tsx b/components/edit-collective/sections/ManagePaymentMethods.tsx index f2797667e61..4f147d7f33f 100644 --- a/components/edit-collective/sections/ManagePaymentMethods.tsx +++ b/components/edit-collective/sections/ManagePaymentMethods.tsx @@ -472,6 +472,8 @@ function AddCreditCardButton(props: AddCreditCardButtonProps) { onClick={submitNewCard} disabled={isSubmitting} loading={isSubmitting} + data-cy="save-credit-card-button" + data-loading={isSubmitting} >
diff --git a/components/gift-cards/CollectiveCard.tsx b/components/gift-cards/CollectiveCard.tsx index 779478c8f2d..6a86c85d362 100644 --- a/components/gift-cards/CollectiveCard.tsx +++ b/components/gift-cards/CollectiveCard.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { has } from 'lodash'; import type { ReactNode } from 'react'; -import type { GraphQLV1Collective } from '../../lib/custom_typings/GraphQLV1Collective'; +import type { GraphQLV1Collective } from '../../lib/custom_typings/GraphQLV1'; import Avatar from '../Avatar'; import Container from '../Container'; diff --git a/components/ui/Pagination.tsx b/components/ui/Pagination.tsx index 52b6d39159c..fe1ccc44ae9 100644 --- a/components/ui/Pagination.tsx +++ b/components/ui/Pagination.tsx @@ -25,7 +25,7 @@ const PaginationContent = React.forwardRef>(({ className, ...props }, ref) => ( -
  • +
  • )); PaginationItem.displayName = 'PaginationItem'; diff --git a/graphql.config.js b/graphql.config.js index 6230b7a9efd..4005573a260 100644 --- a/graphql.config.js +++ b/graphql.config.js @@ -34,7 +34,7 @@ module.exports = { 'components/edit-collective/actions/Archive.js', 'components/edit-collective/actions/Delete.js', 'components/edit-collective/sections/FiscalHosting.js', - 'components/edit-collective/sections/GiftCards.js', + 'components/edit-collective/sections/GiftCards.ts', 'components/edit-collective/sections/PaymentReceipts.js', 'components/edit-collective/sections/Webhooks.js', 'components/tier-page/graphql/queries.js', diff --git a/lang/ca.json b/lang/ca.json index 47a9c651cf0..4a90a22c4fa 100644 --- a/lang/ca.json +++ b/lang/ca.json @@ -126,6 +126,7 @@ "3135/i": "Expense Currency", "322m9e": "El missatge ha de tenir almenys 10 caràcters", "34Up+l": "Mostra'n més", + "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "38dzz9": "Expense Category", "38fPNE": "Transaction Import Settings", "3A7J9A": "We could not find a host that matches all your criteria.", @@ -1829,7 +1830,6 @@ "giftcard.user.text": "Contribute on {WebsiteName} with this Gift Card, courtesy of {emitter}.", "giftCards.balance": "Balance: {balance}", "giftCards.batch": "Batch name", - "giftCards.batches.all": "All batches", "giftCards.claimedBy": "claimed by {user}", "giftCards.create": "Create gift cards", "giftCards.create.customMessage": "Custom message", diff --git a/lang/cs.json b/lang/cs.json index 3e62e8a5057..a1259da2663 100644 --- a/lang/cs.json +++ b/lang/cs.json @@ -126,6 +126,7 @@ "3135/i": "Platební měna", "322m9e": "Zpráva musí být alespoň 10 znaku dlouhá", "34Up+l": "Zobrazit více", + "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "38dzz9": "Kategorie výdajů", "38fPNE": "Transaction Import Settings", "3A7J9A": "Nemohli jsme najít hostitele, který splňuje všechna vaše kritéria.", @@ -1829,7 +1830,6 @@ "giftcard.user.text": "Contribute on {WebsiteName} with this Gift Card, courtesy of {emitter}.", "giftCards.balance": "Balance: {balance}", "giftCards.batch": "Batch name", - "giftCards.batches.all": "All batches", "giftCards.claimedBy": "claimed by {user}", "giftCards.create": "Create gift cards", "giftCards.create.customMessage": "Vlastní zpráva", diff --git a/lang/de.json b/lang/de.json index 854feee9b0e..79b498fe7be 100644 --- a/lang/de.json +++ b/lang/de.json @@ -126,6 +126,7 @@ "3135/i": "Ausgabenwährung", "322m9e": "Nachricht muss mindestens 10 Zeichen lang sein", "34Up+l": "Mehr anzeigen", + "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "38dzz9": "Ausgabenkategorie", "38fPNE": "Transaktions-Import-Einstellungen", "3A7J9A": "Wir konnten keinen Host finden, der Ihren Kriterien entspricht.", @@ -1829,7 +1830,6 @@ "giftcard.user.text": "Spende auf {WebsiteName} mit diesem Geschenkgutschein, dank {emitter}.", "giftCards.balance": "Guthaben: {balance}", "giftCards.batch": "Batch-Name", - "giftCards.batches.all": "Alle Batches", "giftCards.claimedBy": "beansprucht von {user}", "giftCards.create": "Geschenkkarte erstellen", "giftCards.create.customMessage": "Eigene Nachricht", diff --git a/lang/en.json b/lang/en.json index 294a4e423ea..cf31fadbf06 100644 --- a/lang/en.json +++ b/lang/en.json @@ -126,6 +126,7 @@ "3135/i": "Expense Currency", "322m9e": "Message needs to be at least 10 characters long", "34Up+l": "View more", + "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "38dzz9": "Expense Category", "38fPNE": "Transaction Import Settings", "3A7J9A": "We could not find a host that matches all your criteria.", @@ -1829,7 +1830,6 @@ "giftcard.user.text": "Contribute on {WebsiteName} with this Gift Card, courtesy of {emitter}.", "giftCards.balance": "Balance: {balance}", "giftCards.batch": "Batch name", - "giftCards.batches.all": "All batches", "giftCards.claimedBy": "claimed by {user}", "giftCards.create": "Create gift cards", "giftCards.create.customMessage": "Custom message", diff --git a/lang/es.json b/lang/es.json index 68ba83d10a2..9e046afc9b1 100644 --- a/lang/es.json +++ b/lang/es.json @@ -126,6 +126,7 @@ "3135/i": "Moneda de gasto", "322m9e": "El mensaje debe tener al menos 10 caracteres", "34Up+l": "Ver más", + "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "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.", @@ -1829,7 +1830,6 @@ "giftcard.user.text": "Colabora en {WebsiteName} con esta Tarjeta de Regalo, cortesía de {emitter}.", "giftCards.balance": "Saldo: {balance}", "giftCards.batch": "Nombre del lote", - "giftCards.batches.all": "Todos los lotes", "giftCards.claimedBy": "reclamado por {user}", "giftCards.create": "Crear tarjetas de regalo", "giftCards.create.customMessage": "Mensaje personalizado", diff --git a/lang/fr.json b/lang/fr.json index b890d9cc48d..d333aaf3a34 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -126,6 +126,7 @@ "3135/i": "Devise de dépense", "322m9e": "Le message doit comporter au moins 10 caractères", "34Up+l": "Voir plus", + "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "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.", @@ -1829,7 +1830,6 @@ "giftcard.user.text": "Soutenez {WebsiteName} avec cette carte cadeau, offerte par {emitter}.", "giftCards.balance": "Solde : {balance}", "giftCards.batch": "Nom du lot", - "giftCards.batches.all": "Tous les lots", "giftCards.claimedBy": "réclamée par {user}", "giftCards.create": "Créer des cartes-cadeaux", "giftCards.create.customMessage": "Message personnalisé", diff --git a/lang/he.json b/lang/he.json index a1d1358186e..aaa884c5515 100644 --- a/lang/he.json +++ b/lang/he.json @@ -126,6 +126,7 @@ "3135/i": "מטבע הוצעה", "322m9e": "הסיסמה חייבת להיות באורך של 10 תווים לפחות", "34Up+l": "הצג עוד", + "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "38dzz9": "קטגורית הוצאה", "38fPNE": "הגדרות ייבוא ​​עסקאות", "3A7J9A": "לא הצלחנו למצוא מארח שתואם את כל הקריטריונים שלך.", @@ -1829,7 +1830,6 @@ "giftcard.user.text": "Contribute on {WebsiteName} with this Gift Card, courtesy of {emitter}.", "giftCards.balance": "מאזן: {balance}", "giftCards.batch": "שם פעולה מרוכזת", - "giftCards.batches.all": "כל הפעולות המרוכזות", "giftCards.claimedBy": "נדרש על-ידי {user}", "giftCards.create": "יצירת כרטיס מתנה", "giftCards.create.customMessage": "הודעה מותאמת אישית", diff --git a/lang/it.json b/lang/it.json index da2e1c700b5..c33f64dad4a 100644 --- a/lang/it.json +++ b/lang/it.json @@ -126,6 +126,7 @@ "3135/i": "Valuta spese", "322m9e": "Il messaggio deve essere lungo almeno 10 caratteri", "34Up+l": "Mostra di più", + "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "38dzz9": "Categoria di spesa", "38fPNE": "Transaction Import Settings", "3A7J9A": "Non siamo riusciti a trovare un Ospite Fiscale compatibile con i tuoi criteri.", @@ -1829,7 +1830,6 @@ "giftcard.user.text": "Contribute on {WebsiteName} with this Gift Card, courtesy of {emitter}.", "giftCards.balance": "Balance: {balance}", "giftCards.batch": "Batch name", - "giftCards.batches.all": "All batches", "giftCards.claimedBy": "claimed by {user}", "giftCards.create": "Create gift cards", "giftCards.create.customMessage": "Custom message", diff --git a/lang/ja.json b/lang/ja.json index cc3cc214b70..0eb05dc453f 100644 --- a/lang/ja.json +++ b/lang/ja.json @@ -126,6 +126,7 @@ "3135/i": "Expense Currency", "322m9e": "メッセージは10文字以上にする必要があります。", "34Up+l": "View more", + "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "38dzz9": "Expense Category", "38fPNE": "Transaction Import Settings", "3A7J9A": "We could not find a host that matches all your criteria.", @@ -1829,7 +1830,6 @@ "giftcard.user.text": "Contribute on {WebsiteName} with this Gift Card, courtesy of {emitter}.", "giftCards.balance": "Balance: {balance}", "giftCards.batch": "Batch name", - "giftCards.batches.all": "All batches", "giftCards.claimedBy": "claimed by {user}", "giftCards.create": "ギフトカードを作成", "giftCards.create.customMessage": "Custom message", diff --git a/lang/ko.json b/lang/ko.json index 51aff3a433c..6fddc5d7bf0 100644 --- a/lang/ko.json +++ b/lang/ko.json @@ -126,6 +126,7 @@ "3135/i": "Expense Currency", "322m9e": "메시지는 10자 이상이어야 해요", "34Up+l": "더보기", + "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "38dzz9": "Expense Category", "38fPNE": "Transaction Import Settings", "3A7J9A": "해당 키워드로 찾을 수 있는 호스트가 없어요.", @@ -1829,7 +1830,6 @@ "giftcard.user.text": "Contribute on {WebsiteName} with this Gift Card, courtesy of {emitter}.", "giftCards.balance": "Balance: {balance}", "giftCards.batch": "Batch name", - "giftCards.batches.all": "All batches", "giftCards.claimedBy": "claimed by {user}", "giftCards.create": "Create gift cards", "giftCards.create.customMessage": "Custom message", diff --git a/lang/nl.json b/lang/nl.json index db4e91794bc..63a11ae9b4f 100644 --- a/lang/nl.json +++ b/lang/nl.json @@ -126,6 +126,7 @@ "3135/i": "Expense Currency", "322m9e": "Bericht moet ten minste 10 tekens lang zijn", "34Up+l": "Bekijk meer", + "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "38dzz9": "Expense Category", "38fPNE": "Transaction Import Settings", "3A7J9A": "We konden geen host vinden voor al je criteria.", @@ -1829,7 +1830,6 @@ "giftcard.user.text": "Contribute on {WebsiteName} with this Gift Card, courtesy of {emitter}.", "giftCards.balance": "Saldo: {balance}", "giftCards.batch": "Batch name", - "giftCards.batches.all": "All batches", "giftCards.claimedBy": "geclaimd door {user}", "giftCards.create": "Maak cadeaubonnen aan", "giftCards.create.customMessage": "Aangepast bericht", diff --git a/lang/pl.json b/lang/pl.json index f0c6359eea6..961a9289ad6 100644 --- a/lang/pl.json +++ b/lang/pl.json @@ -126,6 +126,7 @@ "3135/i": "Waluta wydatków", "322m9e": "Wiadomość musi mieć co najmniej 10 znaków", "34Up+l": "Pokaż więcej", + "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "38dzz9": "Kategoria wydatku", "38fPNE": "Transaction Import Settings", "3A7J9A": "Nie mogliśmy znaleźć gospodarza, który spełnia wszystkie Twoje kryteria.", @@ -1829,7 +1830,6 @@ "giftcard.user.text": "Wspomóż {WebsiteName} za pomocą tej karty podarunkowej, dzięki uprzejmości {emitter}.", "giftCards.balance": "Saldo: {balance}", "giftCards.batch": "Nazwa serii", - "giftCards.batches.all": "Wszystkie partie", "giftCards.claimedBy": "zgłoszone przez {user}", "giftCards.create": "Utwórz karty podarunkowe", "giftCards.create.customMessage": "Wiadomość niestandardowa", diff --git a/lang/pt-BR.json b/lang/pt-BR.json index ea52378ac05..92b40ddbf98 100644 --- a/lang/pt-BR.json +++ b/lang/pt-BR.json @@ -126,6 +126,7 @@ "3135/i": "Moeda de Despesa", "322m9e": "A mensagem precisa ter pelo menos 10 caracteres", "34Up+l": "Ver mais", + "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "38dzz9": "Categoria de Despesa", "38fPNE": "Transaction Import Settings", "3A7J9A": "Não conseguimos encontrar um Administrador Fiscal com os critérios informados.", @@ -1829,7 +1830,6 @@ "giftcard.user.text": "Contribua em {WebsiteName} com este Gift Card, cortesia de {emitter}.", "giftCards.balance": "Saldo: {balance}", "giftCards.batch": "Nome do lote", - "giftCards.batches.all": "Todos lotes", "giftCards.claimedBy": "reivindicado por {user}", "giftCards.create": "Criar vales-presentes", "giftCards.create.customMessage": "Mensagem personalizada", diff --git a/lang/pt.json b/lang/pt.json index 512fd8e4152..9200cb0df96 100644 --- a/lang/pt.json +++ b/lang/pt.json @@ -126,6 +126,7 @@ "3135/i": "Expense Currency", "322m9e": "A mensagem precisa no mínimo de 10 caracteres", "34Up+l": "View more", + "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "38dzz9": "Expense Category", "38fPNE": "Transaction Import Settings", "3A7J9A": "We could not find a host that matches all your criteria.", @@ -1829,7 +1830,6 @@ "giftcard.user.text": "Contribute on {WebsiteName} with this Gift Card, courtesy of {emitter}.", "giftCards.balance": "Balance: {balance}", "giftCards.batch": "Batch name", - "giftCards.batches.all": "All batches", "giftCards.claimedBy": "claimed by {user}", "giftCards.create": "Create gift cards", "giftCards.create.customMessage": "Custom message", diff --git a/lang/ru.json b/lang/ru.json index 11efb124e92..cf2648687a0 100644 --- a/lang/ru.json +++ b/lang/ru.json @@ -126,6 +126,7 @@ "3135/i": "Валюта Расходов", "322m9e": "Длина сообщения должна быть не менее 10 символов", "34Up+l": "Показать еще", + "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "38dzz9": "Категория Расходов", "38fPNE": "Transaction Import Settings", "3A7J9A": "Мы не смогли найти поставщика, который соответствует всем вашим критериям.", @@ -1829,7 +1830,6 @@ "giftcard.user.text": "Contribute on {WebsiteName} with this Gift Card, courtesy of {emitter}.", "giftCards.balance": "Balance: {balance}", "giftCards.batch": "Batch name", - "giftCards.batches.all": "All batches", "giftCards.claimedBy": "claimed by {user}", "giftCards.create": "Создать подарочные карты", "giftCards.create.customMessage": "Custom message", diff --git a/lang/sk-SK.json b/lang/sk-SK.json index 04fc7e2861e..8a2d5cc38fa 100644 --- a/lang/sk-SK.json +++ b/lang/sk-SK.json @@ -126,6 +126,7 @@ "3135/i": "Mena výdavku", "322m9e": "Správa potrebuje mať aspoň 10 znakov", "34Up+l": "Zobraziť viac", + "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "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á.", @@ -1829,7 +1830,6 @@ "giftcard.user.text": "Contribute on {WebsiteName} with this Gift Card, courtesy of {emitter}.", "giftCards.balance": "Zostatok {balance}", "giftCards.batch": "Názov dávky", - "giftCards.batches.all": "Všetky dávky", "giftCards.claimedBy": "nárokuje si {user}", "giftCards.create": "Vytvoriť darčekové karty", "giftCards.create.customMessage": "Prispôsobená správa", diff --git a/lang/sv-SE.json b/lang/sv-SE.json index f9398d024af..fe6aa008cf4 100644 --- a/lang/sv-SE.json +++ b/lang/sv-SE.json @@ -126,6 +126,7 @@ "3135/i": "Expense Currency", "322m9e": "Meddelandet måste vara minst 10 tecken långt", "34Up+l": "Visa mer", + "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "38dzz9": "Expense Category", "38fPNE": "Transaction Import Settings", "3A7J9A": "We could not find a host that matches all your criteria.", @@ -1829,7 +1830,6 @@ "giftcard.user.text": "Contribute on {WebsiteName} with this Gift Card, courtesy of {emitter}.", "giftCards.balance": "Saldo: {balance}", "giftCards.batch": "Batch-namn", - "giftCards.batches.all": "Alla batcher", "giftCards.claimedBy": "inlöst av {user}", "giftCards.create": "Skapa presentkort", "giftCards.create.customMessage": "Anpassat meddelande", diff --git a/lang/uk.json b/lang/uk.json index 59d1c43fe7c..d3beea51b51 100644 --- a/lang/uk.json +++ b/lang/uk.json @@ -126,6 +126,7 @@ "3135/i": "Витрати валюти", "322m9e": "Повідомлення повинно складатися принаймні з 10 символів", "34Up+l": "Показати більше", + "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "38dzz9": "Категорія витрат", "38fPNE": "Transaction Import Settings", "3A7J9A": "Ми не змогли знайти агент, який відповідає усім вашим критеріям.", @@ -1829,7 +1830,6 @@ "giftcard.user.text": "Contribute on {WebsiteName} with this Gift Card, courtesy of {emitter}.", "giftCards.balance": "Баланс: {balance}", "giftCards.batch": "Назва пакета", - "giftCards.batches.all": "Усі пакети", "giftCards.claimedBy": "claimed by {user}", "giftCards.create": "Створити подарункові картки", "giftCards.create.customMessage": "Власне повідомлення", diff --git a/lang/zh.json b/lang/zh.json index b91c8253b8a..664f6a394b5 100644 --- a/lang/zh.json +++ b/lang/zh.json @@ -126,6 +126,7 @@ "3135/i": "支出货币种类", "322m9e": "消息长度至少需要 10 个字符", "34Up+l": "查看更多", + "35Jfcr": "Gift cards empower your employees or community members to support the open source projects they love. Learn more.", "38dzz9": "支出类别", "38fPNE": "交易导入设置", "3A7J9A": "我们无法找到符合你所有标准的发起者。", @@ -1829,7 +1830,6 @@ "giftcard.user.text": "Contribute on {WebsiteName} with this Gift Card, courtesy of {emitter}.", "giftCards.balance": "余额:{balance}", "giftCards.batch": "批次名", - "giftCards.batches.all": "所有批次", "giftCards.claimedBy": "{user} 已认领", "giftCards.create": "创建礼品卡", "giftCards.create.customMessage": "自定义消息", diff --git a/lib/LoggedInUser.ts b/lib/LoggedInUser.ts index f2f905148a5..42cc2b0ae98 100644 --- a/lib/LoggedInUser.ts +++ b/lib/LoggedInUser.ts @@ -2,7 +2,7 @@ import { get, uniqBy } from 'lodash'; import { CollectiveType } from './constants/collectives'; import type { ReverseCompatibleMemberRole } from './constants/roles'; -import type { GraphQLV1Collective } from './custom_typings/GraphQLV1Collective'; +import type { GraphQLV1Collective } from './custom_typings/GraphQLV1'; import { type CommentFieldsFragment } from './graphql/types/v2/graphql'; import { type Account, type AccountWithParent, MemberRole, type Update } from './graphql/types/v2/schema'; import type { PREVIEW_FEATURE_KEYS, PreviewFeature } from './preview-features'; diff --git a/lib/custom_typings/GraphQLV1.ts b/lib/custom_typings/GraphQLV1.ts new file mode 100644 index 00000000000..94bfa9cbcbc --- /dev/null +++ b/lib/custom_typings/GraphQLV1.ts @@ -0,0 +1,33 @@ +import type { CollectiveType } from '../constants/collectives'; + +export type GraphQLV1Collective = { + id: number; + slug: string; + name: string; + legalName: string; + imageUrl: string; + type: keyof typeof CollectiveType; + isArchived?: boolean; + parentCollective?: GraphQLV1Collective; + settings?: Record; + isHost?: boolean; +}; + +export interface GraphQLV1PaymentMethod { + id: string; + uuid: string; + currency: string; + name: string; + service: string; + type: string; + batch: string; + data: any; + initialBalance: number; + monthlyLimitPerMember: number; + balance: number; + expiryDate: string; + isConfirmed: boolean; + createdAt: string; + description: string; + collective: GraphQLV1Collective; +} diff --git a/lib/custom_typings/GraphQLV1Collective.ts b/lib/custom_typings/GraphQLV1Collective.ts deleted file mode 100644 index af5603f6c51..00000000000 --- a/lib/custom_typings/GraphQLV1Collective.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { CollectiveType } from '../../lib/constants/collectives'; - -export type GraphQLV1Collective = { - id: number; - slug: string; - name: string; - legalName: string; - imageUrl: string; - type: keyof typeof CollectiveType; - isArchived?: boolean; - parentCollective?: GraphQLV1Collective; - settings?: Record; - isHost?: boolean; -}; diff --git a/test/cypress/integration/09-giftcards-admin.test.js b/test/cypress/integration/09-giftcards-admin.test.js index b07703bdd2f..20c0abcd189 100644 --- a/test/cypress/integration/09-giftcards-admin.test.js +++ b/test/cypress/integration/09-giftcards-admin.test.js @@ -50,7 +50,7 @@ describe('Gift cards admin', () => { }); // Should have pagination - cy.get('.vc-pagination').contains(`of ${numberOfPages}`); + cy.get('[data-cy="gift-cards-pagination"] button').should('have.length', numberOfPages + 1); // 2 pages + next button }); it('send gift cards by emails', () => { diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js index 6bdfd2c51b8..91eaf1e58af 100644 --- a/test/cypress/support/commands.js +++ b/test/cypress/support/commands.js @@ -284,7 +284,8 @@ Cypress.Commands.add('addCreditCardToCollective', ({ collectiveSlug }) => { fillStripeInput(); cy.wait(1000); cy.contains('button[type="submit"]', 'Save').click(); - cy.wait(2000); + cy.get('[data-cy="save-credit-card-button"][data-loading="true"]').should('exist'); + cy.get('[data-cy="save-credit-card-button"][data-loading="true"]').should('not.exist', { timeout: 30_000 }); }); /**