diff --git a/api/src/pcapi/sandboxes/scripts/getters/pro.py b/api/src/pcapi/sandboxes/scripts/getters/pro.py index 8308803c389..13fc5ceeb68 100644 --- a/api/src/pcapi/sandboxes/scripts/getters/pro.py +++ b/api/src/pcapi/sandboxes/scripts/getters/pro.py @@ -90,7 +90,10 @@ def create_regular_pro_user_with_virtual_offer() -> dict: offerers_factories.VenueFactory(name="Mon Lieu", managingOfferer=offerer, isPermanent=True) virtual_venue = offerers_factories.VirtualVenueFactory(managingOfferer=offerer) offers_factories.OfferFactory( - name="Mon offre virtuelle", subcategoryId=subcategories.ABO_PLATEFORME_VIDEO.id, venue=virtual_venue + name="Mon offre virtuelle", + subcategoryId=subcategories.ABO_PLATEFORME_VIDEO.id, + venue=virtual_venue, + url="http://www.example.com", ) return {"user": get_pro_user_helper(pro_user)} diff --git a/pro/cypress/e2e/editDigitalIndividualOffer.cy.ts b/pro/cypress/e2e/editDigitalIndividualOffer.cy.ts index 174c76aa8d7..91d1faf4c61 100644 --- a/pro/cypress/e2e/editDigitalIndividualOffer.cy.ts +++ b/pro/cypress/e2e/editDigitalIndividualOffer.cy.ts @@ -1,6 +1,9 @@ import { addDays, format } from 'date-fns' -import { sessionLogInAndGoToPage, logInAndGoToPage } from '../support/helpers.ts' +import { + sessionLogInAndGoToPage, + logInAndGoToPage, +} from '../support/helpers.ts' describe('Edit digital individual offers', () => { describe('Display and url modification', () => { @@ -17,7 +20,11 @@ describe('Edit digital individual offers', () => { }) it('An edited offer is displayed with 4 tabs', function () { - sessionLogInAndGoToPage('Session edit digital individual offer', login1, '/offre/individuelle/1/recapitulatif/details') + sessionLogInAndGoToPage( + 'Session edit digital individual offer', + login1, + '/offre/individuelle/1/recapitulatif/details' + ) cy.contains('Récapitulatif') @@ -33,30 +40,40 @@ describe('Edit digital individual offers', () => { }) it('I should be able to modify the url of a digital offer', function () { - sessionLogInAndGoToPage('Session edit digital individual offer', login1, '/offres') + sessionLogInAndGoToPage( + 'Session edit digital individual offer', + login1, + '/offres' + ) cy.stepLog({ message: 'I open the first offer in the list' }) cy.findAllByTestId('offer-item-row') .first() .within(() => { - cy.findByRole('button', { name: 'Voir les actions'}).click() + cy.findByRole('button', { name: 'Voir les actions' }).click() }) cy.findByRole('menuitem', { name: 'Voir l’offre' }).click() cy.url().should('contain', '/recapitulatif') + cy.findByRole('link', { name: 'Modifier les détails de l’offre' }).click() + + cy.stepLog({ message: 'I update the url link' }) + const randomUrl = `http://myrandomurl.fr/` + cy.get('input#url').type('{selectall}{del}' + randomUrl) + cy.stepLog({ message: 'I display Informations pratiques tab' }) + cy.findByText('Enregistrer les modifications').click() + + cy.findByText('http://myrandomurl.fr/').should('exist') + cy.findByText('Informations pratiques').click() cy.url().should('contain', '/pratiques') cy.stepLog({ message: 'I edit the offer displayed' }) - cy.get('a[aria-label^="Modifier les détails de l’offre"]').click() + cy.findByRole('link', { name: 'Modifier les détails de l’offre' }).click() cy.url().should('contain', '/edition/pratiques') - cy.stepLog({ message: 'I update the url link' }) - const randomUrl = `http://myrandomurl.fr/` - cy.get('input#url').type('{selectall}{del}' + randomUrl) cy.findByText('Enregistrer les modifications').click() - cy.findByText('http://myrandomurl.fr/').should('exist') cy.findByText('Retour à la liste des offres').click() cy.url().should('contain', '/offres') cy.findAllByTestId('spinner', { timeout: 30 * 1000 }).should('not.exist') @@ -66,7 +83,7 @@ describe('Edit digital individual offers', () => { cy.findAllByTestId('offer-item-row') .first() .within(() => { - cy.findByRole('button', { name: 'Voir les actions'}).click() + cy.findByRole('button', { name: 'Voir les actions' }).click() }) cy.findByRole('menuitem', { name: 'Voir l’offre' }).click() cy.url().should('contain', '/recapitulatif') @@ -74,11 +91,6 @@ describe('Edit digital individual offers', () => { cy.stepLog({ message: 'I display Informations pratiques tab' }) cy.findByText('Informations pratiques').click() cy.url().should('contain', '/pratiques') - - cy.stepLog({ - message: 'the url updated is retrieved in the details of the offer', - }) - cy.contains('URL d’accès à l’offre : ' + randomUrl) }) }) diff --git a/pro/src/commons/utils/getOfferConditionalFields.ts b/pro/src/commons/utils/getOfferConditionalFields.ts index 51bc095cc38..273d73a8d98 100644 --- a/pro/src/commons/utils/getOfferConditionalFields.ts +++ b/pro/src/commons/utils/getOfferConditionalFields.ts @@ -4,14 +4,12 @@ interface GetOfferConditionalFieldsProps { offerSubCategory?: SubcategoryResponseModel | null isUserAdmin?: boolean | null receiveNotificationEmails?: boolean | null - isVenueVirtual?: boolean | null } export const getOfferConditionalFields = ({ offerSubCategory = null, isUserAdmin = null, receiveNotificationEmails = null, - isVenueVirtual = null, }: GetOfferConditionalFieldsProps): string[] => { const offerConditionalFields = [] @@ -39,10 +37,6 @@ export const getOfferConditionalFields = ({ offerConditionalFields.push('bookingEmail') } - if (isVenueVirtual) { - offerConditionalFields.push('url') - } - if (offerSubCategory?.canBeWithdrawable) { offerConditionalFields.push('withdrawalType') offerConditionalFields.push('withdrawalDelay') diff --git a/pro/src/components/IndividualOffer/DetailsSummary/DetailsSummary.tsx b/pro/src/components/IndividualOffer/DetailsSummary/DetailsSummary.tsx index 760045a82b1..07111e471b1 100644 --- a/pro/src/components/IndividualOffer/DetailsSummary/DetailsSummary.tsx +++ b/pro/src/components/IndividualOffer/DetailsSummary/DetailsSummary.tsx @@ -59,7 +59,14 @@ export function DetailsSummaryScreen({ offer }: DetailsSummaryScreenProps) { const aboutDescriptions: Description[] = [ { title: 'Titre de l’offre', text: offerData.name }, { title: 'Description', text: offerData.description }, - ] + ].concat( + offer.isDigital + ? { + title: 'URL d’accès à l’offre', + text: offer.url || ' - ', + } + : [] + ) const venueName = offerData.venuePublicName || offerData.venueName aboutDescriptions.unshift({ title: isOfferAddressEnabled ? 'Structure' : 'Lieu', diff --git a/pro/src/components/IndividualOffer/SummaryScreen/OfferSection/OfferSection.tsx b/pro/src/components/IndividualOffer/SummaryScreen/OfferSection/OfferSection.tsx index 1752cd580c8..74884d94830 100644 --- a/pro/src/components/IndividualOffer/SummaryScreen/OfferSection/OfferSection.tsx +++ b/pro/src/components/IndividualOffer/SummaryScreen/OfferSection/OfferSection.tsx @@ -71,7 +71,14 @@ export const OfferSection = ({ const aboutDescriptions: Description[] = [ { title: 'Titre de l’offre', text: offerData.name }, { title: 'Description', text: offerData.description || '-' }, - ] + ].concat( + offer.isDigital + ? { + title: 'URL d’accès à l’offre', + text: offer.url || ' - ', + } + : [] + ) const venueName = offerData.venuePublicName || offerData.venueName aboutDescriptions.unshift({ title: isOfferAddressEnabled ? 'Structure' : 'Lieu', @@ -165,14 +172,6 @@ export const OfferSection = ({ offerData.bookingContact || ' - ', }) } - if (conditionalFields.includes('url')) { - practicalInfoDescriptions.push({ - title: 'URL d’accès à l’offre', - text: - /* istanbul ignore next: DEBT, TO FIX */ - offerData.url || ' - ', - }) - } const artisticInformationsFields = [ 'speaker', @@ -229,7 +228,7 @@ export const OfferSection = ({ })} aria-label="Modifier les informations pratiques de l’offre" > - {!offerData.isVenueVirtual && isOfferAddressEnabled && ( + {!offer.isDigital && isOfferAddressEnabled && ( { venueName: 'Le nom du lieu 1', venuePublicName: 'Mon Lieu', venueDepartmentCode: '78', - isVenueVirtual: false, offererName: 'Le nom de la structure 1', bookingEmail: 'booking@email.com', bookingContact: 'alfonsoLeBg@exampple.com', diff --git a/pro/src/components/IndividualOffer/SummaryScreen/OfferSection/serializer.ts b/pro/src/components/IndividualOffer/SummaryScreen/OfferSection/serializer.ts index 54e065a6315..7cab23ad244 100644 --- a/pro/src/components/IndividualOffer/SummaryScreen/OfferSection/serializer.ts +++ b/pro/src/components/IndividualOffer/SummaryScreen/OfferSection/serializer.ts @@ -116,7 +116,6 @@ export const serializeOfferSectionData = ( venueName: offer.venue.name, venuePublicName: offer.venue.publicName, venueDepartmentCode: offer.venue.departementCode, - isVenueVirtual: offer.venue.isVirtual, offererName: offer.venue.managingOfferer.name, bookingEmail: offer.bookingEmail, bookingContact: offer.bookingContact, diff --git a/pro/src/components/IndividualOffer/SummaryScreen/SummaryScreen.tsx b/pro/src/components/IndividualOffer/SummaryScreen/SummaryScreen.tsx index 30195cf2487..f25e1aec316 100644 --- a/pro/src/components/IndividualOffer/SummaryScreen/SummaryScreen.tsx +++ b/pro/src/components/IndividualOffer/SummaryScreen/SummaryScreen.tsx @@ -134,7 +134,6 @@ export const SummaryScreen = () => { offerSubCategory, isUserAdmin: false, receiveNotificationEmails: true, - isVenueVirtual: offer.venue.isVirtual, }) const subCategoryConditionalFields = offerSubCategory ? offerSubCategory.conditionalFields diff --git a/pro/src/components/IndividualOffer/SummaryScreen/__specs__/SummaryScreen.spec.tsx b/pro/src/components/IndividualOffer/SummaryScreen/__specs__/SummaryScreen.spec.tsx index 2641d3d7b62..81a6d20d09e 100644 --- a/pro/src/components/IndividualOffer/SummaryScreen/__specs__/SummaryScreen.spec.tsx +++ b/pro/src/components/IndividualOffer/SummaryScreen/__specs__/SummaryScreen.spec.tsx @@ -133,6 +133,7 @@ describe('Summary', () => { description: 'ma description', subcategoryId: SubcategoryIdEnum.CONCERT, url: 'https://offer-url.example.com', + isDigital: true, withdrawalDetails: 'détails de retrait', bookingEmail: 'booking@example.com', venue: getOfferVenueFactory({ @@ -165,18 +166,12 @@ describe('Summary', () => { expect( screen.getByText('Notifications des réservations') ).toBeInTheDocument() - expect( - screen.getByText('URL d’accès à l’offre', { exact: false }) - ).toBeInTheDocument() expect(screen.getByText('Aperçu dans l’app')).toBeInTheDocument() expect(screen.getByText(categories[0].proLabel)).toBeInTheDocument() expect(screen.getByText(subCategories[0].proLabel)).toBeInTheDocument() expect(screen.getByText('ma venue (nom public)')).toBeInTheDocument() expect(screen.getByText('détails de retrait')).toBeInTheDocument() - expect( - screen.getByText('https://offer-url.example.com') - ).toBeInTheDocument() expect(screen.getByText('Non accessible')).toBeInTheDocument() expect(screen.getByText('booking@example.com')).toBeInTheDocument() expect(screen.getAllByText('mon offre')).toHaveLength(2) @@ -218,6 +213,7 @@ describe('Summary', () => { screen.queryByText('Visualiser dans l’app') ).not.toBeInTheDocument() }) + it('should render component with new sections', async () => { vi.spyOn(api, 'getOfferer').mockResolvedValue( defaultGetOffererResponseModel diff --git a/pro/src/components/IndividualOffer/UsefulInformationsSummaryScreen/UsefulInformationsSummary.tsx b/pro/src/components/IndividualOffer/UsefulInformationsSummaryScreen/UsefulInformationsSummary.tsx index 01b304fe0dd..818ee452ed0 100644 --- a/pro/src/components/IndividualOffer/UsefulInformationsSummaryScreen/UsefulInformationsSummary.tsx +++ b/pro/src/components/IndividualOffer/UsefulInformationsSummaryScreen/UsefulInformationsSummary.tsx @@ -52,12 +52,6 @@ export function UsefulInformationsSummaryScreen({ text: offer.bookingContact || ' - ', }) } - if (offer.venue.isVirtual) { - practicalInfoDescriptions.push({ - title: 'URL d’accès à l’offre', - text: offer.url || ' - ', - }) - } return ( @@ -70,7 +64,7 @@ export function UsefulInformationsSummaryScreen({ })} aria-label="Modifier les détails de l’offre" > - {!offer.venue.isVirtual && isOfferAddressEnabled && ( + {!offer.isDigital && isOfferAddressEnabled && ( { withdrawalDelay: 120, bookingContact: 'robert@exemple.com', venue: { ...getOfferVenueFactory(), isVirtual: true }, - url: 'https://www.example.com', }) renderUsefulInformationsSummaryScreen(offer) @@ -57,7 +56,6 @@ describe('UsefulInformationsSummaryScreen', () => { screen.getByText('2 minutes avant le début de l’évènement') ).toBeInTheDocument() expect(screen.getByText('robert@exemple.com')).toBeInTheDocument() - expect(screen.getByText('https://www.example.com')).toBeInTheDocument() }) it('should render summary with right field with OA FF', async () => { diff --git a/pro/src/components/OfferAppPreview/OfferAppPreview.tsx b/pro/src/components/OfferAppPreview/OfferAppPreview.tsx index c3104269002..a93ee7d88cf 100644 --- a/pro/src/components/OfferAppPreview/OfferAppPreview.tsx +++ b/pro/src/components/OfferAppPreview/OfferAppPreview.tsx @@ -58,7 +58,7 @@ export const OfferAppPreview = ({ )} - {!venue.isVirtual && ( + {!offer.isDigital && ( { }) }) -describe('buildVenueOptions', () => { - it('should build venues options', () => { - expect( - buildVenueOptions( - [venueListItemFactory({}), venueListItemFactory({})], - false, - false - ) - ).toStrictEqual([ - { - label: 'Sélectionner le partenaire', - value: '', - }, - { - label: 'Le nom du lieu 1', - value: '1', - }, - { - label: 'Le nom du lieu 2', - value: '2', - }, - ]) +describe('formatVenuesOptions', () => { + it('should format venues as options', () => { + const formattedVenuesOptions = formatVenuesOptions( + [ + venueListItemFactory({ isVirtual: false, id: 10 }), + venueListItemFactory({ isVirtual: false, id: 3 }), + ], + false + ) + expect(formattedVenuesOptions).toEqual( + expect.arrayContaining([expect.objectContaining({ value: '10' })]) + ) + + expect(formattedVenuesOptions).toEqual( + expect.arrayContaining([expect.objectContaining({ value: '3' })]) + ) }) -}) -describe('setDefaultInitialValues', () => { - it('should set default initial values', () => { - expect( - setDefaultInitialValues({ - filteredVenues: [venueListItemFactory({}), venueListItemFactory({})], - areSuggestedSubcategoriesUsed: false, - }) - ).toStrictEqual({ - author: '', - categoryId: '', - description: '', - durationMinutes: '', - ean: '', - gtl_id: '', - name: '', - performer: '', - showSubType: '', - showType: '', - speaker: '', - stageDirector: '', - subcategoryConditionalFields: [], - suggestedSubcategory: '', - subcategoryId: '', - venueId: '', - visa: '', - productId: '', - callId: '', - }) + it('should exclude digital venues if a physcal venue is available', () => { + const formattedVenuesOptions = formatVenuesOptions( + [ + venueListItemFactory({ isVirtual: true, id: 10 }), + venueListItemFactory({ isVirtual: false, id: 3 }), + ], + false + ) + expect(formattedVenuesOptions).not.toEqual( + expect.arrayContaining([expect.objectContaining({ value: '10' })]) + ) - expect( - setDefaultInitialValues({ - filteredVenues: [venueListItemFactory({ id: 666 })], - areSuggestedSubcategoriesUsed: false, - }) - ).toStrictEqual( - expect.objectContaining({ - venueId: '666', - }) + expect(formattedVenuesOptions).toEqual( + expect.arrayContaining([expect.objectContaining({ value: '3' })]) + ) + + const formattedDigitalVenuesOptions = formatVenuesOptions( + [venueListItemFactory({ isVirtual: true, id: 10 })], + true + ) + expect(formattedDigitalVenuesOptions).toEqual( + expect.arrayContaining([expect.objectContaining({ value: '10' })]) ) }) }) @@ -248,6 +224,7 @@ describe('setDefaultInitialValuesFromOffer', () => { visa: 'USA', productId: '', callId: '', + url: undefined, }) }) }) @@ -436,6 +413,7 @@ describe('serializeDetailsPostData', () => { visa: '123456789', ean: 'any ean', }, + url: undefined, productId: undefined, callId: '1', }) diff --git a/pro/src/pages/IndividualOffer/IndividualOfferDetails/commons/constants.ts b/pro/src/pages/IndividualOffer/IndividualOfferDetails/commons/constants.ts index f0acc95fe08..b2c7e152523 100644 --- a/pro/src/pages/IndividualOffer/IndividualOfferDetails/commons/constants.ts +++ b/pro/src/pages/IndividualOffer/IndividualOfferDetails/commons/constants.ts @@ -18,4 +18,5 @@ export const DEFAULT_DETAILS_FORM_VALUES = { suggestedSubcategory: '', productId: '', callId: '', + url: null, } diff --git a/pro/src/pages/IndividualOffer/IndividualOfferDetails/commons/types.ts b/pro/src/pages/IndividualOffer/IndividualOfferDetails/commons/types.ts index 194b8b1f67d..f29a05ebfcc 100644 --- a/pro/src/pages/IndividualOffer/IndividualOfferDetails/commons/types.ts +++ b/pro/src/pages/IndividualOffer/IndividualOfferDetails/commons/types.ts @@ -19,6 +19,7 @@ export type DetailsFormValues = { suggestedSubcategory: string productId: string callId?: string + url?: string | null } export type Product = { diff --git a/pro/src/pages/IndividualOffer/IndividualOfferDetails/commons/utils.ts b/pro/src/pages/IndividualOffer/IndividualOfferDetails/commons/utils.ts index 58c551d5c90..f528303fa6f 100644 --- a/pro/src/pages/IndividualOffer/IndividualOfferDetails/commons/utils.ts +++ b/pro/src/pages/IndividualOffer/IndividualOfferDetails/commons/utils.ts @@ -179,58 +179,22 @@ export const onSubcategoryChange = async ({ }) } -export const buildVenueOptions = ( +export const formatVenuesOptions = ( venues: VenueListItemResponseModel[], - areSuggestedCategoriesEnabled: boolean, - isOfferAddressEnabled: boolean + isOnline: boolean ) => { - let venueOptions = venues - .filter((venue) => !areSuggestedCategoriesEnabled || !venue.isVirtual) + // We want to display the virtual venues only if there are no physical venues available + // We also want to prevent selecting a virtual venue for a physical offer form + const hasAtLeastOnePhysicalVenue = venues.some((v) => !v.isVirtual) + return venues + .filter((venue) => + hasAtLeastOnePhysicalVenue || !isOnline ? !venue.isVirtual : true + ) .map((venue) => ({ value: venue.id.toString(), label: computeVenueDisplayName(venue), })) .sort((a, b) => a.label.localeCompare(b.label, 'fr')) - if (venueOptions.length > 1) { - venueOptions = [ - { - value: '', - label: isOfferAddressEnabled - ? 'Sélectionner la structure' - : 'Sélectionner le partenaire', - }, - ...venueOptions, - ] - } - - return venueOptions -} - -type SetDefaultInitialValuesProps = { - filteredVenues: VenueListItemResponseModel[] - areSuggestedSubcategoriesUsed: boolean -} - -export function setDefaultInitialValues({ - filteredVenues, - areSuggestedSubcategoriesUsed, -}: SetDefaultInitialValuesProps): DetailsFormValues { - let venueId = '' - - const venues = areSuggestedSubcategoriesUsed - ? filteredVenues.filter((v) => !v.isVirtual) - : filteredVenues - - if (venues.length === 1) { - venueId = String(venues[0].id) - } else if (venues.length === 0 && filteredVenues.length > 0) { - venueId = String(filteredVenues[0].id) - } - - return { - ...DEFAULT_DETAILS_FORM_VALUES, - venueId, - } } type SetDefaultInitialValuesFromOfferProps = { @@ -280,6 +244,7 @@ export function setDefaultInitialValuesFromOffer({ DEFAULT_DETAILS_FORM_VALUES.stageDirector, productId: offer.productId?.toString() ?? DEFAULT_DETAILS_FORM_VALUES.productId, + url: offer.url, } } @@ -356,6 +321,7 @@ export function serializeDetailsPostData( extraData: serializeExtraData(formValues), productId: formValues.productId ? Number(formValues.productId) : undefined, callId: formValues.callId, + url: formValues.url, }) } @@ -365,6 +331,7 @@ type PatchPayload = { extraData?: Record name: string subcategoryId: string + url?: string | null } export function serializeDetailsPatchData( @@ -376,5 +343,6 @@ export function serializeDetailsPatchData( description: formValues.description, durationMinutes: serializeDurationMinutes(formValues.durationMinutes ?? ''), extraData: serializeExtraData(formValues), + url: formValues.url, } } diff --git a/pro/src/pages/IndividualOffer/IndividualOfferDetails/commons/validationSchema.ts b/pro/src/pages/IndividualOffer/IndividualOfferDetails/commons/validationSchema.ts index 2cfb87ab896..7ba5654a689 100644 --- a/pro/src/pages/IndividualOffer/IndividualOfferDetails/commons/validationSchema.ts +++ b/pro/src/pages/IndividualOffer/IndividualOfferDetails/commons/validationSchema.ts @@ -10,10 +10,20 @@ const eanValidation = yup test: (ean) => ean === undefined || ean.length === 13, }) +// TODO: this regex is subject to backtracking which can lead to "catastrophic backtracking", high memory usage and slow performance +// we cannot use the yup url validation because we need to allow {} in the url to interpolate some data +const offerFormUrlRegex = new RegExp( + /*eslint-disable-next-line no-useless-escape*/ + /^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)(([a-z0-9]+([\-\.\.-\.@_a-z0-9]+)*\.[a-z]{2,5})|((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.){3}(25[0-5]|(2[0-4]|1\d|[1-9]|)\d))(:[0-9]{1,5})?\S*?$/, + 'i' +) + export const getValidationSchema = ({ isOfferAddressEnabled = false, + isDigitalOffer = false, }: { isOfferAddressEnabled: boolean + isDigitalOffer: boolean }) => { return yup.object().shape({ name: yup.string().trim().max(90).required('Veuillez renseigner un titre'), @@ -71,6 +81,20 @@ export const getValidationSchema = ({ ? 'Veuillez sélectionner une structure' : 'Veuillez sélectionner un lieu' ), + url: isDigitalOffer + ? yup + .string() + .required( + 'Veuillez renseigner une URL valide. Ex : https://exemple.com' + ) + .test({ + name: 'url', + message: + 'Veuillez renseigner une URL valide. Ex : https://exemple.com', + test: (url?: string) => + url ? url.match(offerFormUrlRegex) !== null : true, + }) + : yup.string().nullable(), }) } diff --git a/pro/src/pages/IndividualOffer/IndividualOfferDetails/components/DetailsForm/DetailsForm.tsx b/pro/src/pages/IndividualOffer/IndividualOfferDetails/components/DetailsForm/DetailsForm.tsx index 681d904c888..cb64ef9a20c 100644 --- a/pro/src/pages/IndividualOffer/IndividualOfferDetails/components/DetailsForm/DetailsForm.tsx +++ b/pro/src/pages/IndividualOffer/IndividualOfferDetails/components/DetailsForm/DetailsForm.tsx @@ -7,7 +7,6 @@ import { CategoryResponseModel, SubcategoryResponseModel, SuggestedSubcategoriesResponseModel, - VenueListItemResponseModel, } from 'apiClient/v1' import { useIndividualOfferContext } from 'commons/context/IndividualOfferContext/IndividualOfferContext' import { CATEGORY_STATUS } from 'commons/core/Offers/constants' @@ -19,15 +18,13 @@ import { OnImageUploadArgs } from 'components/ImageUploader/components/ButtonIma import fullMoreIcon from 'icons/full-more.svg' import { DEFAULT_DETAILS_FORM_VALUES } from 'pages/IndividualOffer/IndividualOfferDetails/commons/constants' import { DetailsFormValues } from 'pages/IndividualOffer/IndividualOfferDetails/commons/types' -import { - buildVenueOptions, - isSubCategoryCD, -} from 'pages/IndividualOffer/IndividualOfferDetails/commons/utils' +import { isSubCategoryCD } from 'pages/IndividualOffer/IndividualOfferDetails/commons/utils' import { Callout } from 'ui-kit/Callout/Callout' import { CalloutVariant } from 'ui-kit/Callout/types' import { Select } from 'ui-kit/form/Select/Select' import { TextArea } from 'ui-kit/form/TextArea/TextArea' import { TextInput } from 'ui-kit/form/TextInput/TextInput' +import { InfoBox } from 'ui-kit/InfoBox/InfoBox' import { DetailsSubForm } from './DetailsSubForm/DetailsSubForm' import { Subcategories } from './Subcategories/Subcategories' @@ -38,25 +35,27 @@ const DEBOUNCE_TIME_BEFORE_REQUEST = 400 type DetailsFormProps = { isEanSearchDisplayed: boolean isProductBased: boolean - filteredVenues: VenueListItemResponseModel[] + venuesOptions: { label: string; value: string }[] filteredCategories: CategoryResponseModel[] filteredSubcategories: SubcategoryResponseModel[] - readonlyFields: string[] + readOnlyFields: string[] onImageUpload: (values: OnImageUploadArgs) => Promise onImageDelete: () => Promise imageOffer?: IndividualOfferImage + categoryStatus: CATEGORY_STATUS } export const DetailsForm = ({ isEanSearchDisplayed, isProductBased, - filteredVenues, + venuesOptions, filteredCategories, filteredSubcategories, - readonlyFields: readOnlyFields, + readOnlyFields, onImageUpload, onImageDelete, imageOffer, + categoryStatus, }: DetailsFormProps): JSX.Element => { const areSuggestedSubcategoriesUsed = useSuggestedSubcategoriesAbTest() const [hasSuggestionsApiBeenCalled, setHasSuggestionsApiBeenCalled] = @@ -71,12 +70,6 @@ export const DetailsForm = ({ const { offer, subCategories } = useIndividualOfferContext() const offerAddressEnabled = useActiveFeature('WIP_ENABLE_OFFER_ADDRESS') - const venueOptions = buildVenueOptions( - filteredVenues, - areSuggestedSubcategoriesUsed, - offerAddressEnabled - ) - async function getSuggestedSubcategories() { if (!areSuggestedSubcategoriesUsed && !offer) { return @@ -131,14 +124,14 @@ export const DetailsForm = ({ subcategoryId !== DEFAULT_DETAILS_FORM_VALUES.subcategoryId const showAddVenueBanner = - !areSuggestedSubcategoriesUsed && venueOptions.length === 0 + !areSuggestedSubcategoriesUsed && venuesOptions.length === 0 const isSuggestedSubcategoryDisplayed = areSuggestedSubcategoriesUsed && !offer && !isProductBased const showOtherAddVenueBanner = areSuggestedSubcategoriesUsed && - venueOptions.length === 0 && + venuesOptions.length === 0 && subcategory?.onlineOfflinePlatform === CATEGORY_STATUS.OFFLINE return ( @@ -166,12 +159,18 @@ export const DetailsForm = ({ )} {!showAddVenueBanner && ( <> - {venueOptions.length > 1 && ( + {venuesOptions.length > 1 && (