import React, {FC, ReactNode} from "react"; import {NodeProps, Position} from "@xyflow/react"; import {OfferData, RenderedNodeDataInBranch, useNodesStore} from "./tree/store"; import {OfferCard} from "./tree/OfferCard"; import {Handle} from "./tree/Handle"; import {colors} from "./tree/Colors"; import {RightContextButton} from "./tree/RightContextButton"; import {__} from "./globals"; import {Color, groupHover, hover} from "./tree/Color"; import {useBranch, useBranchChildrenShallow} from "./tree/TreeHooks"; import {useOpenTestablesPopupWindow} from "./tree/useOpenTestablesPopupWindow"; import {PopupWindowStateContext, TreeContextData} from "./tree/atoms"; import {clone} from "lodash"; import classNames from "classnames"; import {incompatibleOffersFromInsideTiers} from "./tree/actions/SelectOffersNew"; import {FloatingMarker} from "./tree/FloatingMarker"; import {getCardRendererFromType} from "./tree/cards"; import type {CardRenderer} from "./tree/CardRenderers"; export type OffersBranchData = RenderedNodeDataInBranch & { renderedNodeType: 'branch-offers', } export const getOffersPopupWindowContext = (excludedOfferTypes: string[] = [], windowData?: TreeContextData['mainWindow']): PopupWindowStateContext => { return { id: 'offers-new', scope: 'tier-branch', data: { mainWindow: windowData, componentType: 'offer', supportsMultipleComponents: false, unsupportedComponents: excludedOfferTypes, targetType: 'ghost', // root | testableComposite mode: 'create' } } as PopupWindowStateContext; } type SuggestedScopeMarker = ReturnType>; type SuggestedScopeMarkerData = { prefix?: string, label: string }; const isSuggestedScopeMarkerData = (marker: SuggestedScopeMarker): marker is SuggestedScopeMarkerData => { return !!marker && typeof marker === 'object' && 'label' in marker; } const getSuggestedScopeMarker = (offer: OfferData, suggestedScope?: string): ReactNode => { const offerCardRendererData = getCardRendererFromType(offer.type) as CardRenderer | undefined const marker = offerCardRendererData?.suggestedScopeMarker?.(suggestedScope || '') if (isSuggestedScopeMarkerData(marker)) { return } return marker ?? null } function OffersCard({ isFirst, shiftedOffers, branchId, offer, color, onNewOfferClick, index, afterContentInside, suggestedScope, suggestedScopeMarker }: { id?: string, isFirst: boolean, shiftedOffers: OfferData[], branchId: string, offer: OfferData, color: Color, onNewOfferClick: () => any, index: number, afterContentInside?: ReactNode, suggestedScope?: string, suggestedScopeMarker?: ReactNode }) { if (!shiftedOffers) { return null; } const itsTheLast = index === shiftedOffers.length - 1 && shiftedOffers.length const hasMoreThanOneOffer = shiftedOffers.length > 0 // it will be 0 when there's only one offer (because its been array.shifted out and passed manually) const offerCardRendererData = getCardRendererFromType(offer.type) as CardRenderer const rightContextButton =
{ if (context === 'button.label') { return color.text(40); } else if (context === 'hover:button.background') { return color.background(hover(0)); } else if (context === 'group-hover:anchor') { // @ts-ignore return color[type](groupHover(50)); } // @ts-ignore return color[type](30); } }} thickness={2} bringButtonToFrontOnHover={false} uppercased={false} labelSize="text-smaller-1" label={(buttonIsHovered) => false && buttonIsHovered && __('Extra offer')} onClick={onNewOfferClick} />
let itsTheOnlyOffer = isFirst && !hasMoreThanOneOffer; let itHasMoreOffersAndThisIsntTheFirstOffer = hasMoreThanOneOffer && !isFirst; let isTheLast = shiftedOffers.length === index + 1; const showRightContextButton = (itsTheOnlyOffer || (itHasMoreOffersAndThisIsntTheFirstOffer && isTheLast)) // here render custom card if supported and enabled const customCardExtraData = { isFirst, hasMoreThanOneOffer, itsTheLast, afterContentInside, rightContextButton, showRightContextButton, branchId, suggestedScope } const CustomCard = offerCardRendererData?.CustomCardComponent?.({color, context: 'offers', id: offer.id, localData: offer}, customCardExtraData) if (!!CustomCard) { return } return
afterContentInside} afterContent={() => { return <> {suggestedScopeMarker} {showRightContextButton && rightContextButton} }}/>
; } export const OffersBranch: FC<{ data: OffersBranchData } & NodeProps> = ({id}) => { const getOffer = useNodesStore(store => store.getOffer) const branch = useBranch(id) const offers = clone(useBranchChildrenShallow(id) ?? []) const allSelectedOffers = clone(offers) const openTestablesPopup = useOpenTestablesPopupWindow(); const addBranchOffer = useNodesStore(store => store.addBranchOffer) // @ts-ignore const openPopup = () => { // @ts-ignore openTestablesPopup({ // @ts-ignore context: getOffersPopupWindowContext([ ...allSelectedOffers.map(o => o.type), ...(branch?.isInsideAnotherBranch() ? incompatibleOffersFromInsideTiers : []) ], { title: __('Add another offer'), description: __('You can combine multiple offers per branch like a cart discount + gift, cart discount + shipping discount, etc.') }), onClose: (data) => { const {status, components} = data if (status === 'success' && components?.length > 0) { addBranchOffer(components[0], id) } } }) } if (!offers || !branch) { console.error('OffersBranch: offers or branch not found', id) return null; } const color = colors.blue; const firstOffer = offers.shift()! if (offers.length === 0 && !firstOffer) { return null } const isInsideBranch = branch.isInsideAnotherBranch() const suggestedScope = branch.getTreeNode().suggestedScope return
} />
{offers.map((offer, index) => { return <> {true &&
} })} }