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
+}
|