diff --git a/pro/src/components/CollectiveOfferNavigation/CollectiveOfferNavigation.tsx b/pro/src/components/CollectiveOfferNavigation/CollectiveOfferNavigation.tsx index 49088d22eaf..2f81d67d785 100644 --- a/pro/src/components/CollectiveOfferNavigation/CollectiveOfferNavigation.tsx +++ b/pro/src/components/CollectiveOfferNavigation/CollectiveOfferNavigation.tsx @@ -6,8 +6,10 @@ import { useSWRConfig } from 'swr' import { api } from 'apiClient/api' import { + CollectiveOfferAllowedAction, CollectiveOfferDisplayedStatus, CollectiveOfferStatus, + CollectiveOfferTemplateAllowedAction, GetCollectiveOfferResponseModel, GetCollectiveOfferTemplateResponseModel, } from 'apiClient/v1' @@ -28,12 +30,14 @@ import { isCollectiveOfferTemplate, } from 'core/OfferEducational/types' import { computeURLCollectiveOfferId } from 'core/OfferEducational/utils/computeURLCollectiveOfferId' +import { createOfferFromBookableOffer } from 'core/OfferEducational/utils/createOfferFromBookableOffer' import { createOfferFromTemplate } from 'core/OfferEducational/utils/createOfferFromTemplate' import { useActiveFeature } from 'hooks/useActiveFeature' import { useNotification } from 'hooks/useNotification' import { useOfferStockEditionURL } from 'hooks/useOfferEditionURL' import fullArchiveIcon from 'icons/full-archive.svg' -import fullMoreIcon from 'icons/full-more.svg' +import fullCopyIcon from 'icons/full-duplicate.svg' +import fullPlusIcon from 'icons/full-plus.svg' import fullShowIcon from 'icons/full-show.svg' import { selectCurrentOffererId } from 'store/user/selectors' import { Button } from 'ui-kit/Button/Button' @@ -41,6 +45,7 @@ import { ButtonLink } from 'ui-kit/Button/ButtonLink' import { ButtonVariant } from 'ui-kit/Button/types' import { Divider } from 'ui-kit/Divider/Divider' import { Tabs } from 'ui-kit/Tabs/Tabs' +import { isActionAllowedOnCollectiveOffer } from 'utils/isActionAllowedOnCollectiveOffer' import styles from './CollectiveOfferNavigation.module.scss' @@ -83,6 +88,9 @@ export const CollectiveOfferNavigation = ({ const navigate = useNavigate() const location = useLocation() const isMarseilleActive = useActiveFeature('WIP_ENABLE_MARSEILLE') + const areCollectiveNewStatusesEnabled = useActiveFeature( + 'ENABLE_COLLECTIVE_NEW_STATUSES' + ) const selectedOffererId = useSelector(selectCurrentOffererId) const { mutate } = useSWRConfig() @@ -236,6 +244,17 @@ export const CollectiveOfferNavigation = ({ } } + const canDuplicateOffer = offer + ? areCollectiveNewStatusesEnabled + ? isActionAllowedOnCollectiveOffer( + offer, + isTemplate + ? CollectiveOfferTemplateAllowedAction.CAN_CREATE_BOOKABLE_OFFER + : CollectiveOfferAllowedAction.CAN_DUPLICATE + ) + : isTemplate + : false + return isEditingExistingOffer ? ( <>
@@ -255,28 +274,32 @@ export const CollectiveOfferNavigation = ({ )} - {isTemplate && ( + {canDuplicateOffer && ( )}
diff --git a/pro/src/core/OfferEducational/types.ts b/pro/src/core/OfferEducational/types.ts index bc0e17c3e38..46984ed63dc 100644 --- a/pro/src/core/OfferEducational/types.ts +++ b/pro/src/core/OfferEducational/types.ts @@ -78,13 +78,15 @@ export const isCollectiveOffer = ( value: unknown ): value is GetCollectiveOfferResponseModel => // Could be enhanced to check that it is also a GetCollectiveOfferTemplateResponseModel - hasProperty(value, 'isTemplate') && value.isTemplate === false + (hasProperty(value, 'isTemplate') && value.isTemplate === false) || + (hasProperty(value, 'isShowcase') && value.isShowcase === false) export const isCollectiveOfferTemplate = ( value: unknown ): value is GetCollectiveOfferTemplateResponseModel => // Could be enhanced to check that it is also a GetCollectiveOfferTemplateResponseModel - hasProperty(value, 'isTemplate') && value.isTemplate === true + (hasProperty(value, 'isTemplate') && value.isTemplate === true) || + (hasProperty(value, 'isShowcase') && value.isShowcase === true) export type VisibilityFormValues = { visibility: 'all' | 'one' diff --git a/pro/src/pages/Offers/OffersTable/Cells/CollectiveActionsCells.tsx b/pro/src/pages/Offers/OffersTable/Cells/CollectiveActionsCells.tsx index 3149d74249d..f4ce177a5ba 100644 --- a/pro/src/pages/Offers/OffersTable/Cells/CollectiveActionsCells.tsx +++ b/pro/src/pages/Offers/OffersTable/Cells/CollectiveActionsCells.tsx @@ -9,8 +9,10 @@ import { api } from 'apiClient/api' import { getErrorCode, isErrorAPIError } from 'apiClient/helpers' import { CollectiveBookingStatus, + CollectiveOfferAllowedAction, CollectiveOfferResponseModel, CollectiveOfferStatus, + CollectiveOfferTemplateAllowedAction, } from 'apiClient/v1' import { useAnalytics } from 'app/App/analytics/firebase' import { ArchiveConfirmationModal } from 'components/ArchiveConfirmationModal/ArchiveConfirmationModal' @@ -47,6 +49,7 @@ import { formatBrowserTimezonedDateAsUTC, isDateValid, } from 'utils/date' +import { isActionAllowedOnCollectiveOffer } from 'utils/isActionAllowedOnCollectiveOffer' import { localStorageAvailable } from 'utils/localStorageAvailable' import { BookingLinkCell } from './BookingLinkCell' @@ -90,6 +93,9 @@ export const CollectiveActionsCells = ({ const isNewOffersAndBookingsActive = useActiveFeature( 'WIP_ENABLE_NEW_COLLECTIVE_OFFERS_AND_BOOKINGS_STRUCTURE' ) + const areCollectiveNewStatusesEnabled = useActiveFeature( + 'ENABLE_COLLECTIVE_NEW_STATUSES' + ) const collectiveOffersQueryKeys = getCollectiveOffersSwrKeys({ isNewOffersAndBookingsActive, @@ -225,7 +231,14 @@ export const CollectiveActionsCells = ({ }) } - const canDuplicateOffer = offer.status !== CollectiveOfferStatus.DRAFT + const canDuplicateOffer = areCollectiveNewStatusesEnabled + ? isActionAllowedOnCollectiveOffer( + offer, + offer.isShowcase + ? CollectiveOfferTemplateAllowedAction.CAN_CREATE_BOOKABLE_OFFER + : CollectiveOfferAllowedAction.CAN_DUPLICATE + ) + : offer.status !== CollectiveOfferStatus.DRAFT return ( { + it.each([ + { + description: 'allowed action CAN_DUPLICATE on bookable offer ', + offer: getCollectiveOfferFactory({ + allowedActions: [CollectiveOfferAllowedAction.CAN_DUPLICATE], + }), + action: CollectiveOfferAllowedAction.CAN_DUPLICATE, + expected: true, + }, + { + description: 'allowed action CAN_CREATE_BOOKABLE_OFFER on template offer', + offer: getCollectiveOfferTemplateFactory({ + allowedActions: [ + CollectiveOfferTemplateAllowedAction.CAN_CREATE_BOOKABLE_OFFER, + ], + isTemplate: true, + }), + action: CollectiveOfferTemplateAllowedAction.CAN_CREATE_BOOKABLE_OFFER, + expected: true, + }, + { + description: 'not allowed action CAN_DUPLICATE on bookable offer ', + offer: getCollectiveOfferFactory({ + allowedActions: [], + }), + action: CollectiveOfferAllowedAction.CAN_DUPLICATE, + expected: false, + }, + { + description: + 'not allowed action CAN_CREATE_BOOKABLE_OFFER on template offer', + offer: getCollectiveOfferTemplateFactory({ + allowedActions: [], + isTemplate: true, + }), + action: CollectiveOfferTemplateAllowedAction.CAN_CREATE_BOOKABLE_OFFER, + expected: false, + }, + ])( + 'should return $expected for $description', + ({ offer, action, expected }) => { + expect(isActionAllowedOnCollectiveOffer(offer, action)).toBe(expected) + } + ) +}) diff --git a/pro/src/utils/isActionAllowedOnCollectiveOffer.ts b/pro/src/utils/isActionAllowedOnCollectiveOffer.ts new file mode 100644 index 00000000000..bb9af2fc22c --- /dev/null +++ b/pro/src/utils/isActionAllowedOnCollectiveOffer.ts @@ -0,0 +1,31 @@ +import { + CollectiveOfferAllowedAction, + CollectiveOfferResponseModel, + CollectiveOfferTemplateAllowedAction, + GetCollectiveOfferResponseModel, + GetCollectiveOfferTemplateResponseModel, +} from 'apiClient/v1' +import { + isCollectiveOffer, + isCollectiveOfferTemplate, +} from 'core/OfferEducational/types' + +export function isActionAllowedOnCollectiveOffer( + offer: + | GetCollectiveOfferResponseModel + | GetCollectiveOfferTemplateResponseModel + | CollectiveOfferResponseModel, + action: CollectiveOfferAllowedAction | CollectiveOfferTemplateAllowedAction +) { + if (isCollectiveOffer(offer)) { + return offer.allowedActions.includes(action as CollectiveOfferAllowedAction) + } + + if (isCollectiveOfferTemplate(offer)) { + return offer.allowedActions.includes( + action as CollectiveOfferTemplateAllowedAction + ) + } + + return false +}