diff --git a/components/CreateEventForm.js b/components/CreateEventForm.js index c2380a0963e..7ea4723ca92 100644 --- a/components/CreateEventForm.js +++ b/components/CreateEventForm.js @@ -148,10 +148,9 @@ class CreateEventForm extends React.Component { return
; } - const isNew = !(event && event.id); + const isNew = !event.id; const submitBtnLabel = loading ? 'loading' : isNew ? 'Create Event' : 'Save'; - - this.fields = [ + const fields = [ { name: 'name', maxLength: 255, @@ -192,9 +191,7 @@ class CreateEventForm extends React.Component { type: 'textarea', maxLength: 10000, }, - ]; - - this.fields = this.fields.map(field => { + ].map(field => { if (this.messages[`${field.name}.label`]) { field.label = intl.formatMessage(this.messages[`${field.name}.label`]); } @@ -208,7 +205,7 @@ class CreateEventForm extends React.Component {
- {this.fields.map(field => + {fields.map(field => field.name === 'timezone' ? ( { type="url" placeholder="https://www.youtube.com/watch?v=dQw4w9WgXcQ" value={url || ''} - onChange={e => onChange(e.target.value)} + onChange={e => onChange(e.target.value || null)} width={1} autoFocus /> diff --git a/components/dashboard/sections/AccountSettings.js b/components/dashboard/sections/AccountSettings.js index fa5d8ecf5d9..9f16bd62c3d 100644 --- a/components/dashboard/sections/AccountSettings.js +++ b/components/dashboard/sections/AccountSettings.js @@ -86,28 +86,6 @@ const AccountSettings = ({ account, section }) => { } const CollectiveInputType = pick(collective, collectiveFields); - if (isArray(collective.tiers)) { - CollectiveInputType.tiers = collective.tiers.map(tier => - pick(tier, [ - 'id', - 'type', - 'name', - 'description', - 'longDescription', - 'useStandalonePage', - 'amount', - 'amountType', - 'interval', - 'maxQuantity', - 'presets', - 'minimumAmount', - 'goal', - 'button', - 'invoiceTemplate', - 'singleTicket', - ]), - ); - } if (isArray(collective.socialLinks)) { CollectiveInputType.socialLinks = collective.socialLinks.map(sl => omit(sl, '__typename')); diff --git a/components/edit-collective/Form.js b/components/edit-collective/Form.js index e11e1b8a705..1bbf9e0855a 100644 --- a/components/edit-collective/Form.js +++ b/components/edit-collective/Form.js @@ -4,13 +4,12 @@ import { getApplicableTaxesForCountry, TaxType } from '@opencollective/taxes'; import { InfoCircle } from '@styled-icons/boxicons-regular/InfoCircle'; import { ArrowBack } from '@styled-icons/material/ArrowBack'; import dayjs from 'dayjs'; -import { cloneDeep, find, get, isNil, set } from 'lodash'; +import { cloneDeep, get, isNil, set } from 'lodash'; import { withRouter } from 'next/router'; import { defineMessages, FormattedMessage, injectIntl } from 'react-intl'; import { AccountTypesWithHost, CollectiveType, defaultBackgroundImage } from '../../lib/constants/collectives'; import { Currency } from '../../lib/constants/currency'; -import { TierTypes } from '../../lib/constants/tiers-types'; import { VAT_OPTIONS } from '../../lib/constants/vat'; import { convertDateFromApiUtc, convertDateToApiUtc } from '../../lib/date-utils'; import { isValidUrl } from '../../lib/utils'; @@ -282,15 +281,9 @@ class EditCollectiveForm extends React.Component { collective.slug = collective.slug ? collective.slug.replace(/.*\//, '') : ''; collective.tos = get(collective, 'settings.tos'); - // TODO Remove this once tier legacy is removed - const tiers = collective.tiers && collective.tiers.filter(tier => tier.type !== TierTypes.TICKET); - const tickets = collective.tiers && collective.tiers.filter(tier => tier.type === TierTypes.TICKET); - return { modified: false, collective, - tiers: tiers.length === 0 ? [] : tiers, - tickets: tickets.length === 0 ? [] : tickets, validStartDate: true, validEndDate: true, isValidSocialLinks: true, @@ -352,15 +345,6 @@ class EditCollectiveForm extends React.Component { async handleSubmit() { const collective = { ...this.state.collective }; - // Add Tiers and Tickets - collective.tiers = []; - if (find(this.state.tiers, 'name')) { - collective.tiers = [...this.state.tiers]; - } - if (find(this.state.tickets, 'name')) { - collective.tiers = [...collective.tiers, ...this.state.tickets]; - } - // Add a confirm if slug changed if (collective.slug !== this.props.collective.slug) { if ( diff --git a/components/tier-page/TierLongDescription.js b/components/tier-page/TierLongDescription.js index 3caa9ae5779..9d858e88336 100644 --- a/components/tier-page/TierLongDescription.js +++ b/components/tier-page/TierLongDescription.js @@ -12,9 +12,9 @@ import StyledButton from '../StyledButton'; * Displays the tier long description on the page, with an optional form to edit it * if user is allowed to do so. */ -const TierLongDescription = ({ tier, editMutation, canEdit }) => { +const TierLongDescription = ({ tier, editMutation, canEdit, ...inlineEditFieldProps }) => { return ( - + {({ isEditing, value, setValue, enableEditor, setUploading }) => { if (isEditing) { return ( diff --git a/components/tier-page/TierVideo.js b/components/tier-page/TierVideo.js index dc3143995a0..034d41d64d1 100644 --- a/components/tier-page/TierVideo.js +++ b/components/tier-page/TierVideo.js @@ -12,15 +12,15 @@ const VideoLinkerBox = dynamic(() => import(/* webpackChunkName: 'VideoLinkerBox * Displays the video on the page, with an optional form to edit it * if user is allowed to do so. */ -const TierVideo = ({ tier, editMutation, canEdit }) => { +const TierVideo = ({ tier, editMutation, canEdit, ...inlineEditFieldProps }) => { return ( {({ isEditing, value, setValue, enableEditor, disableEditor }) => { if (isEditing || (!value && canEdit)) { diff --git a/components/tier-page/graphql/queries.js b/components/tier-page/graphql/queries.js index 9f63f29df0b..194c2b01437 100644 --- a/components/tier-page/graphql/queries.js +++ b/components/tier-page/graphql/queries.js @@ -6,6 +6,7 @@ export const tierPageQuery = gqlV1/* GraphQL */ ` query TierPage($tierId: Int!) { Tier(id: $tierId) { id + idV2 name slug description diff --git a/components/tier-page/index.js b/components/tier-page/index.js index 3ad5700cabf..fe0eff1b896 100644 --- a/components/tier-page/index.js +++ b/components/tier-page/index.js @@ -1,5 +1,6 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; +import { gql } from '@apollo/client'; import { themeGet } from '@styled-system/theme-get'; import { withRouter } from 'next/router'; import { FormattedMessage } from 'react-intl'; @@ -7,7 +8,7 @@ import styled from 'styled-components'; // Open Collective Frontend imports import INTERVALS from '../../lib/constants/intervals'; -import { gqlV1 } from '../../lib/graphql/helpers'; +import { API_V2_CONTEXT } from '../../lib/graphql/helpers'; import { isTierExpired } from '../../lib/tier-utils'; import { getCollectivePageRoute } from '../../lib/url-helpers'; import { getWebsiteUrl } from '../../lib/utils'; @@ -80,8 +81,14 @@ const ProgressInfoContainer = styled.div` `; /** A mutation with all the info that user is allowed to edit on this page */ -const editTierMutation = gqlV1/* GraphQL */ ` - mutation UpdateTier($id: Int!, $name: String, $description: String, $longDescription: String, $videoUrl: String) { +const editTierMutation = gql` + mutation EditTierPage( + $id: String! + $name: NonEmptyString + $description: String + $longDescription: String + $videoUrl: URL + ) { editTier( tier: { id: $id, description: $description, name: $name, longDescription: $longDescription, videoUrl: $videoUrl } ) { @@ -115,6 +122,7 @@ class TierPage extends Component { /** The actual tier */ tier: PropTypes.shape({ id: PropTypes.number.isRequired, + idV2: PropTypes.string.isRequired, name: PropTypes.string.isRequired, type: PropTypes.string.isRequired, slug: PropTypes.string.isRequired, @@ -164,6 +172,26 @@ class TierPage extends Component { ); } + /** + * Helper to plug the `InlineEditField` with the GraphQL V2 API. Fields take a GraphQLV1 tier as input, + * use the GraphQLV2 API to update the tier, and update the cache for GraphQLV1 with the result. + */ + getGraphQLV2Bindings(graphqlV1FieldName, graphqlV2FieldName = graphqlV1FieldName) { + const graphQLV1Tier = this.props.tier; + return { + prepareVariables: (tier, newValue) => ({ id: tier.idV2, [graphqlV2FieldName]: newValue }), + mutationOptions: { + context: API_V2_CONTEXT, + update: (cache, { data: { editTier } }) => { + cache.modify({ + id: cache.identify(graphQLV1Tier), + fields: { [graphqlV1FieldName]: () => editTier[graphqlV2FieldName] }, + }); + }, + }, + }; + } + render() { const { collective, tier, contributors, contributorsStats, redirect, LoggedInUser } = this.props; const canEdit = LoggedInUser && LoggedInUser.isAdminOfCollective(collective); @@ -212,6 +240,7 @@ class TierPage extends Component { maxLength={255} placeholder={} required + {...this.getGraphQLV2Bindings('name')} />

} + {...this.getGraphQLV2Bindings('description')} />

- +
- +
@@ -374,7 +416,13 @@ class TierPage extends Component { {/** Video */} - + {/** Share buttons (desktop only) */} {shareBlock} diff --git a/lib/graphql/schema.graphql b/lib/graphql/schema.graphql index ffddc61f576..7fae39262a2 100644 --- a/lib/graphql/schema.graphql +++ b/lib/graphql/schema.graphql @@ -424,6 +424,7 @@ This represents an Tier """ type Tier { id: Int + idV2: String! slug: String type: String name: String diff --git a/lib/graphql/schemaV2.graphql b/lib/graphql/schemaV2.graphql index c4bd8bbfc4f..643fac04700 100644 --- a/lib/graphql/schemaV2.graphql +++ b/lib/graphql/schemaV2.graphql @@ -21016,6 +21016,8 @@ input TierUpdateInput { amount: AmountInput name: NonEmptyString description: String + longDescription: String + videoUrl: URL button: String goal: AmountInput type: TierType diff --git a/lib/graphql/types/v2/graphql.ts b/lib/graphql/types/v2/graphql.ts index f155e7ab049..28f14f52c65 100644 --- a/lib/graphql/types/v2/graphql.ts +++ b/lib/graphql/types/v2/graphql.ts @@ -10341,6 +10341,7 @@ export type TierUpdateInput = { /** The public id identifying the tier (ie: dgm9bnk8-0437xqry-ejpvzeol-jdayw5re) */ id: Scalars['String']['input']; invoiceTemplate?: InputMaybe; + longDescription?: InputMaybe; maxQuantity?: InputMaybe; minimumAmount?: InputMaybe; name?: InputMaybe; @@ -10348,6 +10349,7 @@ export type TierUpdateInput = { singleTicket?: InputMaybe; type?: InputMaybe; useStandalonePage?: InputMaybe; + videoUrl?: InputMaybe; }; export type TimeSeries = { diff --git a/lib/graphql/v1/mutations.js b/lib/graphql/v1/mutations.js index 2117997208a..8bf749c4258 100644 --- a/lib/graphql/v1/mutations.js +++ b/lib/graphql/v1/mutations.js @@ -146,9 +146,6 @@ export const addCreateCollectiveMutation = graphql(createCollectiveMutation, { 'isIncognito', 'settings', ]); - CollectiveInputType.tiers = (collective.tiers || []).map(tier => - pick(tier, ['type', 'name', 'description', 'amount', 'maxQuantity']), - ); CollectiveInputType.location = pick(collective.location, [ 'name', 'address', diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js index 0c884a19a5b..f024fe324ed 100644 --- a/test/cypress/support/commands.js +++ b/test/cypress/support/commands.js @@ -151,7 +151,7 @@ Cypress.Commands.add('createCollective', ({ type = 'ORGANIZATION', email = defau } } `, - variables: { collective: { location: {}, name: 'TestOrg', slug: '', tiers: [], type, ...params } }, + variables: { collective: { location: {}, name: 'TestOrg', slug: '', type, ...params } }, }).then(({ body }) => { return body.data.createCollective; });