mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-23 14:40:50 +01:00
Catalog updates
This commit is contained in:
parent
43867f9f38
commit
c1e9576050
@ -7,13 +7,12 @@ import { CatalogPurchasedEvent } from '../../events/catalog/CatalogPurchasedEven
|
|||||||
import { CatalogPurchaseSoldOutEvent } from '../../events/catalog/CatalogPurchaseSoldOutEvent';
|
import { CatalogPurchaseSoldOutEvent } from '../../events/catalog/CatalogPurchaseSoldOutEvent';
|
||||||
import { dispatchUiEvent } from '../../hooks/events/ui/ui-event';
|
import { dispatchUiEvent } from '../../hooks/events/ui/ui-event';
|
||||||
import { CreateMessageHook } from '../../hooks/messages/message-event';
|
import { CreateMessageHook } from '../../hooks/messages/message-event';
|
||||||
import { CatalogMessageHandlerProps } from './CatalogMessageHandler.types';
|
|
||||||
import { CatalogPetPalette } from './common/CatalogPetPalette';
|
import { CatalogPetPalette } from './common/CatalogPetPalette';
|
||||||
import { SubscriptionInfo } from './common/SubscriptionInfo';
|
import { SubscriptionInfo } from './common/SubscriptionInfo';
|
||||||
import { useCatalogContext } from './context/CatalogContext';
|
import { useCatalogContext } from './context/CatalogContext';
|
||||||
import { CatalogActions } from './reducers/CatalogReducer';
|
import { CatalogActions } from './reducers/CatalogReducer';
|
||||||
|
|
||||||
export const CatalogMessageHandler: FC<CatalogMessageHandlerProps> = props =>
|
export const CatalogMessageHandler: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const { catalogState = null, dispatchCatalogState = null } = useCatalogContext();
|
const { catalogState = null, dispatchCatalogState = null } = useCatalogContext();
|
||||||
|
|
@ -9,7 +9,7 @@ import { useUiEvent } from '../../hooks/events/ui/ui-event';
|
|||||||
import { SendMessageHook } from '../../hooks/messages/message-event';
|
import { SendMessageHook } from '../../hooks/messages/message-event';
|
||||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../layout';
|
import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../layout';
|
||||||
import { CatalogMessageHandler } from './CatalogMessageHandler';
|
import { CatalogMessageHandler } from './CatalogMessageHandler';
|
||||||
import { CatalogMode, CatalogViewProps } from './CatalogView.types';
|
import { CatalogMode } from './common/CatalogMode';
|
||||||
import { BuildCatalogPageTree } from './common/CatalogUtilities';
|
import { BuildCatalogPageTree } from './common/CatalogUtilities';
|
||||||
import { CatalogContextProvider } from './context/CatalogContext';
|
import { CatalogContextProvider } from './context/CatalogContext';
|
||||||
import { CatalogReducer, initialCatalog } from './reducers/CatalogReducer';
|
import { CatalogReducer, initialCatalog } from './reducers/CatalogReducer';
|
||||||
@ -18,7 +18,7 @@ import { ACTIVE_PAGES, CatalogNavigationView } from './views/navigation/CatalogN
|
|||||||
import { CatalogPageView } from './views/page/CatalogPageView';
|
import { CatalogPageView } from './views/page/CatalogPageView';
|
||||||
import { MarketplacePostOfferView } from './views/page/layout/marketplace/post-offer/MarketplacePostOfferView';
|
import { MarketplacePostOfferView } from './views/page/layout/marketplace/post-offer/MarketplacePostOfferView';
|
||||||
|
|
||||||
export const CatalogView: FC<CatalogViewProps> = props =>
|
export const CatalogView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const [ isVisible, setIsVisible ] = useState(false);
|
const [ isVisible, setIsVisible ] = useState(false);
|
||||||
const [ roomPreviewer, setRoomPreviewer ] = useState<RoomPreviewer>(null);
|
const [ roomPreviewer, setRoomPreviewer ] = useState<RoomPreviewer>(null);
|
||||||
@ -215,10 +215,10 @@ export const CatalogView: FC<CatalogViewProps> = props =>
|
|||||||
<NitroCardContentView>
|
<NitroCardContentView>
|
||||||
<Grid>
|
<Grid>
|
||||||
{ currentNavigationPage && !navigationHidden &&
|
{ currentNavigationPage && !navigationHidden &&
|
||||||
<Column size={ 3 }>
|
<Column size={ 3 } overflow="hidden">
|
||||||
<CatalogNavigationView page={ currentNavigationPage } pendingTree={ pendingTree } setPendingTree={ setPendingTree } />
|
<CatalogNavigationView page={ currentNavigationPage } pendingTree={ pendingTree } setPendingTree={ setPendingTree } />
|
||||||
</Column> }
|
</Column> }
|
||||||
<Column size={ (navigationHidden ? 12 : 9) }>
|
<Column size={ (navigationHidden ? 12 : 9) } overflow="hidden">
|
||||||
<CatalogPageView roomPreviewer={ roomPreviewer } />
|
<CatalogPageView roomPreviewer={ roomPreviewer } />
|
||||||
</Column>
|
</Column>
|
||||||
</Grid>
|
</Grid>
|
@ -1,8 +1,3 @@
|
|||||||
export interface CatalogViewProps
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CatalogMode
|
export class CatalogMode
|
||||||
{
|
{
|
||||||
public static MODE_NORMAL: string = 'NORMAL';
|
public static MODE_NORMAL: string = 'NORMAL';
|
10
src/components/catalog/common/CatalogPurchaseState.ts
Normal file
10
src/components/catalog/common/CatalogPurchaseState.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export class CatalogPurchaseState
|
||||||
|
{
|
||||||
|
public static NONE = 0;
|
||||||
|
public static CONFIRM = 1;
|
||||||
|
public static PURCHASE = 2;
|
||||||
|
public static NO_CREDITS = 3;
|
||||||
|
public static NO_POINTS = 4;
|
||||||
|
public static SOLD_OUT = 5;
|
||||||
|
public static FAILED = 6;
|
||||||
|
}
|
@ -1,4 +1,10 @@
|
|||||||
@import './catalog-icon/CatalogIconView';
|
.catalog-icon-image {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
min-width: 20px;
|
||||||
|
min-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
@import './gift/CatalogGiftView';
|
@import './gift/CatalogGiftView';
|
||||||
@import './navigation/CatalogNavigationView';
|
@import './navigation/CatalogNavigationView';
|
||||||
@import './page/CatalogPageView';
|
@import './page/CatalogPageView';
|
@ -0,0 +1,20 @@
|
|||||||
|
import { FC, useMemo } from 'react';
|
||||||
|
import { GetConfiguration } from '../../../../api';
|
||||||
|
import { LayoutImage } from '../../../../common/layout/LayoutImage';
|
||||||
|
|
||||||
|
export interface CatalogIconViewProps
|
||||||
|
{
|
||||||
|
icon: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CatalogIconView: FC<CatalogIconViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { icon = 0 } = props;
|
||||||
|
|
||||||
|
const getIconUrl = useMemo(() =>
|
||||||
|
{
|
||||||
|
return ((GetConfiguration<string>('catalog.asset.icon.url')).replace('%name%', icon.toString()));
|
||||||
|
}, [ icon ]);
|
||||||
|
|
||||||
|
return <LayoutImage imageUrl={ getIconUrl } fit={ false } className="catalog-icon-image" />;
|
||||||
|
}
|
@ -3,8 +3,8 @@ import { FC, useCallback, useRef } from 'react';
|
|||||||
import { GetRoomEngine } from '../../../../api';
|
import { GetRoomEngine } from '../../../../api';
|
||||||
import { CatalogEvent } from '../../../../events';
|
import { CatalogEvent } from '../../../../events';
|
||||||
import { useUiEvent } from '../../../../hooks';
|
import { useUiEvent } from '../../../../hooks';
|
||||||
import { RoomPreviewerView } from '../../../shared/room-previewer/RoomPreviewerView';
|
import { RoomPreviewerView } from '../../../../views/shared/room-previewer/RoomPreviewerView';
|
||||||
import { RoomPreviewerViewProps } from '../../../shared/room-previewer/RoomPreviewerView.types';
|
import { RoomPreviewerViewProps } from '../../../../views/shared/room-previewer/RoomPreviewerView.types';
|
||||||
|
|
||||||
export const CatalogRoomPreviewerView: FC<RoomPreviewerViewProps> = props =>
|
export const CatalogRoomPreviewerView: FC<RoomPreviewerViewProps> = props =>
|
||||||
{
|
{
|
241
src/components/catalog/views/gift/CatalogGiftView.tsx
Normal file
241
src/components/catalog/views/gift/CatalogGiftView.tsx
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
import { PurchaseFromCatalogAsGiftComposer } from '@nitrots/nitro-renderer';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
import { GetSessionDataManager, LocalizeText } from '../../../../api';
|
||||||
|
import { Button } from '../../../../common/Button';
|
||||||
|
import { ButtonGroup } from '../../../../common/ButtonGroup';
|
||||||
|
import { Column } from '../../../../common/Column';
|
||||||
|
import { Flex } from '../../../../common/Flex';
|
||||||
|
import { FormGroup } from '../../../../common/FormGroup';
|
||||||
|
import { Text } from '../../../../common/Text';
|
||||||
|
import { CatalogEvent } from '../../../../events';
|
||||||
|
import { CatalogInitGiftEvent } from '../../../../events/catalog/CatalogInitGiftEvent';
|
||||||
|
import { BatchUpdates, SendMessageHook, useUiEvent } from '../../../../hooks';
|
||||||
|
import { NitroCardContentView, NitroCardHeaderView, NitroCardView, NitroLayoutGiftCardView } from '../../../../layout';
|
||||||
|
import { CurrencyIcon } from '../../../../views/shared/currency-icon/CurrencyIcon';
|
||||||
|
import { FurniImageView } from '../../../../views/shared/furni-image/FurniImageView';
|
||||||
|
import { useCatalogContext } from '../../context/CatalogContext';
|
||||||
|
|
||||||
|
export const CatalogGiftView: FC<{}> = props =>
|
||||||
|
{
|
||||||
|
const [ isVisible, setIsVisible ] = useState<boolean>(false);
|
||||||
|
const [ pageId, setPageId ] = useState<number>(0);
|
||||||
|
const [ offerId, setOfferId ] = useState<number>(0);
|
||||||
|
const [ receiverName, setReceiverName ] = useState<string>('');
|
||||||
|
const [ showMyFace, setShowMyFace ] = useState<boolean>(true);
|
||||||
|
const [ message, setMessage ] = useState<string>('');
|
||||||
|
const [ colors, setColors ] = useState<{ id: number, color: string }[]>([]);
|
||||||
|
const [ selectedBoxIndex, setSelectedBoxIndex ] = useState<number>(0);
|
||||||
|
const [ selectedRibbonIndex, setSelectedRibbonIndex ] = useState<number>(0);
|
||||||
|
const [ selectedColorId, setSelectedColorId ] = useState<number>(0);
|
||||||
|
const [ maxBoxIndex, setMaxBoxIndex ] = useState<number>(0);
|
||||||
|
const [ maxRibbonIndex, setMaxRibbonIndex ] = useState<number>(0);
|
||||||
|
const [ receiverNotFound, setReceiverNotFound ] = useState<boolean>(false);
|
||||||
|
const { catalogState = null } = useCatalogContext();
|
||||||
|
const { giftConfiguration = null } = catalogState;
|
||||||
|
|
||||||
|
const close = useCallback(() =>
|
||||||
|
{
|
||||||
|
BatchUpdates(() =>
|
||||||
|
{
|
||||||
|
setIsVisible(false);
|
||||||
|
setPageId(0);
|
||||||
|
setOfferId(0);
|
||||||
|
setReceiverName('');
|
||||||
|
setShowMyFace(true);
|
||||||
|
setMessage('');
|
||||||
|
setSelectedBoxIndex(0);
|
||||||
|
setSelectedRibbonIndex(0);
|
||||||
|
|
||||||
|
if(colors.length) setSelectedColorId(colors[0].id);
|
||||||
|
});
|
||||||
|
}, [ colors ]);
|
||||||
|
|
||||||
|
const onCatalogEvent = useCallback((event: CatalogEvent) =>
|
||||||
|
{
|
||||||
|
switch(event.type)
|
||||||
|
{
|
||||||
|
case CatalogEvent.PURCHASE_SUCCESS:
|
||||||
|
close();
|
||||||
|
return;
|
||||||
|
case CatalogEvent.INIT_GIFT:
|
||||||
|
const castedEvent = (event as CatalogInitGiftEvent);
|
||||||
|
|
||||||
|
BatchUpdates(() =>
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
|
||||||
|
setPageId(castedEvent.pageId);
|
||||||
|
setOfferId(castedEvent.offerId);
|
||||||
|
setIsVisible(true);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
case CatalogEvent.GIFT_RECEIVER_NOT_FOUND:
|
||||||
|
setReceiverNotFound(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}, [ close ]);
|
||||||
|
|
||||||
|
useUiEvent(CatalogEvent.PURCHASE_SUCCESS, onCatalogEvent);
|
||||||
|
useUiEvent(CatalogEvent.INIT_GIFT, onCatalogEvent);
|
||||||
|
useUiEvent(CatalogEvent.GIFT_RECEIVER_NOT_FOUND, onCatalogEvent);
|
||||||
|
|
||||||
|
const isBoxDefault = useMemo(() =>
|
||||||
|
{
|
||||||
|
return giftConfiguration ? (giftConfiguration.defaultStuffTypes.findIndex(s => (s === giftConfiguration.boxTypes[selectedBoxIndex])) > -1) : true;
|
||||||
|
}, [ giftConfiguration, selectedBoxIndex ]);
|
||||||
|
|
||||||
|
const extraData = useMemo(() =>
|
||||||
|
{
|
||||||
|
if(!giftConfiguration) return '';
|
||||||
|
|
||||||
|
return ((giftConfiguration.boxTypes[selectedBoxIndex] * 1000) + giftConfiguration.ribbonTypes[selectedRibbonIndex]).toString();
|
||||||
|
}, [ giftConfiguration, selectedBoxIndex, selectedRibbonIndex ]);
|
||||||
|
|
||||||
|
const isColorable = useMemo(() =>
|
||||||
|
{
|
||||||
|
if(!giftConfiguration) return false;
|
||||||
|
|
||||||
|
const boxType = giftConfiguration.boxTypes[selectedBoxIndex];
|
||||||
|
|
||||||
|
return (boxType === 8 || (boxType >= 3 && boxType <= 6)) ? false : true;
|
||||||
|
}, [ giftConfiguration, selectedBoxIndex ]);
|
||||||
|
|
||||||
|
const handleAction = useCallback((action: string) =>
|
||||||
|
{
|
||||||
|
switch(action)
|
||||||
|
{
|
||||||
|
case 'prev_box':
|
||||||
|
setSelectedBoxIndex(value => (value === 0 ? maxBoxIndex : value - 1));
|
||||||
|
return;
|
||||||
|
case 'next_box':
|
||||||
|
setSelectedBoxIndex(value => (value === maxBoxIndex ? 0 : value + 1));
|
||||||
|
return;
|
||||||
|
case 'prev_ribbon':
|
||||||
|
setSelectedRibbonIndex(value => (value === 0 ? maxRibbonIndex : value - 1));
|
||||||
|
return;
|
||||||
|
case 'next_ribbon':
|
||||||
|
setSelectedRibbonIndex(value => (value === maxRibbonIndex ? 0 : value + 1));
|
||||||
|
return;
|
||||||
|
case 'buy':
|
||||||
|
if(!receiverName || (receiverName.length === 0))
|
||||||
|
{
|
||||||
|
setReceiverNotFound(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SendMessageHook(new PurchaseFromCatalogAsGiftComposer(pageId, offerId, extraData, receiverName, message, selectedColorId, selectedBoxIndex, selectedRibbonIndex, showMyFace));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}, [ extraData, maxBoxIndex, maxRibbonIndex, message, offerId, pageId, receiverName, selectedBoxIndex, selectedColorId, selectedRibbonIndex, showMyFace ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
setReceiverNotFound(false);
|
||||||
|
}, [ receiverName ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!giftConfiguration) return;
|
||||||
|
|
||||||
|
const newColors: { id: number, color: string }[] = [];
|
||||||
|
|
||||||
|
for(const colorId of giftConfiguration.stuffTypes)
|
||||||
|
{
|
||||||
|
const giftData = GetSessionDataManager().getFloorItemData(colorId);
|
||||||
|
|
||||||
|
if(!giftData) continue;
|
||||||
|
|
||||||
|
if(giftData.colors && giftData.colors.length > 0) newColors.push({ id: colorId, color: `#${giftData.colors[0].toString(16)}` });
|
||||||
|
}
|
||||||
|
|
||||||
|
BatchUpdates(() =>
|
||||||
|
{
|
||||||
|
setMaxBoxIndex(giftConfiguration.boxTypes.length - 1);
|
||||||
|
setMaxRibbonIndex(giftConfiguration.ribbonTypes.length - 1);
|
||||||
|
|
||||||
|
if(newColors.length)
|
||||||
|
{
|
||||||
|
setSelectedColorId(newColors[0].id);
|
||||||
|
setColors(newColors);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [ giftConfiguration ]);
|
||||||
|
|
||||||
|
if(!giftConfiguration || !giftConfiguration.isEnabled || !isVisible) return null;
|
||||||
|
|
||||||
|
const boxName = 'catalog.gift_wrapping_new.box.' + (isBoxDefault ? 'default' : selectedBoxIndex);
|
||||||
|
const ribbonName = `catalog.gift_wrapping_new.ribbon.${ selectedRibbonIndex }`;
|
||||||
|
const priceText = 'catalog.gift_wrapping_new.' + (isBoxDefault ? 'freeprice' : 'price');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NitroCardView uniqueKey="catalog-gift" className="nitro-catalog-gift" simple={ true }>
|
||||||
|
<NitroCardHeaderView headerText={ LocalizeText('catalog.gift_wrapping.title') } onCloseClick={ close } />
|
||||||
|
<NitroCardContentView className="text-black">
|
||||||
|
<FormGroup column>
|
||||||
|
<Text>{ LocalizeText('catalog.gift_wrapping.receiver') }</Text>
|
||||||
|
<input type="text" className={ 'form-control form-control-sm' + classNames({ ' is-invalid': receiverNotFound }) } value={ receiverName } onChange={ (e) => setReceiverName(e.target.value) } />
|
||||||
|
{ receiverNotFound && <div className="invalid-feedback">{ LocalizeText('catalog.gift_wrapping.receiver_not_found.title') }</div> }
|
||||||
|
</FormGroup>
|
||||||
|
<NitroLayoutGiftCardView figure={ GetSessionDataManager().figure } userName={ GetSessionDataManager().userName } message={ message } editable={ true } onChange={ (value) => setMessage(value) } />
|
||||||
|
<div className="form-check">
|
||||||
|
<input className="form-check-input" type="checkbox" name="showMyFace" checked={ showMyFace } onChange={ (e) => setShowMyFace(value => !value) } />
|
||||||
|
<label className="form-check-label">{ LocalizeText('catalog.gift_wrapping.show_face.title') }</label>
|
||||||
|
</div>
|
||||||
|
<Flex alignItems="center" gap={ 2 }>
|
||||||
|
{ selectedColorId &&
|
||||||
|
<div className="gift-preview">
|
||||||
|
<FurniImageView spriteId={ selectedColorId } type="s" extras={ extraData } />
|
||||||
|
</div> }
|
||||||
|
<Column gap={ 1 }>
|
||||||
|
<Flex gap={ 2 }>
|
||||||
|
<ButtonGroup>
|
||||||
|
<Button variant="primary" onClick={ () => handleAction('prev_box') }>
|
||||||
|
<FontAwesomeIcon icon="chevron-left" />
|
||||||
|
</Button>
|
||||||
|
<Button variant="primary" onClick={ () => handleAction('next_box') }>
|
||||||
|
<FontAwesomeIcon icon="chevron-right" />
|
||||||
|
</Button>
|
||||||
|
</ButtonGroup>
|
||||||
|
<Column gap={ 1 }>
|
||||||
|
<Text fontWeight="bold">{ LocalizeText(boxName) }</Text>
|
||||||
|
<Flex alignItems="center" gap={ 1 }>
|
||||||
|
{ LocalizeText(priceText, ['price'], [giftConfiguration.price.toString()]) }
|
||||||
|
<CurrencyIcon type={ -1 } />
|
||||||
|
</Flex>
|
||||||
|
</Column>
|
||||||
|
</Flex>
|
||||||
|
<Flex alignItems="center" gap={ 2 }>
|
||||||
|
<ButtonGroup>
|
||||||
|
<Button variant="primary" onClick={ () => handleAction('prev_ribbon') }>
|
||||||
|
<FontAwesomeIcon icon="chevron-left" />
|
||||||
|
</Button>
|
||||||
|
<Button variant="primary" onClick={ () => handleAction('next_ribbon') }>
|
||||||
|
<FontAwesomeIcon icon="chevron-right" />
|
||||||
|
</Button>
|
||||||
|
</ButtonGroup>
|
||||||
|
<Text fontWeight="bold">{ LocalizeText(ribbonName) }</Text>
|
||||||
|
</Flex>
|
||||||
|
</Column>
|
||||||
|
</Flex>
|
||||||
|
<Column gap={ 1 }>
|
||||||
|
<Text fontWeight="bold">
|
||||||
|
{ LocalizeText('catalog.gift_wrapping.pick_color') }
|
||||||
|
</Text>
|
||||||
|
<ButtonGroup fullWidth>
|
||||||
|
{ colors.map(color => <Button key={ color.id } variant="dark" size="sm" active={ (color.id === selectedColorId) } disabled={ !isColorable } style={{ backgroundColor: color.color }} onClick={ () => setSelectedColorId(color.id) } />) }
|
||||||
|
</ButtonGroup>
|
||||||
|
</Column>
|
||||||
|
<Flex justifyContent="between" alignItems="center">
|
||||||
|
<Button variant="link" onClick={ close } className="text-black">
|
||||||
|
{ LocalizeText('cancel') }
|
||||||
|
</Button>
|
||||||
|
<Button variant="success" onClick={ () => handleAction('buy') }>
|
||||||
|
{ LocalizeText('catalog.gift_wrapping.give_gift') }
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</NitroCardContentView>
|
||||||
|
</NitroCardView>
|
||||||
|
);
|
||||||
|
};
|
@ -1,12 +1,22 @@
|
|||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { GetCatalogPageComposer, INodeData } from '@nitrots/nitro-renderer';
|
import { GetCatalogPageComposer, INodeData } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback, useEffect, useState } from 'react';
|
import { Dispatch, FC, SetStateAction, useCallback, useEffect, useState } from 'react';
|
||||||
import { SendMessageHook } from '../../../../../hooks/messages/message-event';
|
import { LayoutGridItem } from '../../../../common/layout/LayoutGridItem';
|
||||||
import { NitroCardGridItemView } from '../../../../../layout';
|
import { Text } from '../../../../common/Text';
|
||||||
import { CatalogMode } from '../../../CatalogView.types';
|
import { SendMessageHook } from '../../../../hooks/messages/message-event';
|
||||||
import { CatalogIconView } from '../../catalog-icon/CatalogIconView';
|
import { CatalogMode } from '../../common/CatalogMode';
|
||||||
import { ACTIVE_PAGES } from '../CatalogNavigationView';
|
import { CatalogIconView } from '../catalog-icon/CatalogIconView';
|
||||||
import { CatalogNavigationSetView } from '../set/CatalogNavigationSetView';
|
import { CatalogNavigationSetView } from './CatalogNavigationSetView';
|
||||||
import { CatalogNavigationItemViewProps } from './CatalogNavigationItemView.types';
|
import { ACTIVE_PAGES } from './CatalogNavigationView';
|
||||||
|
|
||||||
|
export interface CatalogNavigationItemViewProps
|
||||||
|
{
|
||||||
|
page: INodeData;
|
||||||
|
isActive: boolean;
|
||||||
|
pendingTree: INodeData[];
|
||||||
|
setPendingTree: Dispatch<SetStateAction<INodeData[]>>;
|
||||||
|
setActiveChild: Dispatch<SetStateAction<INodeData>>;
|
||||||
|
}
|
||||||
|
|
||||||
export const CatalogNavigationItemView: FC<CatalogNavigationItemViewProps> = props =>
|
export const CatalogNavigationItemView: FC<CatalogNavigationItemViewProps> = props =>
|
||||||
{
|
{
|
||||||
@ -72,11 +82,12 @@ export const CatalogNavigationItemView: FC<CatalogNavigationItemViewProps> = pro
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<NitroCardGridItemView itemActive={ isActive } onClick={ event => select(page) }>
|
<LayoutGridItem column={ false } itemActive={ isActive } onClick={ event => select(page) }>
|
||||||
<CatalogIconView icon={ page.icon } />
|
<CatalogIconView icon={ page.icon } />
|
||||||
<div className="flex-grow-1 text-black text-truncate">{ page.localization }</div>
|
<Text grow truncate>{ page.localization }</Text>
|
||||||
{ (page.children.length > 0) && <i className={ 'fas fa-caret-' + (isExpanded ? 'up' : 'down') } /> }
|
{ (page.children.length > 0) &&
|
||||||
</NitroCardGridItemView>
|
<FontAwesomeIcon icon={ isExpanded ? 'caret-up' : 'caret-down' } /> }
|
||||||
|
</LayoutGridItem>
|
||||||
{ isActive && isExpanded && page.children && (page.children.length > 0) &&
|
{ isActive && isExpanded && page.children && (page.children.length > 0) &&
|
||||||
<CatalogNavigationSetView page={ page } pendingTree={ pendingTree } setPendingTree={ setPendingTree } /> }
|
<CatalogNavigationSetView page={ page } pendingTree={ pendingTree } setPendingTree={ setPendingTree } /> }
|
||||||
</>
|
</>
|
@ -1,7 +1,14 @@
|
|||||||
import { INodeData } from '@nitrots/nitro-renderer';
|
import { INodeData } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useEffect, useState } from 'react';
|
import { Dispatch, FC, SetStateAction, useEffect, useState } from 'react';
|
||||||
import { CatalogNavigationItemView } from '../item/CatalogNavigationItemView';
|
import { CatalogNavigationItemView } from './CatalogNavigationItemView';
|
||||||
import { CatalogNavigationSetViewProps } from './CatalogNavigationSetView.types';
|
|
||||||
|
export interface CatalogNavigationSetViewProps
|
||||||
|
{
|
||||||
|
page: INodeData;
|
||||||
|
isFirstSet?: boolean;
|
||||||
|
pendingTree: INodeData[];
|
||||||
|
setPendingTree: Dispatch<SetStateAction<INodeData[]>>;
|
||||||
|
}
|
||||||
|
|
||||||
export const CatalogNavigationSetView: FC<CatalogNavigationSetViewProps> = props =>
|
export const CatalogNavigationSetView: FC<CatalogNavigationSetViewProps> = props =>
|
||||||
{
|
{
|
@ -4,7 +4,7 @@
|
|||||||
background-color: #CDD3D9;
|
background-color: #CDD3D9;
|
||||||
border: 2px solid;
|
border: 2px solid;
|
||||||
|
|
||||||
.grid-item {
|
.layout-grid-item {
|
||||||
font-size: $font-size-sm;
|
font-size: $font-size-sm;
|
||||||
height: 23px !important;
|
height: 23px !important;
|
||||||
border-color: unset !important;
|
border-color: unset !important;
|
||||||
@ -12,7 +12,7 @@
|
|||||||
border: 0 !important;
|
border: 0 !important;
|
||||||
padding: 1px 3px;
|
padding: 1px 3px;
|
||||||
|
|
||||||
i.fas {
|
.svg-inline--fa {
|
||||||
color: $black;
|
color: $black;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
padding: 1px;
|
padding: 1px;
|
@ -1,9 +1,16 @@
|
|||||||
import { INodeData } from '@nitrots/nitro-renderer';
|
import { INodeData } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useEffect } from 'react';
|
import { Dispatch, FC, SetStateAction, useEffect } from 'react';
|
||||||
import { NitroCardGridView, NitroLayoutFlexColumn } from '../../../../layout';
|
import { Column } from '../../../../common/Column';
|
||||||
|
import { Grid } from '../../../../common/Grid';
|
||||||
import { CatalogSearchView } from '../search/CatalogSearchView';
|
import { CatalogSearchView } from '../search/CatalogSearchView';
|
||||||
import { CatalogNavigationViewProps } from './CatalogNavigationView.types';
|
import { CatalogNavigationSetView } from './CatalogNavigationSetView';
|
||||||
import { CatalogNavigationSetView } from './set/CatalogNavigationSetView';
|
|
||||||
|
export interface CatalogNavigationViewProps
|
||||||
|
{
|
||||||
|
page: INodeData;
|
||||||
|
pendingTree: INodeData[];
|
||||||
|
setPendingTree: Dispatch<SetStateAction<INodeData[]>>;
|
||||||
|
}
|
||||||
|
|
||||||
export let ACTIVE_PAGES: INodeData[] = [];
|
export let ACTIVE_PAGES: INodeData[] = [];
|
||||||
|
|
||||||
@ -24,13 +31,13 @@ export const CatalogNavigationView: FC<CatalogNavigationViewProps> = props =>
|
|||||||
}, [ page ]);
|
}, [ page ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NitroLayoutFlexColumn className="h-100" gap={ 2 } overflow="auto">
|
<>
|
||||||
<CatalogSearchView />
|
<CatalogSearchView />
|
||||||
<NitroLayoutFlexColumn className="nitro-catalog-navigation-grid-container p-1 h-100" overflow="hidden">
|
<Column fullHeight className="nitro-catalog-navigation-grid-container p-1" overflow="hidden">
|
||||||
<NitroCardGridView columns={ 1 } gap={ 1 }>
|
<Grid grow columnCount={ 1 } gap={ 1 } overflow="auto">
|
||||||
<CatalogNavigationSetView page={ page } isFirstSet={ true } pendingTree={ pendingTree } setPendingTree={ setPendingTree } />
|
<CatalogNavigationSetView page={ page } isFirstSet={ true } pendingTree={ pendingTree } setPendingTree={ setPendingTree } />
|
||||||
</NitroCardGridView>
|
</Grid>
|
||||||
</NitroLayoutFlexColumn>
|
</Column>
|
||||||
</NitroLayoutFlexColumn>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { CatalogPageMessageOfferData, IObjectData, Vector3d } from '@nitrots/nitro-renderer';
|
import { CatalogPageMessageOfferData, IObjectData, RoomPreviewer, Vector3d } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback, useEffect, useState } from 'react';
|
import { FC, useCallback, useEffect, useState } from 'react';
|
||||||
import { GetAvatarRenderManager, GetFurnitureDataForProductOffer, GetSessionDataManager } from '../../../../api';
|
import { GetAvatarRenderManager, GetFurnitureDataForProductOffer, GetSessionDataManager } from '../../../../api';
|
||||||
import { SetRoomPreviewerStuffDataEvent } from '../../../../events';
|
import { SetRoomPreviewerStuffDataEvent } from '../../../../events';
|
||||||
@ -6,10 +6,14 @@ import { useUiEvent } from '../../../../hooks';
|
|||||||
import { FurniCategory } from '../../common/FurniCategory';
|
import { FurniCategory } from '../../common/FurniCategory';
|
||||||
import { ProductTypeEnum } from '../../common/ProductTypeEnum';
|
import { ProductTypeEnum } from '../../common/ProductTypeEnum';
|
||||||
import { useCatalogContext } from '../../context/CatalogContext';
|
import { useCatalogContext } from '../../context/CatalogContext';
|
||||||
import { CatalogPageViewProps } from './CatalogPageView.types';
|
|
||||||
import { GetCatalogLayout } from './layout/GetCatalogLayout';
|
import { GetCatalogLayout } from './layout/GetCatalogLayout';
|
||||||
import { CatalogLayoutSearchResultView } from './search-result/CatalogLayoutSearchResultView';
|
import { CatalogLayoutSearchResultView } from './search-result/CatalogLayoutSearchResultView';
|
||||||
|
|
||||||
|
export interface CatalogPageViewProps
|
||||||
|
{
|
||||||
|
roomPreviewer: RoomPreviewer;
|
||||||
|
}
|
||||||
|
|
||||||
export const CatalogPageView: FC<CatalogPageViewProps> = props =>
|
export const CatalogPageView: FC<CatalogPageViewProps> = props =>
|
||||||
{
|
{
|
||||||
const { roomPreviewer = null } = props;
|
const { roomPreviewer = null } = props;
|
||||||
@ -22,6 +26,9 @@ export const CatalogPageView: FC<CatalogPageViewProps> = props =>
|
|||||||
if(!offer || !roomPreviewer) return;
|
if(!offer || !roomPreviewer) return;
|
||||||
|
|
||||||
const product = offer.products[0];
|
const product = offer.products[0];
|
||||||
|
|
||||||
|
if(!product) return;
|
||||||
|
|
||||||
const furniData = GetFurnitureDataForProductOffer(product);
|
const furniData = GetFurnitureDataForProductOffer(product);
|
||||||
|
|
||||||
if(!furniData && (product.productType !== ProductTypeEnum.ROBOT)) return;
|
if(!furniData && (product.productType !== ProductTypeEnum.ROBOT)) return;
|
32
src/components/catalog/views/page/layout/CatalogLayout.scss
Normal file
32
src/components/catalog/views/page/layout/CatalogLayout.scss
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
.nitro-catalog-layout-info-loyalty {
|
||||||
|
|
||||||
|
.info-loyalty-content {
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: top right;
|
||||||
|
background-image: url('../../../../../assets/images/catalog/diamond_info_illustration.gif');
|
||||||
|
padding-right:123px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-image {
|
||||||
|
width: 123px;
|
||||||
|
height:350px;
|
||||||
|
background-image: url('../../../../../assets/images/catalog/diamond_info_illustration.gif');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nitro-catalog-layout-vip-buy-grid {
|
||||||
|
|
||||||
|
.layout-grid-item {
|
||||||
|
height: 50px !important;
|
||||||
|
max-height: 50px !important;
|
||||||
|
|
||||||
|
.icon-hc-banner {
|
||||||
|
width: 68px;
|
||||||
|
height: 40px;
|
||||||
|
background: url("../../../../../assets/images/catalog/hc_big.png") center no-repeat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@import './marketplace/marketplace-item/MarketplaceItemView';
|
||||||
|
@import './marketplace/post-offer/MarketplacePostOfferView';
|
@ -1,28 +1,26 @@
|
|||||||
import { StringDataType } from '@nitrots/nitro-renderer';
|
import { StringDataType } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback, useEffect, useState } from 'react';
|
import { FC, useCallback, useEffect, useState } from 'react';
|
||||||
import { LocalizeText } from '../../../../../../api';
|
import { LocalizeText } from '../../../../../../api';
|
||||||
|
import { Column } from '../../../../../../common/Column';
|
||||||
|
import { Grid } from '../../../../../../common/Grid';
|
||||||
|
import { LayoutGridItem } from '../../../../../../common/layout/LayoutGridItem';
|
||||||
|
import { Text } from '../../../../../../common/Text';
|
||||||
import { InventoryBadgesUpdatedEvent, SetRoomPreviewerStuffDataEvent } from '../../../../../../events';
|
import { InventoryBadgesUpdatedEvent, SetRoomPreviewerStuffDataEvent } from '../../../../../../events';
|
||||||
import { InventoryBadgesRequestEvent } from '../../../../../../events/inventory/InventoryBadgesRequestEvent';
|
import { InventoryBadgesRequestEvent } from '../../../../../../events/inventory/InventoryBadgesRequestEvent';
|
||||||
import { dispatchUiEvent, useUiEvent } from '../../../../../../hooks';
|
import { dispatchUiEvent, useUiEvent } from '../../../../../../hooks';
|
||||||
import { NitroLayoutFlexColumn, NitroLayoutGrid, NitroLayoutGridColumn } from '../../../../../../layout';
|
import { BadgeImageView } from '../../../../../../views/shared/badge-image/BadgeImageView';
|
||||||
import { NitroLayoutBase } from '../../../../../../layout/base';
|
|
||||||
import { NitroCardGridItemView } from '../../../../../../layout/card/grid/item/NitroCardGridItemView';
|
|
||||||
import { NitroCardGridView } from '../../../../../../layout/card/grid/NitroCardGridView';
|
|
||||||
import { BadgeImageView } from '../../../../../shared/badge-image/BadgeImageView';
|
|
||||||
import { useCatalogContext } from '../../../../context/CatalogContext';
|
import { useCatalogContext } from '../../../../context/CatalogContext';
|
||||||
import { CatalogPageOffersView } from '../../offers/CatalogPageOffersView';
|
import { CatalogPageOffersView } from '../../offers/CatalogPageOffersView';
|
||||||
import { CatalogProductPreviewView } from '../../product-preview/CatalogProductPreviewView';
|
import { CatalogProductPreviewView } from '../../product-preview/CatalogProductPreviewView';
|
||||||
import { CatalogLayoutBadgeDisplayViewProps } from './CatalogLayoutBadgeDisplayView.types';
|
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
||||||
|
|
||||||
export const CatalogLayoutBadgeDisplayView: FC<CatalogLayoutBadgeDisplayViewProps> = props =>
|
export const CatalogLayoutBadgeDisplayView: FC<CatalogLayoutProps> = props =>
|
||||||
{
|
{
|
||||||
const { roomPreviewer = null, pageParser = null } = props;
|
const { roomPreviewer = null, pageParser = null } = props;
|
||||||
const { catalogState = null, dispatchCatalogState = null } = useCatalogContext();
|
|
||||||
const { activeOffer = null } = catalogState;
|
|
||||||
const [ badges, setBadges ] = useState<string[]>([]);
|
const [ badges, setBadges ] = useState<string[]>([]);
|
||||||
const [ currentBadge, setCurrentBadge ] = useState<string>(null);
|
const [ currentBadge, setCurrentBadge ] = useState<string>(null);
|
||||||
|
const { catalogState = null } = useCatalogContext();
|
||||||
const product = ((activeOffer && activeOffer.products[0]) || null);
|
const { activeOffer = null } = catalogState;
|
||||||
|
|
||||||
const onInventoryBadgesUpdatedEvent = useCallback((event: InventoryBadgesUpdatedEvent) =>
|
const onInventoryBadgesUpdatedEvent = useCallback((event: InventoryBadgesUpdatedEvent) =>
|
||||||
{
|
{
|
||||||
@ -55,26 +53,26 @@ export const CatalogLayoutBadgeDisplayView: FC<CatalogLayoutBadgeDisplayViewProp
|
|||||||
}, [ currentBadge, activeOffer, roomPreviewer ]);
|
}, [ currentBadge, activeOffer, roomPreviewer ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NitroLayoutGrid>
|
<Grid>
|
||||||
<NitroLayoutGridColumn size={ 7 }>
|
<Column size={ 7 } overflow="hidden">
|
||||||
<CatalogPageOffersView className="flex-shrink-0" offers={ pageParser.offers } />
|
<CatalogPageOffersView shrink offers={ pageParser.offers } />
|
||||||
<NitroLayoutFlexColumn gap={ 1 } overflow="hidden">
|
<Column gap={ 1 } overflow="hidden">
|
||||||
<NitroLayoutBase className="flex-shrink-0 fw-bold text-black text-truncate">{ LocalizeText('catalog_selectbadge') }</NitroLayoutBase>
|
<Text truncate shrink fontWeight="bold">{ LocalizeText('catalog_selectbadge') }</Text>
|
||||||
<NitroCardGridView>
|
<Grid grow columnCount={ 5 } overflow="auto">
|
||||||
{ badges && (badges.length > 0) && badges.map(code =>
|
{ badges && (badges.length > 0) && badges.map(code =>
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
<NitroCardGridItemView key={ code } itemActive={ (currentBadge === code) } onMouseDown={ event => setCurrentBadge(code) }>
|
<LayoutGridItem key={ code } itemActive={ (currentBadge === code) } onMouseDown={ event => setCurrentBadge(code) }>
|
||||||
<BadgeImageView badgeCode={ code } />
|
<BadgeImageView badgeCode={ code } />
|
||||||
</NitroCardGridItemView>
|
</LayoutGridItem>
|
||||||
);
|
);
|
||||||
}) }
|
}) }
|
||||||
</NitroCardGridView>
|
</Grid>
|
||||||
</NitroLayoutFlexColumn>
|
</Column>
|
||||||
</NitroLayoutGridColumn>
|
</Column>
|
||||||
<NitroLayoutGridColumn size={ 5 }>
|
<Column size={ 5 } overflow="hidden">
|
||||||
<CatalogProductPreviewView pageParser={ pageParser } activeOffer={ activeOffer } roomPreviewer={ roomPreviewer } extra={ currentBadge } disabled={ !currentBadge } />
|
<CatalogProductPreviewView pageParser={ pageParser } activeOffer={ activeOffer } roomPreviewer={ roomPreviewer } extra={ currentBadge } disabled={ !currentBadge } />
|
||||||
</NitroLayoutGridColumn>
|
</Column>
|
||||||
</NitroLayoutGrid>
|
</Grid>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -1,26 +1,25 @@
|
|||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
import { NitroLayoutGrid, NitroLayoutGridColumn } from '../../../../../../layout';
|
import { Column } from '../../../../../../common/Column';
|
||||||
|
import { Grid } from '../../../../../../common/Grid';
|
||||||
import { useCatalogContext } from '../../../../context/CatalogContext';
|
import { useCatalogContext } from '../../../../context/CatalogContext';
|
||||||
import { CatalogPageOffersView } from '../../offers/CatalogPageOffersView';
|
import { CatalogPageOffersView } from '../../offers/CatalogPageOffersView';
|
||||||
import { CatalogProductPreviewView } from '../../product-preview/CatalogProductPreviewView';
|
import { CatalogProductPreviewView } from '../../product-preview/CatalogProductPreviewView';
|
||||||
import { CatalogLayoutDefaultViewProps } from './CatalogLayoutDefaultView.types';
|
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
||||||
|
|
||||||
export const CatalogLayoutDefaultView: FC<CatalogLayoutDefaultViewProps> = props =>
|
export const CatalogLayoutDefaultView: FC<CatalogLayoutProps> = props =>
|
||||||
{
|
{
|
||||||
const { roomPreviewer = null, pageParser = null } = props;
|
const { roomPreviewer = null, pageParser = null } = props;
|
||||||
const { catalogState = null } = useCatalogContext();
|
const { catalogState = null } = useCatalogContext();
|
||||||
const { activeOffer = null } = catalogState;
|
const { activeOffer = null } = catalogState;
|
||||||
|
|
||||||
const product = ((activeOffer && activeOffer.products[0]) || null);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NitroLayoutGrid>
|
<Grid>
|
||||||
<NitroLayoutGridColumn size={ 7 }>
|
<Column size={ 7 } overflow="hidden">
|
||||||
<CatalogPageOffersView offers={ pageParser.offers } />
|
<CatalogPageOffersView offers={ pageParser.offers } />
|
||||||
</NitroLayoutGridColumn>
|
</Column>
|
||||||
<NitroLayoutGridColumn size={ 5 }>
|
<Column size={ 5 } overflow="hidden">
|
||||||
<CatalogProductPreviewView pageParser={ pageParser } activeOffer={ activeOffer } roomPreviewer={ roomPreviewer } />
|
<CatalogProductPreviewView pageParser={ pageParser } activeOffer={ activeOffer } roomPreviewer={ roomPreviewer } />
|
||||||
</NitroLayoutGridColumn>
|
</Column>
|
||||||
</NitroLayoutGrid>
|
</Grid>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
import { FrontPageItem } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC, useMemo } from 'react';
|
||||||
|
import { GetConfiguration } from '../../../../../../api';
|
||||||
|
import { LayoutImage, LayoutImageProps } from '../../../../../../common/layout/LayoutImage';
|
||||||
|
import { Text } from '../../../../../../common/Text';
|
||||||
|
|
||||||
|
export interface CatalogLayoutFrontPageItemViewProps extends LayoutImageProps
|
||||||
|
{
|
||||||
|
item: FrontPageItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CatalogLayoutFrontPageItemView: FC<CatalogLayoutFrontPageItemViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { item = null, position = 'relative', pointer = true, overflow = 'hidden', fullHeight = true, classNames = [], children = null, ...rest } = props;
|
||||||
|
|
||||||
|
const getClassNames = useMemo(() =>
|
||||||
|
{
|
||||||
|
const newClassNames: string[] = [ 'rounded', 'nitro-front-page-item' ];
|
||||||
|
|
||||||
|
if(classNames.length) newClassNames.push(...classNames);
|
||||||
|
|
||||||
|
return newClassNames;
|
||||||
|
}, [ classNames ]);
|
||||||
|
|
||||||
|
if(!item) return null;
|
||||||
|
|
||||||
|
const imageUrl = (GetConfiguration<string>('image.library.url') + item.itemPromoImage);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LayoutImage imageUrl={ imageUrl } classNames={ getClassNames } position={ position } fullHeight={ fullHeight } pointer={ pointer } overflow={ overflow } { ...rest }>
|
||||||
|
<Text position="absolute" variant="white" className="bg-dark rounded p-2 m-2 bottom-0">
|
||||||
|
{ item.itemName }
|
||||||
|
</Text>
|
||||||
|
{ children }
|
||||||
|
</LayoutImage>
|
||||||
|
);
|
||||||
|
}
|
@ -1,22 +1,17 @@
|
|||||||
import { FrontPageItem } from '@nitrots/nitro-renderer';
|
import { FrontPageItem } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback, useMemo } from 'react';
|
import { FC, useCallback } from 'react';
|
||||||
import { CreateLinkEvent, GetConfiguration } from '../../../../../../api';
|
import { CreateLinkEvent } from '../../../../../../api';
|
||||||
import { NitroLayoutGrid, NitroLayoutGridColumn } from '../../../../../../layout';
|
import { Column } from '../../../../../../common/Column';
|
||||||
|
import { Grid } from '../../../../../../common/Grid';
|
||||||
import { GetCatalogPageText } from '../../../../common/CatalogUtilities';
|
import { GetCatalogPageText } from '../../../../common/CatalogUtilities';
|
||||||
import { CatalogRedeemVoucherView } from '../../redeem-voucher/CatalogRedeemVoucherView';
|
import { CatalogRedeemVoucherView } from '../../redeem-voucher/CatalogRedeemVoucherView';
|
||||||
import { CatalogLayoutFrontpage4ViewProps } from './CatalogLayoutFrontpage4View.types';
|
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
||||||
import { CatalogLayoutFrontPageItemView } from './item/CatalogLayoutFrontPageItemView';
|
import { CatalogLayoutFrontPageItemView } from './CatalogLayoutFrontPageItemView';
|
||||||
|
|
||||||
export const CatalogLayoutFrontpage4View: FC<CatalogLayoutFrontpage4ViewProps> = props =>
|
export const CatalogLayoutFrontpage4View: FC<CatalogLayoutProps> = props =>
|
||||||
{
|
{
|
||||||
const { pageParser = null } = props;
|
const { pageParser = null } = props;
|
||||||
|
|
||||||
const imageLibraryUrl = useMemo(() =>
|
|
||||||
{
|
|
||||||
console.log(pageParser);
|
|
||||||
return GetConfiguration<string>('image.library.url');
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const selectItem = useCallback((item: FrontPageItem) =>
|
const selectItem = useCallback((item: FrontPageItem) =>
|
||||||
{
|
{
|
||||||
switch(item.type)
|
switch(item.type)
|
||||||
@ -30,15 +25,13 @@ export const CatalogLayoutFrontpage4View: FC<CatalogLayoutFrontpage4ViewProps> =
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if(!pageParser) return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NitroLayoutGrid>
|
<Grid>
|
||||||
<NitroLayoutGridColumn size={ 4 }>
|
<Column size={ 4 }>
|
||||||
{ pageParser.frontPageItems[0] &&
|
{ pageParser.frontPageItems[0] &&
|
||||||
<CatalogLayoutFrontPageItemView item={ pageParser.frontPageItems[0] } onClick={ event => selectItem(pageParser.frontPageItems[0]) } /> }
|
<CatalogLayoutFrontPageItemView item={ pageParser.frontPageItems[0] } onClick={ event => selectItem(pageParser.frontPageItems[0]) } /> }
|
||||||
</NitroLayoutGridColumn>
|
</Column>
|
||||||
<NitroLayoutGridColumn size={ 8 }>
|
<Column size={ 8 }>
|
||||||
{ pageParser.frontPageItems[1] &&
|
{ pageParser.frontPageItems[1] &&
|
||||||
<CatalogLayoutFrontPageItemView item={ pageParser.frontPageItems[1] } onClick={ event => selectItem(pageParser.frontPageItems[1]) } /> }
|
<CatalogLayoutFrontPageItemView item={ pageParser.frontPageItems[1] } onClick={ event => selectItem(pageParser.frontPageItems[1]) } /> }
|
||||||
{ pageParser.frontPageItems[2] &&
|
{ pageParser.frontPageItems[2] &&
|
||||||
@ -46,7 +39,7 @@ export const CatalogLayoutFrontpage4View: FC<CatalogLayoutFrontpage4ViewProps> =
|
|||||||
{ pageParser.frontPageItems[3] &&
|
{ pageParser.frontPageItems[3] &&
|
||||||
<CatalogLayoutFrontPageItemView item={ pageParser.frontPageItems[3] } onClick={ event => selectItem(pageParser.frontPageItems[3]) } /> }
|
<CatalogLayoutFrontPageItemView item={ pageParser.frontPageItems[3] } onClick={ event => selectItem(pageParser.frontPageItems[3]) } /> }
|
||||||
<CatalogRedeemVoucherView text={ GetCatalogPageText(pageParser, 1) } />
|
<CatalogRedeemVoucherView text={ GetCatalogPageText(pageParser, 1) } />
|
||||||
</NitroLayoutGridColumn>
|
</Column>
|
||||||
</NitroLayoutGrid>
|
</Grid>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -1,16 +1,17 @@
|
|||||||
import { CatalogGroupsComposer, StringDataType } from '@nitrots/nitro-renderer';
|
import { CatalogGroupsComposer, StringDataType } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useEffect, useMemo, useState } from 'react';
|
import { FC, useEffect, useMemo, useState } from 'react';
|
||||||
|
import { Column } from '../../../../../../common/Column';
|
||||||
|
import { Grid } from '../../../../../../common/Grid';
|
||||||
import { SetRoomPreviewerStuffDataEvent } from '../../../../../../events';
|
import { SetRoomPreviewerStuffDataEvent } from '../../../../../../events';
|
||||||
import { dispatchUiEvent } from '../../../../../../hooks';
|
import { dispatchUiEvent } from '../../../../../../hooks';
|
||||||
import { SendMessageHook } from '../../../../../../hooks/messages';
|
import { SendMessageHook } from '../../../../../../hooks/messages';
|
||||||
import { NitroLayoutGrid, NitroLayoutGridColumn } from '../../../../../../layout';
|
|
||||||
import { useCatalogContext } from '../../../../context/CatalogContext';
|
import { useCatalogContext } from '../../../../context/CatalogContext';
|
||||||
import { CatalogSelectGroupView } from '../../../select-group/CatalogSelectGroupView';
|
import { CatalogSelectGroupView } from '../../../select-group/CatalogSelectGroupView';
|
||||||
import { CatalogPageOffersView } from '../../offers/CatalogPageOffersView';
|
import { CatalogPageOffersView } from '../../offers/CatalogPageOffersView';
|
||||||
import { CatalogProductPreviewView } from '../../product-preview/CatalogProductPreviewView';
|
import { CatalogProductPreviewView } from '../../product-preview/CatalogProductPreviewView';
|
||||||
import { CatalogLayoutGuildCustomFurniViewProps } from './CatalogLayoutGuildCustomFurniView.types';
|
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
||||||
|
|
||||||
export const CatalogLayouGuildCustomFurniView: FC<CatalogLayoutGuildCustomFurniViewProps> = props =>
|
export const CatalogLayouGuildCustomFurniView: FC<CatalogLayoutProps> = props =>
|
||||||
{
|
{
|
||||||
const { roomPreviewer = null, pageParser = null } = props;
|
const { roomPreviewer = null, pageParser = null } = props;
|
||||||
const [ selectedGroupIndex, setSelectedGroupIndex ] = useState<number>(0);
|
const [ selectedGroupIndex, setSelectedGroupIndex ] = useState<number>(0);
|
||||||
@ -49,15 +50,15 @@ export const CatalogLayouGuildCustomFurniView: FC<CatalogLayoutGuildCustomFurniV
|
|||||||
if(!groups) return null;
|
if(!groups) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NitroLayoutGrid>
|
<Grid>
|
||||||
<NitroLayoutGridColumn size={ 7 }>
|
<Column size={ 7 } overflow="hidden">
|
||||||
<CatalogPageOffersView offers={ pageParser.offers } />
|
<CatalogPageOffersView offers={ pageParser.offers } />
|
||||||
</NitroLayoutGridColumn>
|
</Column>
|
||||||
<NitroLayoutGridColumn size={ 5 }>
|
<Column size={ 5 } overflow="hidden">
|
||||||
<CatalogProductPreviewView pageParser={ pageParser } activeOffer={ activeOffer } roomPreviewer={ roomPreviewer } badgeCode={ ((selectedGroup && selectedGroup.badgeCode) || null) } extra={ groups[selectedGroupIndex] ? groups[selectedGroupIndex].groupId.toString() : '' } disabled={ !(!!groups[selectedGroupIndex]) }>
|
<CatalogProductPreviewView pageParser={ pageParser } activeOffer={ activeOffer } roomPreviewer={ roomPreviewer } badgeCode={ ((selectedGroup && selectedGroup.badgeCode) || null) } extra={ groups[selectedGroupIndex] ? groups[selectedGroupIndex].groupId.toString() : '' } disabled={ !(!!groups[selectedGroupIndex]) }>
|
||||||
<CatalogSelectGroupView selectedGroupIndex={ selectedGroupIndex } setSelectedGroupIndex={ setSelectedGroupIndex } />
|
<CatalogSelectGroupView selectedGroupIndex={ selectedGroupIndex } setSelectedGroupIndex={ setSelectedGroupIndex } />
|
||||||
</CatalogProductPreviewView>
|
</CatalogProductPreviewView>
|
||||||
</NitroLayoutGridColumn>
|
</Column>
|
||||||
</NitroLayoutGrid>
|
</Grid>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -1,26 +1,23 @@
|
|||||||
import { CatalogGroupsComposer } from '@nitrots/nitro-renderer';
|
import { CatalogGroupsComposer } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useEffect, useState } from 'react';
|
import { FC, useEffect, useState } from 'react';
|
||||||
|
import { Base } from '../../../../../../common/Base';
|
||||||
|
import { Column } from '../../../../../../common/Column';
|
||||||
|
import { Grid } from '../../../../../../common/Grid';
|
||||||
import { SendMessageHook } from '../../../../../../hooks/messages';
|
import { SendMessageHook } from '../../../../../../hooks/messages';
|
||||||
import { NitroLayoutGrid, NitroLayoutGridColumn } from '../../../../../../layout';
|
|
||||||
import { NitroLayoutBase } from '../../../../../../layout/base';
|
|
||||||
import { GetCatalogPageText } from '../../../../common/CatalogUtilities';
|
import { GetCatalogPageText } from '../../../../common/CatalogUtilities';
|
||||||
import { useCatalogContext } from '../../../../context/CatalogContext';
|
import { useCatalogContext } from '../../../../context/CatalogContext';
|
||||||
import { CatalogActions } from '../../../../reducers/CatalogReducer';
|
import { CatalogActions } from '../../../../reducers/CatalogReducer';
|
||||||
import { CatalogSelectGroupView } from '../../../select-group/CatalogSelectGroupView';
|
import { CatalogSelectGroupView } from '../../../select-group/CatalogSelectGroupView';
|
||||||
import { CatalogProductPreviewView } from '../../product-preview/CatalogProductPreviewView';
|
import { CatalogProductPreviewView } from '../../product-preview/CatalogProductPreviewView';
|
||||||
import { CatalogLayoutGuildForumViewProps } from './CatalogLayoutGuildForumView.types';
|
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
||||||
|
|
||||||
export const CatalogLayouGuildForumView: FC<CatalogLayoutGuildForumViewProps> = props =>
|
export const CatalogLayouGuildForumView: FC<CatalogLayoutProps> = props =>
|
||||||
{
|
{
|
||||||
const { pageParser = null } = props;
|
const { pageParser = null } = props;
|
||||||
|
const [ selectedGroupIndex, setSelectedGroupIndex ] = useState<number>(0);
|
||||||
const { catalogState = null, dispatchCatalogState = null } = useCatalogContext();
|
const { catalogState = null, dispatchCatalogState = null } = useCatalogContext();
|
||||||
const { activeOffer = null, groups = null } = catalogState;
|
const { activeOffer = null, groups = null } = catalogState;
|
||||||
|
|
||||||
const [ selectedGroupIndex, setSelectedGroupIndex ] = useState<number>(0);
|
|
||||||
|
|
||||||
const product = ((activeOffer && activeOffer.products[0]) || null);
|
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
SendMessageHook(new CatalogGroupsComposer());
|
SendMessageHook(new CatalogGroupsComposer());
|
||||||
@ -37,15 +34,15 @@ export const CatalogLayouGuildForumView: FC<CatalogLayoutGuildForumViewProps> =
|
|||||||
}, [ dispatchCatalogState, pageParser ]);
|
}, [ dispatchCatalogState, pageParser ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NitroLayoutGrid>
|
<Grid>
|
||||||
<NitroLayoutGridColumn className="bg-muted rounded p-2 text-black overflow-hidden" size={ 7 }>
|
<Column className="bg-muted rounded p-2 text-black" size={ 7 } overflow="hidden">
|
||||||
<NitroLayoutBase className="overflow-auto" dangerouslySetInnerHTML={ { __html: GetCatalogPageText(pageParser, 1) } } />
|
<Base className="overflow-auto" dangerouslySetInnerHTML={ { __html: GetCatalogPageText(pageParser, 1) } } />
|
||||||
</NitroLayoutGridColumn>
|
</Column>
|
||||||
<NitroLayoutGridColumn size={ 5 }>
|
<Column size={ 5 } overflow="hidden">
|
||||||
<CatalogProductPreviewView pageParser={ pageParser } activeOffer={ activeOffer } roomPreviewer={ null } extra={ groups[selectedGroupIndex] ? groups[selectedGroupIndex].groupId.toString() : '' } disabled={ !(!!groups[selectedGroupIndex]) }>
|
<CatalogProductPreviewView pageParser={ pageParser } activeOffer={ activeOffer } roomPreviewer={ null } extra={ groups[selectedGroupIndex] ? groups[selectedGroupIndex].groupId.toString() : '' } disabled={ !(!!groups[selectedGroupIndex]) }>
|
||||||
<CatalogSelectGroupView selectedGroupIndex={ selectedGroupIndex } setSelectedGroupIndex={ setSelectedGroupIndex } />
|
<CatalogSelectGroupView selectedGroupIndex={ selectedGroupIndex } setSelectedGroupIndex={ setSelectedGroupIndex } />
|
||||||
</CatalogProductPreviewView>
|
</CatalogProductPreviewView>
|
||||||
</NitroLayoutGridColumn>
|
</Column>
|
||||||
</NitroLayoutGrid>
|
</Grid>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
import { FC } from 'react';
|
||||||
|
import { CreateLinkEvent, LocalizeText } from '../../../../../../api';
|
||||||
|
import { Base } from '../../../../../../common/Base';
|
||||||
|
import { Button } from '../../../../../../common/Button';
|
||||||
|
import { Column } from '../../../../../../common/Column';
|
||||||
|
import { Grid } from '../../../../../../common/Grid';
|
||||||
|
import { LayoutImage } from '../../../../../../common/layout/LayoutImage';
|
||||||
|
import { GetCatalogPageImage, GetCatalogPageText } from '../../../../common/CatalogUtilities';
|
||||||
|
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
||||||
|
|
||||||
|
export const CatalogLayouGuildFrontpageView: FC<CatalogLayoutProps> = props =>
|
||||||
|
{
|
||||||
|
const { pageParser = null } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid>
|
||||||
|
<Column size={ 7 } overflow="hidden" className="bg-muted rounded p-2 text-black">
|
||||||
|
<Base dangerouslySetInnerHTML={ { __html: GetCatalogPageText(pageParser, 2) } } />
|
||||||
|
<Base overflow="auto" dangerouslySetInnerHTML={ { __html: GetCatalogPageText(pageParser, 0) } } />
|
||||||
|
<Base dangerouslySetInnerHTML={ { __html: GetCatalogPageText(pageParser, 1) } } />
|
||||||
|
</Column>
|
||||||
|
<Column center size={ 5 } overflow="hidden">
|
||||||
|
<LayoutImage imageUrl={ GetCatalogPageImage(pageParser, 1) } />
|
||||||
|
<Button onClick={ () => CreateLinkEvent('groups/create') }>
|
||||||
|
{ LocalizeText('catalog.start.guild.purchase.button') }
|
||||||
|
</Button>
|
||||||
|
</Column>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
import { GetCatalogPageText } from '../../../../common/CatalogUtilities';
|
import { GetCatalogPageText } from '../../../../common/CatalogUtilities';
|
||||||
import { CatalogLayoutInfoLoyaltyViewProps } from './CatalogLayoutInfoLoyaltyView.types';
|
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
||||||
|
|
||||||
export const CatalogLayoutInfoLoyaltyView: FC<CatalogLayoutInfoLoyaltyViewProps> = props =>
|
export const CatalogLayoutInfoLoyaltyView: FC<CatalogLayoutProps> = props =>
|
||||||
{
|
{
|
||||||
const { pageParser = null } = props;
|
const { pageParser = null } = props;
|
||||||
|
|
@ -4,8 +4,8 @@ import { LocalizeText } from '../../../../../../../api';
|
|||||||
import { BatchUpdates, CreateMessageHook, SendMessageHook, UseMountEffect } from '../../../../../../../hooks';
|
import { BatchUpdates, CreateMessageHook, SendMessageHook, UseMountEffect } from '../../../../../../../hooks';
|
||||||
import { NitroCardGridView } from '../../../../../../../layout';
|
import { NitroCardGridView } from '../../../../../../../layout';
|
||||||
import { NitroLayoutBase } from '../../../../../../../layout/base';
|
import { NitroLayoutBase } from '../../../../../../../layout/base';
|
||||||
import { NotificationAlertType } from '../../../../../../notification-center/common/NotificationAlertType';
|
import { NotificationAlertType } from '../../../../../../../views/notification-center/common/NotificationAlertType';
|
||||||
import { NotificationUtilities } from '../../../../../../notification-center/common/NotificationUtilities';
|
import { NotificationUtilities } from '../../../../../../../views/notification-center/common/NotificationUtilities';
|
||||||
import { CatalogLayoutProps } from '../../CatalogLayout.types';
|
import { CatalogLayoutProps } from '../../CatalogLayout.types';
|
||||||
import { MarketplaceOfferData } from '../common/MarketplaceOfferData';
|
import { MarketplaceOfferData } from '../common/MarketplaceOfferData';
|
||||||
import { MarketPlaceOfferState } from '../common/MarketplaceOfferState';
|
import { MarketPlaceOfferState } from '../common/MarketplaceOfferState';
|
@ -1,11 +1,11 @@
|
|||||||
import { ImageResult, MakeOfferMessageComposer, Vector3d } from '@nitrots/nitro-renderer';
|
import { ImageResult, MakeOfferMessageComposer, Vector3d } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback, useState } from 'react';
|
import { FC, useCallback, useState } from 'react';
|
||||||
import { GetRoomEngine, LocalizeText } from '../../../../../../../api';
|
import { GetRoomEngine, LocalizeText } from '../../../../../../../api';
|
||||||
import { FurnitureItem } from '../../../../../../../components/inventory/common/FurnitureItem';
|
|
||||||
import { CatalogPostMarketplaceOfferEvent } from '../../../../../../../events/catalog/CatalogPostMarketplaceOfferEvent';
|
import { CatalogPostMarketplaceOfferEvent } from '../../../../../../../events/catalog/CatalogPostMarketplaceOfferEvent';
|
||||||
import { SendMessageHook, useUiEvent } from '../../../../../../../hooks';
|
import { SendMessageHook, useUiEvent } from '../../../../../../../hooks';
|
||||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView, NitroLayoutFlex } from '../../../../../../../layout';
|
import { NitroCardContentView, NitroCardHeaderView, NitroCardView, NitroLayoutFlex } from '../../../../../../../layout';
|
||||||
import { NotificationUtilities } from '../../../../../../notification-center/common/NotificationUtilities';
|
import { NotificationUtilities } from '../../../../../../../views/notification-center/common/NotificationUtilities';
|
||||||
|
import { FurnitureItem } from '../../../../../../inventory/common/FurnitureItem';
|
||||||
|
|
||||||
export const MarketplacePostOfferView : FC<{}> = props =>
|
export const MarketplacePostOfferView : FC<{}> = props =>
|
||||||
{
|
{
|
@ -3,9 +3,9 @@ import { FC, useCallback, useMemo, useState } from 'react';
|
|||||||
import { LocalizeText } from '../../../../../../../api';
|
import { LocalizeText } from '../../../../../../../api';
|
||||||
import { BatchUpdates, CreateMessageHook, SendMessageHook } from '../../../../../../../hooks';
|
import { BatchUpdates, CreateMessageHook, SendMessageHook } from '../../../../../../../hooks';
|
||||||
import { NitroCardGridView } from '../../../../../../../layout';
|
import { NitroCardGridView } from '../../../../../../../layout';
|
||||||
import { NotificationAlertType } from '../../../../../../notification-center/common/NotificationAlertType';
|
import { NotificationAlertType } from '../../../../../../../views/notification-center/common/NotificationAlertType';
|
||||||
import { NotificationUtilities } from '../../../../../../notification-center/common/NotificationUtilities';
|
import { NotificationUtilities } from '../../../../../../../views/notification-center/common/NotificationUtilities';
|
||||||
import { GetCurrencyAmount } from '../../../../../../purse/common/CurrencyHelper';
|
import { GetCurrencyAmount } from '../../../../../../../views/purse/common/CurrencyHelper';
|
||||||
import { CatalogLayoutProps } from '../../CatalogLayout.types';
|
import { CatalogLayoutProps } from '../../CatalogLayout.types';
|
||||||
import { IMarketplaceSearchOptions } from '../common/IMarketplaceSearchOptions';
|
import { IMarketplaceSearchOptions } from '../common/IMarketplaceSearchOptions';
|
||||||
import { MarketplaceOfferData } from '../common/MarketplaceOfferData';
|
import { MarketplaceOfferData } from '../common/MarketplaceOfferData';
|
@ -0,0 +1,75 @@
|
|||||||
|
import { ApproveNameMessageComposer, CatalogPageMessageOfferData } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC, useCallback, useState } from 'react';
|
||||||
|
import { LocalizeText } from '../../../../../../api';
|
||||||
|
import { Column } from '../../../../../../common/Column';
|
||||||
|
import { Flex } from '../../../../../../common/Flex';
|
||||||
|
import { Text } from '../../../../../../common/Text';
|
||||||
|
import { CatalogEvent } from '../../../../../../events';
|
||||||
|
import { useUiEvent } from '../../../../../../hooks/events/ui/ui-event';
|
||||||
|
import { SendMessageHook } from '../../../../../../hooks/messages/message-event';
|
||||||
|
import { CurrencyIcon } from '../../../../../../views/shared/currency-icon/CurrencyIcon';
|
||||||
|
import { CatalogPurchaseButtonView } from '../../purchase/CatalogPurchaseButtonView';
|
||||||
|
import { CatalogPurchaseGiftButtonView } from '../../purchase/CatalogPurchaseGiftButtonView';
|
||||||
|
import { CatalogPetNameApprovalView } from './CatalogPetNameApprovalView';
|
||||||
|
|
||||||
|
export interface CatalogLayoutPetPurchaseViewProps
|
||||||
|
{
|
||||||
|
offer: CatalogPageMessageOfferData;
|
||||||
|
pageId: number;
|
||||||
|
extra?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CatalogLayoutPetPurchaseView: FC<CatalogLayoutPetPurchaseViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { offer = null, pageId = -1, extra = '' } = props;
|
||||||
|
const [ petNameValue, setPetNameValue ] = useState('');
|
||||||
|
const [ nameApproved, setNameApproved ] = useState(false);
|
||||||
|
|
||||||
|
const onCatalogEvent = useCallback((event: CatalogEvent) =>
|
||||||
|
{
|
||||||
|
switch(event.type)
|
||||||
|
{
|
||||||
|
case CatalogEvent.PURCHASE_SUCCESS:
|
||||||
|
setNameApproved(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useUiEvent(CatalogEvent.PURCHASE_SUCCESS, onCatalogEvent);
|
||||||
|
|
||||||
|
const beforePurchase = useCallback(() =>
|
||||||
|
{
|
||||||
|
SendMessageHook(new ApproveNameMessageComposer(petNameValue, 1));
|
||||||
|
}, [ petNameValue ]);
|
||||||
|
|
||||||
|
const extraData = `${ petNameValue }\n${ extra }`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Column fullWidth grow justifyContent="end">
|
||||||
|
<div className="d-flex flex-grow-1 justify-content-center align-items-center">
|
||||||
|
<CatalogPetNameApprovalView petNameValue={ petNameValue } setPetNameValue={ setPetNameValue } nameApproved={ nameApproved } setNameApproved={ setNameApproved } />
|
||||||
|
</div>
|
||||||
|
<Flex alignItems="end">
|
||||||
|
<div className="flex-grow-1 align-items-end">
|
||||||
|
<Text>{ LocalizeText('catalog.bundlewidget.price') }</Text>
|
||||||
|
</div>
|
||||||
|
<Column>
|
||||||
|
{ (offer.priceCredits > 0) &&
|
||||||
|
<Flex alignItems="center" justifyContent="end" gap={ 1 }>
|
||||||
|
<Text>{ offer.priceCredits }</Text>
|
||||||
|
<CurrencyIcon type={ -1 } />
|
||||||
|
</Flex> }
|
||||||
|
{ (offer.priceActivityPoints > 0) &&
|
||||||
|
<Flex alignItems="center" justifyContent="end" gap={ 1 }>
|
||||||
|
<Text>{ offer.priceActivityPoints }</Text>
|
||||||
|
<CurrencyIcon type={ offer.priceActivityPointsType } />
|
||||||
|
</Flex> }
|
||||||
|
</Column>
|
||||||
|
</Flex>
|
||||||
|
<Column gap={ 1 }>
|
||||||
|
<CatalogPurchaseButtonView className="btn-sm w-100" offer={ offer } pageId={ pageId } extra={ extraData } quantity={ 1 } isPurchaseAllowed={ nameApproved } beforePurchase={ beforePurchase } />
|
||||||
|
{ offer.giftable && <CatalogPurchaseGiftButtonView className="btn-sm w-100 mt-1" offer={ offer } pageId={ pageId } extra={ extraData } disabled={ nameApproved } /> }
|
||||||
|
</Column>
|
||||||
|
</Column>
|
||||||
|
);
|
||||||
|
}
|
@ -1,89 +1,35 @@
|
|||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { ColorConverter, GetSellablePetPalettesComposer, SellablePetPaletteData } from '@nitrots/nitro-renderer';
|
import { ColorConverter, GetSellablePetPalettesComposer, SellablePetPaletteData } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useEffect, useMemo, useState } from 'react';
|
import { FC, useEffect, useMemo, useState } from 'react';
|
||||||
import { GetProductDataForLocalization, LocalizeText } from '../../../../../../api';
|
import { GetProductDataForLocalization, LocalizeText } from '../../../../../../api';
|
||||||
|
import { Base } from '../../../../../../common/Base';
|
||||||
|
import { Button } from '../../../../../../common/Button';
|
||||||
|
import { Column } from '../../../../../../common/Column';
|
||||||
|
import { Grid } from '../../../../../../common/Grid';
|
||||||
|
import { LayoutGridItem } from '../../../../../../common/layout/LayoutGridItem';
|
||||||
|
import { Text } from '../../../../../../common/Text';
|
||||||
|
import { BatchUpdates } from '../../../../../../hooks';
|
||||||
import { SendMessageHook } from '../../../../../../hooks/messages/message-event';
|
import { SendMessageHook } from '../../../../../../hooks/messages/message-event';
|
||||||
import { NitroCardGridItemView, NitroCardGridView, NitroLayoutFlexColumn, NitroLayoutGrid, NitroLayoutGridColumn } from '../../../../../../layout';
|
import { PetImageView } from '../../../../../../views/shared/pet-image/PetImageView';
|
||||||
import { NitroLayoutBase } from '../../../../../../layout/base';
|
|
||||||
import { PetImageView } from '../../../../../shared/pet-image/PetImageView';
|
|
||||||
import { GetPetAvailableColors, GetPetIndexFromLocalization } from '../../../../common/CatalogUtilities';
|
import { GetPetAvailableColors, GetPetIndexFromLocalization } from '../../../../common/CatalogUtilities';
|
||||||
import { useCatalogContext } from '../../../../context/CatalogContext';
|
import { useCatalogContext } from '../../../../context/CatalogContext';
|
||||||
import { CatalogActions } from '../../../../reducers/CatalogReducer';
|
import { CatalogActions } from '../../../../reducers/CatalogReducer';
|
||||||
import { CatalogRoomPreviewerView } from '../../../catalog-room-previewer/CatalogRoomPreviewerView';
|
import { CatalogRoomPreviewerView } from '../../../catalog-room-previewer/CatalogRoomPreviewerView';
|
||||||
import { CatalogPageDetailsView } from '../../../page-details/CatalogPageDetailsView';
|
import { CatalogPageDetailsView } from '../../../page-details/CatalogPageDetailsView';
|
||||||
import { CatalogLayoutPetViewProps } from './CatalogLayoutPetView.types';
|
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
||||||
import { CatalogLayoutPetPurchaseView } from './purchase/CatalogLayoutPetPurchaseView';
|
import { CatalogLayoutPetPurchaseView } from './CatalogLayoutPetPurchaseView';
|
||||||
|
|
||||||
export const CatalogLayoutPetView: FC<CatalogLayoutPetViewProps> = props =>
|
export const CatalogLayoutPetView: FC<CatalogLayoutProps> = props =>
|
||||||
{
|
{
|
||||||
const { roomPreviewer = null, pageParser = null } = props;
|
const { roomPreviewer = null, pageParser = null } = props;
|
||||||
const { catalogState = null, dispatchCatalogState = null } = useCatalogContext();
|
|
||||||
const { activeOffer = null, petPalettes = [] } = catalogState;
|
|
||||||
const [ petIndex, setPetIndex ] = useState(-1);
|
const [ petIndex, setPetIndex ] = useState(-1);
|
||||||
const [ sellablePalettes, setSellablePalettes ] = useState<SellablePetPaletteData[]>([]);
|
const [ sellablePalettes, setSellablePalettes ] = useState<SellablePetPaletteData[]>([]);
|
||||||
const [ selectedPaletteIndex, setSelectedPaletteIndex ] = useState(-1);
|
const [ selectedPaletteIndex, setSelectedPaletteIndex ] = useState(-1);
|
||||||
const [ sellableColors, setSellableColors ] = useState<number[][]>([]);
|
const [ sellableColors, setSellableColors ] = useState<number[][]>([]);
|
||||||
const [ selectedColorIndex, setSelectedColorIndex ] = useState(-1);
|
const [ selectedColorIndex, setSelectedColorIndex ] = useState(-1);
|
||||||
const [ colorsShowing, setColorsShowing ] = useState(false);
|
const [ colorsShowing, setColorsShowing ] = useState(false);
|
||||||
|
const { catalogState = null, dispatchCatalogState = null } = useCatalogContext();
|
||||||
useEffect(() =>
|
const { activeOffer = null, petPalettes = [] } = catalogState;
|
||||||
{
|
|
||||||
if(!pageParser || !pageParser.offers.length) return;
|
|
||||||
|
|
||||||
const offer = pageParser.offers[0];
|
|
||||||
|
|
||||||
dispatchCatalogState({
|
|
||||||
type: CatalogActions.SET_CATALOG_ACTIVE_OFFER,
|
|
||||||
payload: {
|
|
||||||
activeOffer: offer
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setPetIndex(GetPetIndexFromLocalization(offer.localizationId));
|
|
||||||
setColorsShowing(false);
|
|
||||||
}, [ pageParser, dispatchCatalogState ]);
|
|
||||||
|
|
||||||
useEffect(() =>
|
|
||||||
{
|
|
||||||
if(!activeOffer) return;
|
|
||||||
|
|
||||||
const productData = GetProductDataForLocalization(activeOffer.localizationId);
|
|
||||||
|
|
||||||
if(!productData) return;
|
|
||||||
|
|
||||||
for(const paletteData of petPalettes)
|
|
||||||
{
|
|
||||||
if(paletteData.breed !== productData.type) continue;
|
|
||||||
|
|
||||||
const palettes: SellablePetPaletteData[] = [];
|
|
||||||
|
|
||||||
for(const palette of paletteData.palettes)
|
|
||||||
{
|
|
||||||
if(!palette.sellable) continue;
|
|
||||||
|
|
||||||
palettes.push(palette);
|
|
||||||
}
|
|
||||||
|
|
||||||
setSelectedPaletteIndex((palettes.length ? 0 : -1));
|
|
||||||
setSellablePalettes(palettes);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setSelectedPaletteIndex(-1);
|
|
||||||
setSellablePalettes([]);
|
|
||||||
|
|
||||||
SendMessageHook(new GetSellablePetPalettesComposer(productData.type));
|
|
||||||
}, [ activeOffer, petPalettes ]);
|
|
||||||
|
|
||||||
useEffect(() =>
|
|
||||||
{
|
|
||||||
if(petIndex === -1) return;
|
|
||||||
|
|
||||||
const colors = GetPetAvailableColors(petIndex, sellablePalettes);
|
|
||||||
|
|
||||||
setSelectedColorIndex((colors.length ? 0 : -1));
|
|
||||||
setSellableColors(colors);
|
|
||||||
}, [ petIndex, sellablePalettes ]);
|
|
||||||
|
|
||||||
const getColor = useMemo(() =>
|
const getColor = useMemo(() =>
|
||||||
{
|
{
|
||||||
@ -92,21 +38,6 @@ export const CatalogLayoutPetView: FC<CatalogLayoutPetViewProps> = props =>
|
|||||||
return sellableColors[selectedColorIndex][0];
|
return sellableColors[selectedColorIndex][0];
|
||||||
}, [ sellableColors, selectedColorIndex ]);
|
}, [ sellableColors, selectedColorIndex ]);
|
||||||
|
|
||||||
useEffect(() =>
|
|
||||||
{
|
|
||||||
if(!roomPreviewer) return;
|
|
||||||
|
|
||||||
roomPreviewer && roomPreviewer.reset(false);
|
|
||||||
|
|
||||||
if((petIndex === -1) || !sellablePalettes.length || (selectedPaletteIndex === -1)) return;
|
|
||||||
|
|
||||||
let petFigureString = `${ petIndex } ${ sellablePalettes[selectedPaletteIndex].paletteId }`;
|
|
||||||
|
|
||||||
if(petIndex <= 7) petFigureString += ` ${ getColor.toString(16) }`;
|
|
||||||
|
|
||||||
roomPreviewer.addPetIntoRoom(petFigureString);
|
|
||||||
}, [ roomPreviewer, petIndex, sellablePalettes, selectedPaletteIndex, getColor ]);
|
|
||||||
|
|
||||||
const petBreedName = useMemo(() =>
|
const petBreedName = useMemo(() =>
|
||||||
{
|
{
|
||||||
if((petIndex === -1) || !sellablePalettes.length || (selectedPaletteIndex === -1)) return '';
|
if((petIndex === -1) || !sellablePalettes.length || (selectedPaletteIndex === -1)) return '';
|
||||||
@ -131,56 +62,135 @@ export const CatalogLayoutPetView: FC<CatalogLayoutPetViewProps> = props =>
|
|||||||
|
|
||||||
let colorString = color.toString(16).toUpperCase();
|
let colorString = color.toString(16).toUpperCase();
|
||||||
|
|
||||||
while(colorString.length < 6)
|
while(colorString.length < 6) colorString = ('0' + colorString);
|
||||||
{
|
|
||||||
colorString = ('0' + colorString);
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${ paletteId }\n${ colorString }`;
|
return `${ paletteId }\n${ colorString }`;
|
||||||
}, [ sellablePalettes, selectedPaletteIndex, petIndex, sellableColors, selectedColorIndex ]);
|
}, [ sellablePalettes, selectedPaletteIndex, petIndex, sellableColors, selectedColorIndex ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!pageParser || !pageParser.offers.length) return;
|
||||||
|
|
||||||
|
const offer = pageParser.offers[0];
|
||||||
|
|
||||||
|
dispatchCatalogState({
|
||||||
|
type: CatalogActions.SET_CATALOG_ACTIVE_OFFER,
|
||||||
|
payload: {
|
||||||
|
activeOffer: offer
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
BatchUpdates(() =>
|
||||||
|
{
|
||||||
|
setPetIndex(GetPetIndexFromLocalization(offer.localizationId));
|
||||||
|
setColorsShowing(false);
|
||||||
|
});
|
||||||
|
}, [ pageParser, dispatchCatalogState ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!activeOffer) return;
|
||||||
|
|
||||||
|
const productData = GetProductDataForLocalization(activeOffer.localizationId);
|
||||||
|
|
||||||
|
if(!productData) return;
|
||||||
|
|
||||||
|
for(const paletteData of petPalettes)
|
||||||
|
{
|
||||||
|
if(paletteData.breed !== productData.type) continue;
|
||||||
|
|
||||||
|
const palettes: SellablePetPaletteData[] = [];
|
||||||
|
|
||||||
|
for(const palette of paletteData.palettes)
|
||||||
|
{
|
||||||
|
if(!palette.sellable) continue;
|
||||||
|
|
||||||
|
palettes.push(palette);
|
||||||
|
}
|
||||||
|
|
||||||
|
BatchUpdates(() =>
|
||||||
|
{
|
||||||
|
setSelectedPaletteIndex((palettes.length ? 0 : -1));
|
||||||
|
setSellablePalettes(palettes);
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BatchUpdates(() =>
|
||||||
|
{
|
||||||
|
setSelectedPaletteIndex(-1);
|
||||||
|
setSellablePalettes([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
SendMessageHook(new GetSellablePetPalettesComposer(productData.type));
|
||||||
|
}, [ activeOffer, petPalettes ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(petIndex === -1) return;
|
||||||
|
|
||||||
|
const colors = GetPetAvailableColors(petIndex, sellablePalettes);
|
||||||
|
|
||||||
|
BatchUpdates(() =>
|
||||||
|
{
|
||||||
|
setSelectedColorIndex((colors.length ? 0 : -1));
|
||||||
|
setSellableColors(colors);
|
||||||
|
});
|
||||||
|
}, [ petIndex, sellablePalettes ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!roomPreviewer) return;
|
||||||
|
|
||||||
|
roomPreviewer && roomPreviewer.reset(false);
|
||||||
|
|
||||||
|
if((petIndex === -1) || !sellablePalettes.length || (selectedPaletteIndex === -1)) return;
|
||||||
|
|
||||||
|
let petFigureString = `${ petIndex } ${ sellablePalettes[selectedPaletteIndex].paletteId }`;
|
||||||
|
|
||||||
|
if(petIndex <= 7) petFigureString += ` ${ getColor.toString(16) }`;
|
||||||
|
|
||||||
|
roomPreviewer.addPetIntoRoom(petFigureString);
|
||||||
|
}, [ roomPreviewer, petIndex, sellablePalettes, selectedPaletteIndex, getColor ]);
|
||||||
|
|
||||||
if(!activeOffer) return null;
|
if(!activeOffer) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NitroLayoutGrid>
|
<Grid>
|
||||||
<NitroLayoutGridColumn size={ 7 }>
|
<Column size={ 7 } overflow="hidden">
|
||||||
<NitroCardGridView>
|
<Grid grow columnCount={ 5 } overflow="auto">
|
||||||
{ !colorsShowing && (sellablePalettes.length > 0) && sellablePalettes.map((palette, index) =>
|
{ !colorsShowing && (sellablePalettes.length > 0) && sellablePalettes.map((palette, index) =>
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
<NitroCardGridItemView key={ index } itemActive={ (selectedPaletteIndex === index) } onClick={ event => setSelectedPaletteIndex(index) }>
|
<LayoutGridItem key={ index } itemActive={ (selectedPaletteIndex === index) } onClick={ event => setSelectedPaletteIndex(index) }>
|
||||||
<PetImageView typeId={ petIndex } paletteId={ palette.paletteId } direction={ 2 } headOnly={ true } />
|
<PetImageView typeId={ petIndex } paletteId={ palette.paletteId } direction={ 2 } headOnly={ true } />
|
||||||
</NitroCardGridItemView>
|
</LayoutGridItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{ colorsShowing && (sellableColors.length > 0) && sellableColors.map((colorSet, index) =>
|
{ colorsShowing && (sellableColors.length > 0) && sellableColors.map((colorSet, index) => <LayoutGridItem key={ index } itemActive={ (selectedColorIndex === index) } itemColor={ ColorConverter.int2rgb(colorSet[0]) } onClick={ event => setSelectedColorIndex(index) } />) }
|
||||||
{
|
</Grid>
|
||||||
return (
|
</Column>
|
||||||
<NitroCardGridItemView key={ index } itemActive={ (selectedColorIndex === index) } itemColor={ ColorConverter.int2rgb(colorSet[0]) } onClick={ event => setSelectedColorIndex(index) } />
|
<Column size={ 5 } overflow="hidden">
|
||||||
);
|
|
||||||
})}
|
|
||||||
</NitroCardGridView>
|
|
||||||
</NitroLayoutGridColumn>
|
|
||||||
<NitroLayoutGridColumn size={ 5 }>
|
|
||||||
{ (petIndex === -1) &&
|
{ (petIndex === -1) &&
|
||||||
<CatalogPageDetailsView pageParser={ pageParser } /> }
|
<CatalogPageDetailsView pageParser={ pageParser } /> }
|
||||||
{ (petIndex >= 0) &&
|
{ (petIndex >= 0) &&
|
||||||
<>
|
<>
|
||||||
<NitroLayoutFlexColumn overflow="hidden" position="relative">
|
<Column overflow="hidden" position="relative" gap={ 0 }>
|
||||||
{ roomPreviewer && <CatalogRoomPreviewerView roomPreviewer={ roomPreviewer } height={ 140 } /> }
|
{ roomPreviewer && <CatalogRoomPreviewerView roomPreviewer={ roomPreviewer } height={ 140 } /> }
|
||||||
{ (petIndex > -1 && petIndex <= 7) &&
|
{ (petIndex > -1 && petIndex <= 7) &&
|
||||||
<NitroLayoutBase className="start-2 bottom-2" position="absolute">
|
<Base position="absolute" className="start-1 bottom-1">
|
||||||
<button type="button" className= { 'btn btn-primary btn-sm color-button ' + (colorsShowing ? 'active ' : '') } onClick={ event => setColorsShowing(!colorsShowing) }>
|
<Button size="sm" active={ colorsShowing } onClick={ event => setColorsShowing(!colorsShowing) }>
|
||||||
<i className="fas fa-fill-drip" />
|
<FontAwesomeIcon icon="fill-drip" />
|
||||||
</button>
|
</Button>
|
||||||
</NitroLayoutBase> }
|
</Base> }
|
||||||
</NitroLayoutFlexColumn>
|
</Column>
|
||||||
<NitroLayoutFlexColumn className="flex-grow-1" gap={ 2 }>
|
<Column grow>
|
||||||
<NitroLayoutBase className="flex-grow-1 text-black text-truncate">{ petBreedName }</NitroLayoutBase>
|
<Text grow truncate>{ petBreedName }</Text>
|
||||||
<CatalogLayoutPetPurchaseView offer={ activeOffer } pageId={ pageParser.pageId } extra={ petPurchaseString } />
|
<CatalogLayoutPetPurchaseView offer={ activeOffer } pageId={ pageParser.pageId } extra={ petPurchaseString } />
|
||||||
</NitroLayoutFlexColumn>
|
</Column>
|
||||||
</> }
|
</> }
|
||||||
</NitroLayoutGridColumn>
|
</Column>
|
||||||
</NitroLayoutGrid>
|
</Grid>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -1,19 +1,21 @@
|
|||||||
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
import { Dispatch, FC, SetStateAction, useCallback, useEffect, useState } from 'react';
|
||||||
import { LocalizeText } from '../../../../../../../api';
|
import { LocalizeText } from '../../../../../../api';
|
||||||
import { CatalogEvent, CatalogNameResultEvent } from '../../../../../../../events';
|
import { CatalogEvent, CatalogNameResultEvent } from '../../../../../../events';
|
||||||
import { useUiEvent } from '../../../../../../../hooks/events/ui/ui-event';
|
import { useUiEvent } from '../../../../../../hooks/events/ui/ui-event';
|
||||||
import { CatalogPetNameApprovalViewProps } from './CatalogPetNameApprovalView.types';
|
|
||||||
|
export interface CatalogPetNameApprovalViewProps
|
||||||
|
{
|
||||||
|
petNameValue: string;
|
||||||
|
setPetNameValue: Dispatch<SetStateAction<string>>;
|
||||||
|
nameApproved: boolean;
|
||||||
|
setNameApproved: Dispatch<SetStateAction<boolean>>;
|
||||||
|
}
|
||||||
|
|
||||||
export const CatalogPetNameApprovalView: FC<CatalogPetNameApprovalViewProps> = props =>
|
export const CatalogPetNameApprovalView: FC<CatalogPetNameApprovalViewProps> = props =>
|
||||||
{
|
{
|
||||||
const { petNameValue = null, setPetNameValue = null, nameApproved = false, setNameApproved = null } = props;
|
const { petNameValue = null, setPetNameValue = null, nameApproved = false, setNameApproved = null } = props;
|
||||||
const [ validationResult, setValidationResult ] = useState(-1);
|
const [ validationResult, setValidationResult ] = useState(-1);
|
||||||
|
|
||||||
useEffect(() =>
|
|
||||||
{
|
|
||||||
setValidationResult(-1);
|
|
||||||
}, [ petNameValue ]);
|
|
||||||
|
|
||||||
const onCatalogNameResultEvent = useCallback((event: CatalogNameResultEvent) =>
|
const onCatalogNameResultEvent = useCallback((event: CatalogNameResultEvent) =>
|
||||||
{
|
{
|
||||||
if(event.result === 0)
|
if(event.result === 0)
|
||||||
@ -28,7 +30,7 @@ export const CatalogPetNameApprovalView: FC<CatalogPetNameApprovalViewProps> = p
|
|||||||
|
|
||||||
useUiEvent(CatalogEvent.APPROVE_NAME_RESULT, onCatalogNameResultEvent);
|
useUiEvent(CatalogEvent.APPROVE_NAME_RESULT, onCatalogNameResultEvent);
|
||||||
|
|
||||||
const validationErrorMessage = useMemo(() =>
|
const validationErrorMessage = () =>
|
||||||
{
|
{
|
||||||
let key: string = '';
|
let key: string = '';
|
||||||
|
|
||||||
@ -49,7 +51,12 @@ export const CatalogPetNameApprovalView: FC<CatalogPetNameApprovalViewProps> = p
|
|||||||
}
|
}
|
||||||
|
|
||||||
return LocalizeText(key);
|
return LocalizeText(key);
|
||||||
}, [ validationResult ]);
|
}
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
setValidationResult(-1);
|
||||||
|
}, [ petNameValue ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="input-group has-validation">
|
<div className="input-group has-validation">
|
@ -1,8 +1,8 @@
|
|||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
|
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
||||||
import { CatalogLayoutPets3View } from '../pets3/CatalogLayoutPets3View';
|
import { CatalogLayoutPets3View } from '../pets3/CatalogLayoutPets3View';
|
||||||
import { CatalogLayoutPets2ViewProps } from './CatalogLayoutPets2View.types';
|
|
||||||
|
|
||||||
export const CatalogLayoutPets2View: FC<CatalogLayoutPets2ViewProps> = props =>
|
export const CatalogLayoutPets2View: FC<CatalogLayoutProps> = props =>
|
||||||
{
|
{
|
||||||
return <CatalogLayoutPets3View { ...props } />
|
return <CatalogLayoutPets3View { ...props } />
|
||||||
}
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
import { FC } from 'react';
|
||||||
|
import { Base } from '../../../../../../common/Base';
|
||||||
|
import { Column } from '../../../../../../common/Column';
|
||||||
|
import { Flex } from '../../../../../../common/Flex';
|
||||||
|
import { GetCatalogPageImage, GetCatalogPageText } from '../../../../common/CatalogUtilities';
|
||||||
|
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
||||||
|
|
||||||
|
export const CatalogLayoutPets3View: FC<CatalogLayoutProps> = props =>
|
||||||
|
{
|
||||||
|
const { pageParser = null } = props;
|
||||||
|
|
||||||
|
const imageUrl = GetCatalogPageImage(pageParser, 1);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Column grow className="bg-muted rounded text-black p-2" overflow="hidden">
|
||||||
|
<Flex alignItems="center" gap={ 2 }>
|
||||||
|
{ imageUrl && <img alt="" src={ GetCatalogPageImage(pageParser, 1) } /> }
|
||||||
|
<Base className="fs-5" dangerouslySetInnerHTML={ { __html: GetCatalogPageText(pageParser, 1) } } />
|
||||||
|
</Flex>
|
||||||
|
<Column grow alignItems="center" overflow="auto">
|
||||||
|
<Base dangerouslySetInnerHTML={ { __html: GetCatalogPageText(pageParser, 2) } } />
|
||||||
|
</Column>
|
||||||
|
<Flex alignItems="center">
|
||||||
|
<Base className="fw-bold" dangerouslySetInnerHTML={ { __html: GetCatalogPageText(pageParser, 3) } } />
|
||||||
|
</Flex>
|
||||||
|
</Column>
|
||||||
|
);
|
||||||
|
}
|
@ -1,31 +1,29 @@
|
|||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
import { NitroCardGridView, NitroLayoutGrid, NitroLayoutGridColumn } from '../../../../../../layout';
|
import { Column } from '../../../../../../common/Column';
|
||||||
|
import { Grid } from '../../../../../../common/Grid';
|
||||||
import { useCatalogContext } from '../../../../context/CatalogContext';
|
import { useCatalogContext } from '../../../../context/CatalogContext';
|
||||||
import { CatalogPageDetailsView } from '../../../page-details/CatalogPageDetailsView';
|
import { CatalogPageDetailsView } from '../../../page-details/CatalogPageDetailsView';
|
||||||
import { CatalogProductView } from '../../product/CatalogProductView';
|
import { CatalogProductView } from '../../product/CatalogProductView';
|
||||||
import { CatalogPurchaseView } from '../../purchase/CatalogPurchaseView';
|
import { CatalogPurchaseView } from '../../purchase/CatalogPurchaseView';
|
||||||
import { CatalogLayoutSingleBundleViewProps } from './CatalogLayoutSingleBundleView.types';
|
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
||||||
|
|
||||||
export const CatalogLayoutSingleBundleView: FC<CatalogLayoutSingleBundleViewProps> = props =>
|
export const CatalogLayoutSingleBundleView: FC<CatalogLayoutProps> = props =>
|
||||||
{
|
{
|
||||||
const { roomPreviewer = null, pageParser = null } = props;
|
const { roomPreviewer = null, pageParser = null } = props;
|
||||||
const { catalogState = null, dispatchCatalogState = null } = useCatalogContext();
|
const { catalogState = null, dispatchCatalogState = null } = useCatalogContext();
|
||||||
const { activeOffer = null } = catalogState;
|
const { activeOffer = null } = catalogState;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NitroLayoutGrid>
|
<Grid>
|
||||||
<NitroLayoutGridColumn size={ 7 }>
|
<Column size={ 7 } overflow="hidden">
|
||||||
<NitroCardGridView>
|
<Grid grow overflow="auto">
|
||||||
{ activeOffer && activeOffer.products && (activeOffer.products.length > 0) && activeOffer.products.map((product, index) =>
|
{ activeOffer && activeOffer.products && (activeOffer.products.length > 0) && activeOffer.products.map((product, index) => <CatalogProductView key={ index } itemActive={ false } product={ product } />)}
|
||||||
{
|
</Grid>
|
||||||
return <CatalogProductView key={ index } isActive={ false } product={ product } />
|
</Column>
|
||||||
}) }
|
<Column size={ 5 } overflow="hidden">
|
||||||
</NitroCardGridView>
|
|
||||||
</NitroLayoutGridColumn>
|
|
||||||
<NitroLayoutGridColumn size={ 5 }>
|
|
||||||
<CatalogPageDetailsView pageParser={ pageParser } />
|
<CatalogPageDetailsView pageParser={ pageParser } />
|
||||||
{ activeOffer && <CatalogPurchaseView offer={ activeOffer } pageId={ pageParser.pageId } /> }
|
{ activeOffer && <CatalogPurchaseView offer={ activeOffer } pageId={ pageParser.pageId } /> }
|
||||||
</NitroLayoutGridColumn>
|
</Column>
|
||||||
</NitroLayoutGrid>
|
</Grid>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -1,14 +1,17 @@
|
|||||||
import { CatalogPageMessageOfferData, IFurnitureData } from '@nitrots/nitro-renderer';
|
import { CatalogPageMessageOfferData, IFurnitureData } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useEffect, useState } from 'react';
|
import { FC, useEffect, useState } from 'react';
|
||||||
import { GetSessionDataManager, LocalizeText } from '../../../../../../api';
|
import { GetSessionDataManager, LocalizeText } from '../../../../../../api';
|
||||||
import { NitroLayoutGrid, NitroLayoutGridColumn } from '../../../../../../layout';
|
import { Button } from '../../../../../../common/Button';
|
||||||
|
import { ButtonGroup } from '../../../../../../common/ButtonGroup';
|
||||||
|
import { Column } from '../../../../../../common/Column';
|
||||||
|
import { Grid } from '../../../../../../common/Grid';
|
||||||
import { ProductTypeEnum } from '../../../../common/ProductTypeEnum';
|
import { ProductTypeEnum } from '../../../../common/ProductTypeEnum';
|
||||||
import { useCatalogContext } from '../../../../context/CatalogContext';
|
import { useCatalogContext } from '../../../../context/CatalogContext';
|
||||||
import { CatalogPageOffersView } from '../../offers/CatalogPageOffersView';
|
import { CatalogPageOffersView } from '../../offers/CatalogPageOffersView';
|
||||||
import { CatalogProductPreviewView } from '../../product-preview/CatalogProductPreviewView';
|
import { CatalogProductPreviewView } from '../../product-preview/CatalogProductPreviewView';
|
||||||
import { CatalogLayoutSpacesViewProps } from './CatalogLayoutSpacesView.types';
|
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
||||||
|
|
||||||
export const CatalogLayoutSpacesView: FC<CatalogLayoutSpacesViewProps> = props =>
|
export const CatalogLayoutSpacesView: FC<CatalogLayoutProps> = props =>
|
||||||
{
|
{
|
||||||
const { roomPreviewer = null, pageParser = null } = props;
|
const { roomPreviewer = null, pageParser = null } = props;
|
||||||
const [ groups, setGroups ] = useState<CatalogPageMessageOfferData[][]>([]);
|
const [ groups, setGroups ] = useState<CatalogPageMessageOfferData[][]>([]);
|
||||||
@ -67,19 +70,16 @@ export const CatalogLayoutSpacesView: FC<CatalogLayoutSpacesViewProps> = props =
|
|||||||
const product = ((activeOffer && activeOffer.products[0]) || null);
|
const product = ((activeOffer && activeOffer.products[0]) || null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NitroLayoutGrid>
|
<Grid>
|
||||||
<NitroLayoutGridColumn size={ 7 }>
|
<Column size={ 7 } overflow="hidden">
|
||||||
<div className="btn-group w-100">
|
<ButtonGroup>
|
||||||
{ groupNames.map((name, index) =>
|
{ groupNames.map((name, index) => <Button key={ index } size="sm" active={ (activeGroupIndex === index) } onClick={ event => setActiveGroupIndex(index) }>{ LocalizeText(`catalog.spaces.tab.${ name }`) }</Button>)}
|
||||||
{
|
</ButtonGroup>
|
||||||
return <button key={ index } type="button" className={ 'btn btn-primary btn-sm ' + ((activeGroupIndex === index) ? 'active ' : '' )} onClick={ event => setActiveGroupIndex(index) }>{ LocalizeText(`catalog.spaces.tab.${ name }`) }</button>
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<CatalogPageOffersView offers={ groups[activeGroupIndex] } />
|
<CatalogPageOffersView offers={ groups[activeGroupIndex] } />
|
||||||
</NitroLayoutGridColumn>
|
</Column>
|
||||||
<NitroLayoutGridColumn size={ 5 }>
|
<Column size={ 5 } overflow="hidden">
|
||||||
<CatalogProductPreviewView pageParser={ pageParser } activeOffer={ activeOffer } roomPreviewer={ roomPreviewer } />
|
<CatalogProductPreviewView pageParser={ pageParser } activeOffer={ activeOffer } roomPreviewer={ roomPreviewer } />
|
||||||
</NitroLayoutGridColumn>
|
</Column>
|
||||||
</NitroLayoutGrid>
|
</Grid>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -1,11 +1,12 @@
|
|||||||
import { FC, useState } from 'react';
|
import { FC, useState } from 'react';
|
||||||
import { NitroLayoutGrid, NitroLayoutGridColumn } from '../../../../../../layout';
|
import { Column } from '../../../../../../common/Column';
|
||||||
|
import { Grid } from '../../../../../../common/Grid';
|
||||||
import { useCatalogContext } from '../../../../context/CatalogContext';
|
import { useCatalogContext } from '../../../../context/CatalogContext';
|
||||||
import { CatalogPageOffersView } from '../../offers/CatalogPageOffersView';
|
import { CatalogPageOffersView } from '../../offers/CatalogPageOffersView';
|
||||||
import { CatalogProductPreviewView } from '../../product-preview/CatalogProductPreviewView';
|
import { CatalogProductPreviewView } from '../../product-preview/CatalogProductPreviewView';
|
||||||
import { CatalogLayoutTrophiesViewProps } from './CatalogLayoutTrophiesView.types';
|
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
||||||
|
|
||||||
export const CatalogLayoutTrophiesView: FC<CatalogLayoutTrophiesViewProps> = props =>
|
export const CatalogLayoutTrophiesView: FC<CatalogLayoutProps> = props =>
|
||||||
{
|
{
|
||||||
const { roomPreviewer = null, pageParser = null } = props;
|
const { roomPreviewer = null, pageParser = null } = props;
|
||||||
const { catalogState = null, dispatchCatalogState = null } = useCatalogContext();
|
const { catalogState = null, dispatchCatalogState = null } = useCatalogContext();
|
||||||
@ -15,14 +16,14 @@ export const CatalogLayoutTrophiesView: FC<CatalogLayoutTrophiesViewProps> = pro
|
|||||||
const product = ((activeOffer && activeOffer.products[0]) || null);
|
const product = ((activeOffer && activeOffer.products[0]) || null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NitroLayoutGrid>
|
<Grid>
|
||||||
<NitroLayoutGridColumn size={ 7 }>
|
<Column size={ 7 } overflow="hidden">
|
||||||
<CatalogPageOffersView offers={ pageParser.offers } />
|
<CatalogPageOffersView offers={ pageParser.offers } />
|
||||||
<textarea className="flex-grow-1 form-control w-100" defaultValue={ trophyText || '' } onChange={ event => setTrophyText(event.target.value) } />
|
<textarea className="flex-grow-1 form-control w-100" defaultValue={ trophyText || '' } onChange={ event => setTrophyText(event.target.value) } />
|
||||||
</NitroLayoutGridColumn>
|
</Column>
|
||||||
<NitroLayoutGridColumn size={ 5 }>
|
<Column size={ 5 } overflow="hidden">
|
||||||
<CatalogProductPreviewView pageParser={ pageParser } activeOffer={ activeOffer } roomPreviewer={ roomPreviewer } extra={ trophyText } />
|
<CatalogProductPreviewView pageParser={ pageParser } activeOffer={ activeOffer } roomPreviewer={ roomPreviewer } extra={ trophyText } />
|
||||||
</NitroLayoutGridColumn>
|
</Column>
|
||||||
</NitroLayoutGrid>
|
</Grid>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -1,29 +1,30 @@
|
|||||||
import { ClubOfferData, GetClubOffersMessageComposer, PurchaseFromCatalogComposer } from '@nitrots/nitro-renderer';
|
import { ClubOfferData, GetClubOffersMessageComposer, PurchaseFromCatalogComposer } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { LocalizeText } from '../../../../../../api';
|
import { LocalizeText } from '../../../../../../api';
|
||||||
|
import { Button } from '../../../../../../common/Button';
|
||||||
|
import { Column } from '../../../../../../common/Column';
|
||||||
|
import { Flex } from '../../../../../../common/Flex';
|
||||||
|
import { Grid } from '../../../../../../common/Grid';
|
||||||
|
import { LayoutGridItem } from '../../../../../../common/layout/LayoutGridItem';
|
||||||
|
import { Text } from '../../../../../../common/Text';
|
||||||
import { CatalogEvent } from '../../../../../../events/catalog/CatalogEvent';
|
import { CatalogEvent } from '../../../../../../events/catalog/CatalogEvent';
|
||||||
import { useUiEvent } from '../../../../../../hooks';
|
import { useUiEvent } from '../../../../../../hooks';
|
||||||
import { SendMessageHook } from '../../../../../../hooks/messages/message-event';
|
import { SendMessageHook } from '../../../../../../hooks/messages/message-event';
|
||||||
import { NitroLayoutFlexColumn, NitroLayoutGrid, NitroLayoutGridColumn } from '../../../../../../layout';
|
|
||||||
import { NitroLayoutBase } from '../../../../../../layout/base';
|
|
||||||
import { NitroCardGridItemView } from '../../../../../../layout/card/grid/item/NitroCardGridItemView';
|
|
||||||
import { NitroCardGridView } from '../../../../../../layout/card/grid/NitroCardGridView';
|
|
||||||
import { LoadingSpinnerView } from '../../../../../../layout/loading-spinner/LoadingSpinnerView';
|
import { LoadingSpinnerView } from '../../../../../../layout/loading-spinner/LoadingSpinnerView';
|
||||||
import { GetCurrencyAmount } from '../../../../../purse/common/CurrencyHelper';
|
import { GetCurrencyAmount } from '../../../../../../views/purse/common/CurrencyHelper';
|
||||||
import { GLOBAL_PURSE } from '../../../../../purse/PurseView';
|
import { GLOBAL_PURSE } from '../../../../../../views/purse/PurseView';
|
||||||
import { CurrencyIcon } from '../../../../../shared/currency-icon/CurrencyIcon';
|
import { CurrencyIcon } from '../../../../../../views/shared/currency-icon/CurrencyIcon';
|
||||||
|
import { CatalogPurchaseState } from '../../../../common/CatalogPurchaseState';
|
||||||
import { GetCatalogPageImage } from '../../../../common/CatalogUtilities';
|
import { GetCatalogPageImage } from '../../../../common/CatalogUtilities';
|
||||||
import { useCatalogContext } from '../../../../context/CatalogContext';
|
import { useCatalogContext } from '../../../../context/CatalogContext';
|
||||||
import { CatalogPurchaseState } from '../../purchase/purchase-button/CatalogPurchaseButtonView.types';
|
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
||||||
import { CatalogLayoutVipBuyViewProps } from './CatalogLayoutVipBuyView.types';
|
|
||||||
|
|
||||||
export const CatalogLayoutVipBuyView: FC<CatalogLayoutVipBuyViewProps> = props =>
|
export const CatalogLayoutVipBuyView: FC<CatalogLayoutProps> = props =>
|
||||||
{
|
{
|
||||||
const { catalogState = null } = useCatalogContext();
|
|
||||||
const { pageParser = null, clubOffers = null, subscriptionInfo = null } = catalogState;
|
|
||||||
|
|
||||||
const [ pendingOffer, setPendingOffer ] = useState<ClubOfferData>(null);
|
const [ pendingOffer, setPendingOffer ] = useState<ClubOfferData>(null);
|
||||||
const [ purchaseState, setPurchaseState ] = useState(CatalogPurchaseState.NONE);
|
const [ purchaseState, setPurchaseState ] = useState(CatalogPurchaseState.NONE);
|
||||||
|
const { catalogState = null } = useCatalogContext();
|
||||||
|
const { pageParser = null, clubOffers = null, subscriptionInfo = null } = catalogState;
|
||||||
|
|
||||||
const onCatalogEvent = useCallback((event: CatalogEvent) =>
|
const onCatalogEvent = useCallback((event: CatalogEvent) =>
|
||||||
{
|
{
|
||||||
@ -104,11 +105,6 @@ export const CatalogLayoutVipBuyView: FC<CatalogLayoutVipBuyViewProps> = props =
|
|||||||
SendMessageHook(new PurchaseFromCatalogComposer(pageParser.pageId, pendingOffer.offerId, null, 1));
|
SendMessageHook(new PurchaseFromCatalogComposer(pageParser.pageId, pendingOffer.offerId, null, 1));
|
||||||
}, [ pendingOffer, pageParser ]);
|
}, [ pendingOffer, pageParser ]);
|
||||||
|
|
||||||
useEffect(() =>
|
|
||||||
{
|
|
||||||
if(clubOffers === null) SendMessageHook(new GetClubOffersMessageComposer(1));
|
|
||||||
}, [ clubOffers ]);
|
|
||||||
|
|
||||||
const setOffer = useCallback((offer: ClubOfferData) =>
|
const setOffer = useCallback((offer: ClubOfferData) =>
|
||||||
{
|
{
|
||||||
setPurchaseState(CatalogPurchaseState.NONE);
|
setPurchaseState(CatalogPurchaseState.NONE);
|
||||||
@ -121,85 +117,91 @@ export const CatalogLayoutVipBuyView: FC<CatalogLayoutVipBuyViewProps> = props =
|
|||||||
|
|
||||||
if(pendingOffer.priceCredits > GetCurrencyAmount(-1))
|
if(pendingOffer.priceCredits > GetCurrencyAmount(-1))
|
||||||
{
|
{
|
||||||
return <button className="btn btn-danger btn-sm w-100">{ LocalizeText('catalog.alert.notenough.title') }</button>;
|
return <Button fullWidth variant="danger" size="sm">{ LocalizeText('catalog.alert.notenough.title') }</Button>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(pendingOffer.priceActivityPoints > GetCurrencyAmount(pendingOffer.priceActivityPointsType))
|
if(pendingOffer.priceActivityPoints > GetCurrencyAmount(pendingOffer.priceActivityPointsType))
|
||||||
{
|
{
|
||||||
return <button className="btn btn-danger btn-sm w-100">{ LocalizeText('catalog.alert.notenough.activitypoints.title.' + pendingOffer.priceActivityPointsType) }</button>;
|
return <Button fullWidth variant="danger" size="sm">{ LocalizeText('catalog.alert.notenough.activitypoints.title.' + pendingOffer.priceActivityPointsType) }</Button>;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(purchaseState)
|
switch(purchaseState)
|
||||||
{
|
{
|
||||||
case CatalogPurchaseState.CONFIRM:
|
case CatalogPurchaseState.CONFIRM:
|
||||||
return <button type="button" className="btn btn-warning w-100" onClick={ purchaseSubscription }>{ LocalizeText('catalog.marketplace.confirm_title') }</button>;
|
return <Button fullWidth variant="warning" size="sm" onClick={ purchaseSubscription }>{ LocalizeText('catalog.marketplace.confirm_title') }</Button>;
|
||||||
case CatalogPurchaseState.PURCHASE:
|
case CatalogPurchaseState.PURCHASE:
|
||||||
return <button type="button" className="btn btn-primary w-100" disabled><LoadingSpinnerView /></button>;
|
return <Button fullWidth variant="primary" size="sm" disabled><LoadingSpinnerView /></Button>;
|
||||||
case CatalogPurchaseState.FAILED:
|
case CatalogPurchaseState.FAILED:
|
||||||
return <button type="button" className="btn btn-danger w-100" disabled>{ LocalizeText('generic.failed') }</button>;
|
return <Button fullWidth variant="danger" size="sm" disabled>{ LocalizeText('generic.failed') }</Button>;
|
||||||
case CatalogPurchaseState.NONE:
|
case CatalogPurchaseState.NONE:
|
||||||
default:
|
default:
|
||||||
return <button type="button" className="btn btn-success w-100" onClick={ () => setPurchaseState(CatalogPurchaseState.CONFIRM) }>{ LocalizeText('buy') }</button>;
|
return <Button fullWidth variant="success" size="sm" onClick={ () => setPurchaseState(CatalogPurchaseState.CONFIRM) }>{ LocalizeText('buy') }</Button>;
|
||||||
}
|
}
|
||||||
}, [ pendingOffer, purchaseState, purchaseSubscription ]);
|
}, [ pendingOffer, purchaseState, purchaseSubscription ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
if(!clubOffers) SendMessageHook(new GetClubOffersMessageComposer(1));
|
||||||
|
}, [ clubOffers ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NitroLayoutGrid>
|
<Grid>
|
||||||
<NitroLayoutGridColumn size={ 7 }>
|
<Column fullHeight size={ 7 } overflow="hidden">
|
||||||
<NitroCardGridView columns={ 1 } className="vip-buy-grid">
|
<Grid grow columnCount={ 1 } className="nitro-catalog-layout-vip-buy-grid" overflow="auto">
|
||||||
{ clubOffers && (clubOffers.length > 0) && clubOffers.map((offer, index) =>
|
{ clubOffers && (clubOffers.length > 0) && clubOffers.map((offer, index) =>
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
<NitroCardGridItemView key={ index } className="justify-content-between py-1 px-2 text-black" itemActive={ pendingOffer === offer } onClick={ () => setOffer(offer) }>
|
<LayoutGridItem key={ index } column={ false } center={ false } alignItems="center" justifyContent="between" itemActive={ pendingOffer === offer } className="p-1" onClick={ () => setOffer(offer) }>
|
||||||
<i className="icon icon-hc-banner" />
|
<i className="icon-hc-banner" />
|
||||||
<div className="fw-bold">
|
<Column justifyContent="end" gap={ 0 }>
|
||||||
<div className="text-end">{ getOfferText(offer) }</div>
|
<Text textEnd>{ getOfferText(offer) }</Text>
|
||||||
<div className="d-flex gap-2 justify-content-end">
|
<Flex justifyContent="end" gap={ 1 }>
|
||||||
{ (offer.priceCredits > 0) &&
|
{ (offer.priceCredits > 0) &&
|
||||||
<div className="d-flex align-items-center justify-content-end gap-1">
|
<Flex alignItems="center" justifyContent="end" gap={ 1 }>
|
||||||
<span className="text-black">{ offer.priceCredits }</span>
|
<Text>{ offer.priceCredits }</Text>
|
||||||
<CurrencyIcon type={ -1 } />
|
<CurrencyIcon type={ -1 } />
|
||||||
</div> }
|
</Flex> }
|
||||||
{ (offer.priceActivityPoints > 0) &&
|
{ (offer.priceActivityPoints > 0) &&
|
||||||
<div className="d-flex align-items-center justify-content-end gap-1">
|
<Flex alignItems="center" justifyContent="end" gap={ 1 }>
|
||||||
<span className="text-black">{ offer.priceActivityPoints }</span>
|
<Text>{ offer.priceActivityPoints }</Text>
|
||||||
<CurrencyIcon type={ offer.priceActivityPointsType } />
|
<CurrencyIcon type={ offer.priceActivityPointsType } />
|
||||||
</div> }
|
</Flex> }
|
||||||
</div>
|
</Flex>
|
||||||
</div>
|
</Column>
|
||||||
</NitroCardGridItemView>
|
</LayoutGridItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
<div className="mt-auto text-black">{ LocalizeText('catalog.vip.buy.hccenter') }</div>
|
<Text dangerouslySetInnerHTML={{ __html: LocalizeText('catalog.vip.buy.hccenter') }}></Text>
|
||||||
</NitroCardGridView>
|
</Grid>
|
||||||
</NitroLayoutGridColumn>
|
</Column>
|
||||||
<NitroLayoutGridColumn size={ 5 }>
|
<Column size={ 5 } overflow="hidden">
|
||||||
<NitroLayoutFlexColumn className="justify-content-center align-items-center h-100" overflow="hidden" gap={ 2 }>
|
<Column fullHeight center overflow="hidden">
|
||||||
{ GetCatalogPageImage(pageParser, 1) && <img className="" alt="" src={ GetCatalogPageImage(pageParser, 1) } /> }
|
{ GetCatalogPageImage(pageParser, 1) && <img alt="" src={ GetCatalogPageImage(pageParser, 1) } /> }
|
||||||
<NitroLayoutBase className="text-center text-black" overflow="auto" dangerouslySetInnerHTML={{ __html: getSubscriptionDetails }} />
|
<Text center overflow="auto" dangerouslySetInnerHTML={{ __html: getSubscriptionDetails }} />
|
||||||
</NitroLayoutFlexColumn>
|
</Column>
|
||||||
{ pendingOffer && <div className="mt-auto w-100 text-black">
|
{ pendingOffer &&
|
||||||
<div className="d-flex gap-2 mb-2 align-items-center">
|
<Column fullWidth grow justifyContent="end">
|
||||||
<div className="w-100">
|
<Flex alignItems="end">
|
||||||
<div className="fw-bold">{ getPurchaseHeader() }</div>
|
<Column grow gap={ 0 }>
|
||||||
<div className="small">{ getPurchaseValidUntil() }</div>
|
<Text fontWeight="bold">{ getPurchaseHeader() }</Text>
|
||||||
</div>
|
<Text>{ getPurchaseValidUntil() }</Text>
|
||||||
<div>
|
</Column>
|
||||||
{ (pendingOffer.priceCredits > 0) &&
|
<Column gap={ 1 }>
|
||||||
<div className="d-flex align-items-center justify-content-end gap-1">
|
{ (pendingOffer.priceCredits > 0) &&
|
||||||
<span className="text-black">{ pendingOffer.priceCredits }</span>
|
<Flex alignItems="center" justifyContent="end" gap={ 1 }>
|
||||||
<CurrencyIcon type={ -1 } />
|
<Text>{ pendingOffer.priceCredits }</Text>
|
||||||
</div> }
|
<CurrencyIcon type={ -1 } />
|
||||||
{ (pendingOffer.priceActivityPoints > 0) &&
|
</Flex> }
|
||||||
<div className="d-flex align-items-center justify-content-end gap-1">
|
{ (pendingOffer.priceActivityPoints > 0) &&
|
||||||
<span className="text-black">{ pendingOffer.priceActivityPoints }</span>
|
<Flex alignItems="center" justifyContent="end" gap={ 1 }>
|
||||||
<CurrencyIcon type={ pendingOffer.priceActivityPointsType } />
|
<Text>{ pendingOffer.priceActivityPoints }</Text>
|
||||||
</div> }
|
<CurrencyIcon type={ pendingOffer.priceActivityPointsType } />
|
||||||
</div>
|
</Flex> }
|
||||||
</div>
|
</Column>
|
||||||
{ getPurchaseButton() }
|
</Flex>
|
||||||
</div> }
|
{ getPurchaseButton() }
|
||||||
</NitroLayoutGridColumn>
|
</Column> }
|
||||||
</NitroLayoutGrid>
|
</Column>
|
||||||
|
</Grid>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -1,9 +1,14 @@
|
|||||||
import { MouseEventType } from '@nitrots/nitro-renderer';
|
import { CatalogPageMessageOfferData, MouseEventType } from '@nitrots/nitro-renderer';
|
||||||
import { FC, MouseEvent, useCallback, useState } from 'react';
|
import { FC, MouseEvent, useCallback, useState } from 'react';
|
||||||
import { useCatalogContext } from '../../../context/CatalogContext';
|
import { useCatalogContext } from '../../../context/CatalogContext';
|
||||||
import { CatalogActions } from '../../../reducers/CatalogReducer';
|
import { CatalogActions } from '../../../reducers/CatalogReducer';
|
||||||
import { CatalogProductView } from '../product/CatalogProductView';
|
import { CatalogProductView } from '../product/CatalogProductView';
|
||||||
import { CatalogPageOfferViewProps } from './CatalogPageOfferView.types';
|
|
||||||
|
export interface CatalogPageOfferViewProps
|
||||||
|
{
|
||||||
|
isActive: boolean;
|
||||||
|
offer: CatalogPageMessageOfferData;
|
||||||
|
}
|
||||||
|
|
||||||
export const CatalogPageOfferView: FC<CatalogPageOfferViewProps> = props =>
|
export const CatalogPageOfferView: FC<CatalogPageOfferViewProps> = props =>
|
||||||
{
|
{
|
||||||
@ -41,5 +46,5 @@ export const CatalogPageOfferView: FC<CatalogPageOfferViewProps> = props =>
|
|||||||
|
|
||||||
if(!product) return null;
|
if(!product) return null;
|
||||||
|
|
||||||
return <CatalogProductView isActive={ isActive } product={ product } onClick={ onMouseEvent } onMouseDown={ onMouseEvent } onMouseUp={ onMouseEvent } onMouseOut={ onMouseEvent } />
|
return <CatalogProductView itemActive={ isActive } product={ product } onClick={ onMouseEvent } onMouseDown={ onMouseEvent } onMouseUp={ onMouseEvent } onMouseOut={ onMouseEvent } />
|
||||||
}
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
import { CatalogPageMessageOfferData } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC } from 'react';
|
||||||
|
import { Grid, GridProps } from '../../../../../common/Grid';
|
||||||
|
import { useCatalogContext } from '../../../context/CatalogContext';
|
||||||
|
import { CatalogPageOfferView } from './CatalogPageOfferView';
|
||||||
|
|
||||||
|
export interface CatalogPageOffersViewProps extends GridProps
|
||||||
|
{
|
||||||
|
offers: CatalogPageMessageOfferData[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CatalogPageOffersView: FC<CatalogPageOffersViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { offers = [], children = null, ...rest } = props;
|
||||||
|
const { catalogState = null } = useCatalogContext();
|
||||||
|
const { activeOffer = null } = catalogState;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid grow columnCount={ 5 } overflow="auto" { ...rest }>
|
||||||
|
{ offers && (offers.length > 0) && offers.map((offer, index) => <CatalogPageOfferView key={ index } isActive={ (activeOffer === offer) } offer={ offer } />) }
|
||||||
|
{ children }
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
import { CatalogPageMessageOfferData, CatalogPageMessageParser, RoomPreviewer } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC } from 'react';
|
||||||
|
import { Base } from '../../../../../common/Base';
|
||||||
|
import { Column } from '../../../../../common/Column';
|
||||||
|
import { Text } from '../../../../../common/Text';
|
||||||
|
import { BadgeImageView } from '../../../../../views/shared/badge-image/BadgeImageView';
|
||||||
|
import { LimitedEditionCompletePlateView } from '../../../../../views/shared/limited-edition/LimitedEditionCompletePlateView';
|
||||||
|
import { GetOfferName } from '../../../common/CatalogUtilities';
|
||||||
|
import { CatalogRoomPreviewerView } from '../../catalog-room-previewer/CatalogRoomPreviewerView';
|
||||||
|
import { CatalogPageDetailsView } from '../../page-details/CatalogPageDetailsView';
|
||||||
|
import { CatalogPurchaseView } from '../purchase/CatalogPurchaseView';
|
||||||
|
|
||||||
|
export interface CatalogProductPreviewViewProps
|
||||||
|
{
|
||||||
|
pageParser: CatalogPageMessageParser;
|
||||||
|
activeOffer: CatalogPageMessageOfferData;
|
||||||
|
roomPreviewer: RoomPreviewer;
|
||||||
|
badgeCode?: string;
|
||||||
|
extra?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CatalogProductPreviewView: FC<CatalogProductPreviewViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { pageParser = null, activeOffer = null, roomPreviewer = null, badgeCode = null, extra = '', disabled = false, children = null } = props;
|
||||||
|
|
||||||
|
const product = ((activeOffer && activeOffer.products[0]) || null);
|
||||||
|
|
||||||
|
if(!product) return <CatalogPageDetailsView pageParser={ pageParser } />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Column overflow="hidden" position="relative" gap={ 0 }>
|
||||||
|
{ roomPreviewer && <CatalogRoomPreviewerView roomPreviewer={ roomPreviewer } height={ 140 } /> }
|
||||||
|
{ product.uniqueLimitedItem &&
|
||||||
|
<Base fullWidth position="absolute" className="top-1">
|
||||||
|
<LimitedEditionCompletePlateView className="mx-auto" uniqueLimitedItemsLeft={ product.uniqueLimitedItemsLeft } uniqueLimitedSeriesSize={ product.uniqueLimitedSeriesSize } />
|
||||||
|
</Base> }
|
||||||
|
{ badgeCode && badgeCode.length &&
|
||||||
|
<Base position="absolute" className="top-1 end-1">
|
||||||
|
<BadgeImageView badgeCode={ badgeCode } isGroup={ true } />
|
||||||
|
</Base> }
|
||||||
|
</Column>
|
||||||
|
<Column grow>
|
||||||
|
<Text grow truncate>{ GetOfferName(activeOffer) }</Text>
|
||||||
|
{ children }
|
||||||
|
<CatalogPurchaseView offer={ activeOffer } pageId={ pageParser.pageId } extra={ extra } disabled={ disabled } />
|
||||||
|
</Column>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
import { CatalogPageMessageProductData } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC } from 'react';
|
||||||
|
import { LayoutGridItem, LayoutGridItemProps } from '../../../../../common/layout/LayoutGridItem';
|
||||||
|
import { AvatarImageView } from '../../../../../views/shared/avatar-image/AvatarImageView';
|
||||||
|
import { GetProductIconUrl } from '../../../common/GetProuductIconUrl';
|
||||||
|
import { ProductTypeEnum } from '../../../common/ProductTypeEnum';
|
||||||
|
|
||||||
|
export interface CatalogProductViewProps extends LayoutGridItemProps
|
||||||
|
{
|
||||||
|
product: CatalogPageMessageProductData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CatalogProductView: FC<CatalogProductViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { product = null, ...rest } = props;
|
||||||
|
|
||||||
|
if(!product) return null;
|
||||||
|
|
||||||
|
const iconUrl = GetProductIconUrl(product.furniClassId, product.productType, product.extraParam);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LayoutGridItem itemImage={ iconUrl } itemCount={ product.productCount } itemUniqueSoldout={ (product.uniqueLimitedSeriesSize && !product.uniqueLimitedItemsLeft) } itemUniqueNumber={ product.uniqueLimitedSeriesSize } { ...rest }>
|
||||||
|
{ (product.productType === ProductTypeEnum.ROBOT) &&
|
||||||
|
<AvatarImageView figure={ product.extraParam } direction={ 3 } headOnly={ true } /> }
|
||||||
|
</LayoutGridItem>
|
||||||
|
);
|
||||||
|
}
|
@ -1,16 +1,28 @@
|
|||||||
import { PurchaseFromCatalogComposer } from '@nitrots/nitro-renderer';
|
import { CatalogPageMessageOfferData, PurchaseFromCatalogComposer } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback, useEffect, useState } from 'react';
|
import { FC, useCallback, useEffect, useState } from 'react';
|
||||||
import { LocalizeText } from '../../../../../../api';
|
import { LocalizeText } from '../../../../../api';
|
||||||
import { CatalogEvent } from '../../../../../../events';
|
import { Button, ButtonProps } from '../../../../../common/Button';
|
||||||
import { useUiEvent } from '../../../../../../hooks/events/ui/ui-event';
|
import { CatalogEvent } from '../../../../../events';
|
||||||
import { SendMessageHook } from '../../../../../../hooks/messages/message-event';
|
import { BatchUpdates } from '../../../../../hooks';
|
||||||
import { LoadingSpinnerView } from '../../../../../../layout';
|
import { useUiEvent } from '../../../../../hooks/events/ui/ui-event';
|
||||||
import { GetCurrencyAmount } from '../../../../../purse/common/CurrencyHelper';
|
import { SendMessageHook } from '../../../../../hooks/messages/message-event';
|
||||||
import { CatalogPurchaseButtonViewProps, CatalogPurchaseState } from './CatalogPurchaseButtonView.types';
|
import { LoadingSpinnerView } from '../../../../../layout';
|
||||||
|
import { GetCurrencyAmount } from '../../../../../views/purse/common/CurrencyHelper';
|
||||||
|
import { CatalogPurchaseState } from '../../../common/CatalogPurchaseState';
|
||||||
|
|
||||||
|
export interface CatalogPurchaseButtonViewProps extends ButtonProps
|
||||||
|
{
|
||||||
|
offer: CatalogPageMessageOfferData;
|
||||||
|
pageId: number;
|
||||||
|
extra?: string;
|
||||||
|
quantity?: number;
|
||||||
|
isPurchaseAllowed?: boolean;
|
||||||
|
beforePurchase?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
export const CatalogPurchaseButtonView: FC<CatalogPurchaseButtonViewProps> = props =>
|
export const CatalogPurchaseButtonView: FC<CatalogPurchaseButtonViewProps> = props =>
|
||||||
{
|
{
|
||||||
const { className = '', offer = null, pageId = -1, extra = null, quantity = 1, isPurchaseAllowed = true, disabled = false, beforePurchase = null } = props;
|
const { offer = null, pageId = -1, extra = null, quantity = 1, isPurchaseAllowed = true, beforePurchase = null, ...rest } = props;
|
||||||
const [ purchaseState, setPurchaseState ] = useState(CatalogPurchaseState.NONE);
|
const [ purchaseState, setPurchaseState ] = useState(CatalogPurchaseState.NONE);
|
||||||
const [ pendingApproval, setPendingApproval ] = useState(false);
|
const [ pendingApproval, setPendingApproval ] = useState(false);
|
||||||
|
|
||||||
@ -47,8 +59,11 @@ export const CatalogPurchaseButtonView: FC<CatalogPurchaseButtonViewProps> = pro
|
|||||||
|
|
||||||
if(!isPurchaseAllowed)
|
if(!isPurchaseAllowed)
|
||||||
{
|
{
|
||||||
setPendingApproval(true);
|
BatchUpdates(() =>
|
||||||
setPurchaseState(CatalogPurchaseState.NONE);
|
{
|
||||||
|
setPendingApproval(true);
|
||||||
|
setPurchaseState(CatalogPurchaseState.NONE);
|
||||||
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -77,31 +92,31 @@ export const CatalogPurchaseButtonView: FC<CatalogPurchaseButtonViewProps> = pro
|
|||||||
|
|
||||||
if(product && product.uniqueLimitedItem && !product.uniqueLimitedItemsLeft)
|
if(product && product.uniqueLimitedItem && !product.uniqueLimitedItemsLeft)
|
||||||
{
|
{
|
||||||
return <button type="button" className={ 'btn btn-danger ' + className } disabled>{ LocalizeText('catalog.alert.limited_edition_sold_out.title') }</button>;
|
return <Button variant="danger" size="sm" disabled>{ LocalizeText('catalog.alert.limited_edition_sold_out.title') }</Button>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if((offer.priceCredits * quantity) > GetCurrencyAmount(-1))
|
if((offer.priceCredits * quantity) > GetCurrencyAmount(-1))
|
||||||
{
|
{
|
||||||
return <button type="button" className={ 'btn btn-danger ' + className } disabled>{ LocalizeText('catalog.alert.notenough.title') }</button>;
|
return <Button variant="danger" size="sm" disabled>{ LocalizeText('catalog.alert.notenough.title') }</Button>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if((offer.priceActivityPoints * quantity) > GetCurrencyAmount(offer.priceActivityPointsType))
|
if((offer.priceActivityPoints * quantity) > GetCurrencyAmount(offer.priceActivityPointsType))
|
||||||
{
|
{
|
||||||
return <button type="button" className={ 'btn btn-danger ' + className } disabled>{ LocalizeText('catalog.alert.notenough.activitypoints.title.' + offer.priceActivityPointsType) }</button>;
|
return <Button variant="danger" size="sm" disabled>{ LocalizeText('catalog.alert.notenough.activitypoints.title.' + offer.priceActivityPointsType) }</Button>;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(purchaseState)
|
switch(purchaseState)
|
||||||
{
|
{
|
||||||
case CatalogPurchaseState.CONFIRM:
|
case CatalogPurchaseState.CONFIRM:
|
||||||
return <button type="button" className={ 'btn btn-warning ' + className } onClick={ attemptPurchase }>{ LocalizeText('catalog.marketplace.confirm_title') }</button>;
|
return <Button variant="warning" size="sm" onClick={ attemptPurchase } { ...rest }>{ LocalizeText('catalog.marketplace.confirm_title') }</Button>;
|
||||||
case CatalogPurchaseState.PURCHASE:
|
case CatalogPurchaseState.PURCHASE:
|
||||||
return <button type="button" className={ 'btn btn-primary ' + className } disabled><LoadingSpinnerView /></button>;
|
return <Button variant="primary" size="sm" disabled { ...rest }><LoadingSpinnerView /></Button>;
|
||||||
case CatalogPurchaseState.SOLD_OUT:
|
case CatalogPurchaseState.SOLD_OUT:
|
||||||
return <button type="button" className={ 'btn btn-danger ' + className } disabled>{ LocalizeText('generic.failed') + ' - ' + LocalizeText('catalog.alert.limited_edition_sold_out.title') }</button>;
|
return <Button variant="danger" size="sm" disabled { ...rest }>{ LocalizeText('generic.failed') + ' - ' + LocalizeText('catalog.alert.limited_edition_sold_out.title') }</Button>;
|
||||||
case CatalogPurchaseState.FAILED:
|
case CatalogPurchaseState.FAILED:
|
||||||
return <button type="button" className={ 'btn btn-danger ' + className } disabled>{ LocalizeText('generic.failed') }</button>;
|
return <Button variant="danger" size="sm" disabled { ...rest }>{ LocalizeText('generic.failed') }</Button>;
|
||||||
case CatalogPurchaseState.NONE:
|
case CatalogPurchaseState.NONE:
|
||||||
default:
|
default:
|
||||||
return <button type="button" className={ 'btn btn-success ' + className } disabled={ disabled } onClick={ event => setPurchaseState(CatalogPurchaseState.CONFIRM) }>{ LocalizeText('buy') }</button>
|
return <Button variant="success" size="sm" onClick={ event => setPurchaseState(CatalogPurchaseState.CONFIRM) }>{ LocalizeText('buy') }</Button>
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
import { CatalogPageMessageOfferData } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC } from 'react';
|
||||||
|
import { LocalizeText } from '../../../../../api';
|
||||||
|
import { Button, ButtonProps } from '../../../../../common/Button';
|
||||||
|
import { CatalogInitGiftEvent } from '../../../../../events/catalog/CatalogInitGiftEvent';
|
||||||
|
import { dispatchUiEvent } from '../../../../../hooks';
|
||||||
|
|
||||||
|
export interface CatalogPurchaseGiftButtonViewProps extends ButtonProps
|
||||||
|
{
|
||||||
|
offer: CatalogPageMessageOfferData;
|
||||||
|
pageId: number;
|
||||||
|
extra?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CatalogPurchaseGiftButtonView: FC<CatalogPurchaseGiftButtonViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { offer = null, pageId = -1, extra = null, ...rest } = props;
|
||||||
|
|
||||||
|
const initGift = () =>
|
||||||
|
{
|
||||||
|
dispatchUiEvent(new CatalogInitGiftEvent(pageId, offer.offerId, extra));
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Button variant="secondary" onClick={ initGift } { ...rest }>{ LocalizeText('catalog.purchase_confirmation.gift') }</Button>;
|
||||||
|
}
|
@ -0,0 +1,90 @@
|
|||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
import { CatalogPageMessageOfferData } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC, useEffect, useState } from 'react';
|
||||||
|
import { LocalizeText } from '../../../../../api';
|
||||||
|
import { Column } from '../../../../../common/Column';
|
||||||
|
import { Flex } from '../../../../../common/Flex';
|
||||||
|
import { Text } from '../../../../../common/Text';
|
||||||
|
import { CurrencyIcon } from '../../../../../views/shared/currency-icon/CurrencyIcon';
|
||||||
|
import { CatalogPurchaseButtonView } from './CatalogPurchaseButtonView';
|
||||||
|
import { CatalogPurchaseGiftButtonView } from './CatalogPurchaseGiftButtonView';
|
||||||
|
|
||||||
|
export interface CatalogPurchaseViewProps
|
||||||
|
{
|
||||||
|
offer: CatalogPageMessageOfferData;
|
||||||
|
pageId: number;
|
||||||
|
extra?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CatalogPurchaseView: FC<CatalogPurchaseViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { offer = null, pageId = -1, extra = '', disabled = false } = props;
|
||||||
|
const [ quantity, setQuantity ] = useState(1);
|
||||||
|
|
||||||
|
const increaseQuantity = () =>
|
||||||
|
{
|
||||||
|
let newQuantity = quantity + 1;
|
||||||
|
|
||||||
|
if(newQuantity > 99) newQuantity = 99
|
||||||
|
|
||||||
|
setQuantity(newQuantity);
|
||||||
|
}
|
||||||
|
|
||||||
|
const decreaseQuantity = () =>
|
||||||
|
{
|
||||||
|
let newQuantity = quantity - 1;
|
||||||
|
|
||||||
|
if(newQuantity <= 0) newQuantity = 1;
|
||||||
|
|
||||||
|
setQuantity(newQuantity);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateQuantity = (amount: number) =>
|
||||||
|
{
|
||||||
|
if(isNaN(amount) || (amount <= 0)) amount = 1;
|
||||||
|
|
||||||
|
if(amount > 99) amount = 99;
|
||||||
|
|
||||||
|
setQuantity(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
setQuantity(1);
|
||||||
|
}, [ offer ]);
|
||||||
|
|
||||||
|
const extraData = ((extra && extra.length) ? extra : (offer?.products[0]?.extraParam || null));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Column fullWidth grow justifyContent="end">
|
||||||
|
<Flex alignItems="end">
|
||||||
|
<div className="flex-grow-1 align-items-end">
|
||||||
|
<Text>{ LocalizeText('catalog.bundlewidget.price') }</Text>
|
||||||
|
{ offer.bundlePurchaseAllowed &&
|
||||||
|
<Flex alignItems="center" gap={ 1 }>
|
||||||
|
<FontAwesomeIcon icon="caret-left" className="text-black cursor-pointer" onClick={ decreaseQuantity } />
|
||||||
|
<input type="number" className="form-control form-control-sm quantity-input" value={ quantity } onChange={ event => updateQuantity(event.target.valueAsNumber)} />
|
||||||
|
<FontAwesomeIcon icon="caret-right" className="text-black cursor-pointer" onClick={ increaseQuantity } />
|
||||||
|
</Flex> }
|
||||||
|
</div>
|
||||||
|
<Column gap={ 1 }>
|
||||||
|
{ (offer.priceCredits > 0) &&
|
||||||
|
<Flex alignItems="center" justifyContent="end" gap={ 1 }>
|
||||||
|
<Text>{ offer.priceCredits * quantity }</Text>
|
||||||
|
<CurrencyIcon type={ -1 } />
|
||||||
|
</Flex> }
|
||||||
|
{ (offer.priceActivityPoints > 0) &&
|
||||||
|
<Flex alignItems="center" justifyContent="end" gap={ 1 }>
|
||||||
|
<Text>{ offer.priceActivityPoints * quantity }</Text>
|
||||||
|
<CurrencyIcon type={ offer.priceActivityPointsType } />
|
||||||
|
</Flex> }
|
||||||
|
</Column>
|
||||||
|
</Flex>
|
||||||
|
<Column gap={ 1 }>
|
||||||
|
<CatalogPurchaseButtonView className="btn-sm w-100" offer={ offer } pageId={ pageId } extra={ extraData } quantity={ quantity } disabled={ disabled } />
|
||||||
|
{ offer.giftable && <CatalogPurchaseGiftButtonView className="btn-sm w-100 mt-1" offer={ offer } pageId={ pageId } extra={ extraData } disabled={ disabled } /> }
|
||||||
|
</Column>
|
||||||
|
</Column>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
import { RedeemVoucherMessageComposer, VoucherRedeemErrorMessageEvent, VoucherRedeemOkMessageEvent } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC, useCallback, useState } from 'react';
|
||||||
|
import { LocalizeText } from '../../../../../api';
|
||||||
|
import { Button } from '../../../../../common/Button';
|
||||||
|
import { Flex } from '../../../../../common/Flex';
|
||||||
|
import { BatchUpdates, CreateMessageHook, SendMessageHook } from '../../../../../hooks';
|
||||||
|
import { NotificationUtilities } from '../../../../../views/notification-center/common/NotificationUtilities';
|
||||||
|
|
||||||
|
export interface CatalogRedeemVoucherViewProps
|
||||||
|
{
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CatalogRedeemVoucherView: FC<CatalogRedeemVoucherViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { text = null } = props;
|
||||||
|
const [ voucher, setVoucher ] = useState<string>('');
|
||||||
|
const [ isWaiting, setIsWaiting ] = useState(false);
|
||||||
|
|
||||||
|
const redeemVoucher = () =>
|
||||||
|
{
|
||||||
|
if(!voucher || !voucher.length || isWaiting) return;
|
||||||
|
|
||||||
|
SendMessageHook(new RedeemVoucherMessageComposer(voucher));
|
||||||
|
|
||||||
|
setIsWaiting(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onVoucherRedeemOkMessageEvent = useCallback((event: VoucherRedeemOkMessageEvent) =>
|
||||||
|
{
|
||||||
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
let message = LocalizeText('catalog.alert.voucherredeem.ok.description');
|
||||||
|
|
||||||
|
if(parser.productName) message = LocalizeText('catalog.alert.voucherredeem.ok.description.furni', [ 'productName', 'productDescription' ], [ parser.productName, parser.productDescription ]);
|
||||||
|
|
||||||
|
NotificationUtilities.simpleAlert(message, null, null, null, LocalizeText('catalog.alert.voucherredeem.ok.title'));
|
||||||
|
|
||||||
|
BatchUpdates(() =>
|
||||||
|
{
|
||||||
|
setIsWaiting(false);
|
||||||
|
setVoucher('');
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
CreateMessageHook(VoucherRedeemOkMessageEvent, onVoucherRedeemOkMessageEvent);
|
||||||
|
|
||||||
|
const onVoucherRedeemErrorMessageEvent = useCallback((event: VoucherRedeemErrorMessageEvent) =>
|
||||||
|
{
|
||||||
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
NotificationUtilities.simpleAlert(LocalizeText(`catalog.alert.voucherredeem.error.description.${ parser.errorCode }`), null, null, null, LocalizeText('catalog.alert.voucherredeem.error.title'));
|
||||||
|
|
||||||
|
setIsWaiting(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
CreateMessageHook(VoucherRedeemErrorMessageEvent, onVoucherRedeemErrorMessageEvent);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex gap={ 1 }>
|
||||||
|
<input type="text" className="form-control form-control-sm" placeholder={ text } value={ voucher } onChange={ event => setVoucher(event.target.value) } />
|
||||||
|
<Button variant="primary" size="sm" onClick={ redeemVoucher } disabled={ isWaiting }>
|
||||||
|
<FontAwesomeIcon icon="tag" />
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
import { IFurnitureData, RoomPreviewer } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC } from 'react';
|
||||||
|
import { Base } from '../../../../../common/Base';
|
||||||
|
import { Column } from '../../../../../common/Column';
|
||||||
|
import { Grid } from '../../../../../common/Grid';
|
||||||
|
import { Text } from '../../../../../common/Text';
|
||||||
|
import { LimitedEditionCompletePlateView } from '../../../../../views/shared/limited-edition/LimitedEditionCompletePlateView';
|
||||||
|
import { GetOfferName } from '../../../common/CatalogUtilities';
|
||||||
|
import { useCatalogContext } from '../../../context/CatalogContext';
|
||||||
|
import { CatalogRoomPreviewerView } from '../../catalog-room-previewer/CatalogRoomPreviewerView';
|
||||||
|
import { CatalogPurchaseView } from '../purchase/CatalogPurchaseView';
|
||||||
|
import { CatalogSearchResultOffersView } from './CatalogSearchResultOffersView';
|
||||||
|
|
||||||
|
export interface CatalogLayoutSearchResultViewProps
|
||||||
|
{
|
||||||
|
roomPreviewer: RoomPreviewer;
|
||||||
|
furnitureDatas: IFurnitureData[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CatalogLayoutSearchResultView: FC<CatalogLayoutSearchResultViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { roomPreviewer = null, furnitureDatas = null } = props;
|
||||||
|
const { catalogState } = useCatalogContext();
|
||||||
|
const { activeOffer = null } = catalogState;
|
||||||
|
|
||||||
|
const product = ((activeOffer && activeOffer.products[0]) || null);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid>
|
||||||
|
<Column size={ 7 } overflow="hidden">
|
||||||
|
<CatalogSearchResultOffersView offers={ furnitureDatas } />
|
||||||
|
</Column>
|
||||||
|
{ product &&
|
||||||
|
<Column size={ 5 } overflow="hidden">
|
||||||
|
<Column overflow="hidden" position="relative" gap={ 0 }>
|
||||||
|
{ roomPreviewer && <CatalogRoomPreviewerView roomPreviewer={ roomPreviewer } height={ 140 } /> }
|
||||||
|
{ product.uniqueLimitedItem &&
|
||||||
|
<Base fullWidth position="absolute" className="top-1">
|
||||||
|
<LimitedEditionCompletePlateView className="mx-auto" uniqueLimitedItemsLeft={ product.uniqueLimitedItemsLeft } uniqueLimitedSeriesSize={ product.uniqueLimitedSeriesSize } />
|
||||||
|
</Base> }
|
||||||
|
</Column>
|
||||||
|
<Column grow>
|
||||||
|
<Text grow truncate>{ GetOfferName(activeOffer) }</Text>
|
||||||
|
<CatalogPurchaseView offer={ activeOffer } pageId={ -1 } />
|
||||||
|
</Column>
|
||||||
|
</Column> }
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
import { GetProductOfferComposer, IFurnitureData, MouseEventType } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC, MouseEvent, useCallback } from 'react';
|
||||||
|
import { LayoutGridItem, LayoutGridItemProps } from '../../../../../common/layout/LayoutGridItem';
|
||||||
|
import { SendMessageHook } from '../../../../../hooks/messages/message-event';
|
||||||
|
import { AvatarImageView } from '../../../../../views/shared/avatar-image/AvatarImageView';
|
||||||
|
import { GetProductIconUrl } from '../../../common/GetProuductIconUrl';
|
||||||
|
import { ProductTypeEnum } from '../../../common/ProductTypeEnum';
|
||||||
|
|
||||||
|
export interface CatalogSearchResultOfferViewProps extends LayoutGridItemProps
|
||||||
|
{
|
||||||
|
offer: IFurnitureData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CatalogSearchResultOfferView: FC<CatalogSearchResultOfferViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { offer = null, ...rest } = props;
|
||||||
|
|
||||||
|
const onMouseEvent = useCallback((event: MouseEvent) =>
|
||||||
|
{
|
||||||
|
switch(event.type)
|
||||||
|
{
|
||||||
|
case MouseEventType.MOUSE_DOWN:
|
||||||
|
SendMessageHook(new GetProductOfferComposer(offer.purchaseOfferId));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}, [ offer ]);
|
||||||
|
|
||||||
|
if(!offer) return null;
|
||||||
|
|
||||||
|
const iconUrl = GetProductIconUrl(offer.id, offer.type, offer.customParams);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LayoutGridItem itemImage={ iconUrl } onMouseDown={ onMouseEvent } { ...rest }>
|
||||||
|
{ (offer.type === ProductTypeEnum.ROBOT) &&
|
||||||
|
<AvatarImageView figure={ offer.customParams } direction={ 3 } headOnly={ true } /> }
|
||||||
|
</LayoutGridItem>
|
||||||
|
);
|
||||||
|
}
|
@ -1,14 +1,18 @@
|
|||||||
import { GetProductOfferComposer } from '@nitrots/nitro-renderer';
|
import { GetProductOfferComposer, IFurnitureData } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useEffect } from 'react';
|
import { FC, useEffect } from 'react';
|
||||||
import { SendMessageHook } from '../../../../../../hooks/messages/message-event';
|
import { Grid, GridProps } from '../../../../../common/Grid';
|
||||||
import { NitroCardGridView } from '../../../../../../layout';
|
import { SendMessageHook } from '../../../../../hooks/messages/message-event';
|
||||||
import { useCatalogContext } from '../../../../context/CatalogContext';
|
import { useCatalogContext } from '../../../context/CatalogContext';
|
||||||
import { CatalogSearchResultOfferView } from '../offer/CatalogSearchResultOfferView';
|
import { CatalogSearchResultOfferView } from './CatalogSearchResultOfferView';
|
||||||
import { CatalogSearchResultOffersViewProps } from './CatalogSearchResultOffersView.types';
|
|
||||||
|
export interface CatalogSearchResultOffersViewProps extends GridProps
|
||||||
|
{
|
||||||
|
offers: IFurnitureData[];
|
||||||
|
}
|
||||||
|
|
||||||
export const CatalogSearchResultOffersView: FC<CatalogSearchResultOffersViewProps> = props =>
|
export const CatalogSearchResultOffersView: FC<CatalogSearchResultOffersViewProps> = props =>
|
||||||
{
|
{
|
||||||
const { offers = [], ...rest } = props;
|
const { offers = [], children = null, ...rest } = props;
|
||||||
const { catalogState = null } = useCatalogContext();
|
const { catalogState = null } = useCatalogContext();
|
||||||
const { activeOffer = null } = catalogState;
|
const { activeOffer = null } = catalogState;
|
||||||
|
|
||||||
@ -20,13 +24,14 @@ export const CatalogSearchResultOffersView: FC<CatalogSearchResultOffersViewProp
|
|||||||
}, [ offers ]);
|
}, [ offers ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NitroCardGridView { ...rest }>
|
<Grid grow columnCount={ 5 } overflow="auto" { ...rest }>
|
||||||
{ offers && (offers.length > 0) && offers.map((offer, index) =>
|
{ offers && (offers.length > 0) && offers.map((offer, index) =>
|
||||||
{
|
{
|
||||||
const isActive = (activeOffer && (activeOffer.products[0].furniClassId === offer.id));
|
const isActive = (activeOffer && (activeOffer.products[0].furniClassId === offer.id));
|
||||||
|
|
||||||
return <CatalogSearchResultOfferView key={ index } isActive={ isActive } offer={ offer } />
|
return <CatalogSearchResultOfferView key={ index } itemActive={ isActive } offer={ offer } />
|
||||||
})}
|
})}
|
||||||
</NitroCardGridView>
|
{ children }
|
||||||
|
</Grid>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -1,12 +1,14 @@
|
|||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { IFurnitureData, INodeData } from '@nitrots/nitro-renderer';
|
import { IFurnitureData, INodeData } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback, useEffect, useState } from 'react';
|
import { FC, useCallback, useEffect, useState } from 'react';
|
||||||
import { GetSessionDataManager, LocalizeText } from '../../../../api';
|
import { GetSessionDataManager, LocalizeText } from '../../../../api';
|
||||||
|
import { Button } from '../../../../common/Button';
|
||||||
|
import { Flex } from '../../../../common/Flex';
|
||||||
import { GetOfferNodes } from '../../common/CatalogUtilities';
|
import { GetOfferNodes } from '../../common/CatalogUtilities';
|
||||||
import { useCatalogContext } from '../../context/CatalogContext';
|
import { useCatalogContext } from '../../context/CatalogContext';
|
||||||
import { CatalogActions } from '../../reducers/CatalogReducer';
|
import { CatalogActions } from '../../reducers/CatalogReducer';
|
||||||
import { CatalogSearchViewProps } from './CatalogSearchView.types';
|
|
||||||
|
|
||||||
export const CatalogSearchView: FC<CatalogSearchViewProps> = props =>
|
export const CatalogSearchView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const [ searchValue, setSearchValue ] = useState('');
|
const [ searchValue, setSearchValue ] = useState('');
|
||||||
const { catalogState = null, dispatchCatalogState = null } = useCatalogContext();
|
const { catalogState = null, dispatchCatalogState = null } = useCatalogContext();
|
||||||
@ -110,15 +112,11 @@ export const CatalogSearchView: FC<CatalogSearchViewProps> = props =>
|
|||||||
}, [ searchValue, processSearch ]);
|
}, [ searchValue, processSearch ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="d-flex">
|
<Flex gap={ 1 }>
|
||||||
<div className="d-flex flex-grow-1 me-1">
|
<input type="text" className="form-control form-control-sm" placeholder={ LocalizeText('generic.search') } value={ searchValue } onChange={ event => setSearchValue(event.target.value) } />
|
||||||
<input type="text" className="form-control form-control-sm" placeholder={ LocalizeText('generic.search') } value={ searchValue } onChange={ event => setSearchValue(event.target.value) } />
|
<Button variant="primary" size="sm">
|
||||||
</div>
|
<FontAwesomeIcon icon="search" />
|
||||||
<div className="d-flex">
|
</Button>
|
||||||
<button type="button" className="btn btn-primary btn-sm">
|
</Flex>
|
||||||
<i className="fas fa-search"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -1,4 +0,0 @@
|
|||||||
export interface CatalogMessageHandlerProps
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
.catalog-icon-image {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
min-width: 20px;
|
|
||||||
min-height: 20px;
|
|
||||||
background-position: center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
import { FC } from 'react';
|
|
||||||
import { GetConfiguration } from '../../../../api';
|
|
||||||
import { CatalogIconViewProps } from './CatalogIconView.types';
|
|
||||||
|
|
||||||
export const CatalogIconView: FC<CatalogIconViewProps> = props =>
|
|
||||||
{
|
|
||||||
const { icon = 0 } = props;
|
|
||||||
|
|
||||||
function getIconUrl(): string
|
|
||||||
{
|
|
||||||
return ((GetConfiguration<string>('catalog.asset.icon.url')).replace('%name%', icon.toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = `url('${ getIconUrl() }')`;
|
|
||||||
|
|
||||||
return <div className="catalog-icon-image" style={ (url && url.length) ? { backgroundImage: url } : {} }></div>;
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
export interface CatalogIconViewProps
|
|
||||||
{
|
|
||||||
icon: number;
|
|
||||||
}
|
|
@ -1,239 +0,0 @@
|
|||||||
import { PurchaseFromCatalogAsGiftComposer } from '@nitrots/nitro-renderer';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
|
||||||
import { GetSessionDataManager, LocalizeText } from '../../../../api';
|
|
||||||
import { CatalogEvent } from '../../../../events';
|
|
||||||
import { CatalogInitGiftEvent } from '../../../../events/catalog/CatalogInitGiftEvent';
|
|
||||||
import { SendMessageHook, useUiEvent } from '../../../../hooks';
|
|
||||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView, NitroLayoutGiftCardView } from '../../../../layout';
|
|
||||||
import { CurrencyIcon } from '../../../shared/currency-icon/CurrencyIcon';
|
|
||||||
import { FurniImageView } from '../../../shared/furni-image/FurniImageView';
|
|
||||||
import { useCatalogContext } from '../../context/CatalogContext';
|
|
||||||
|
|
||||||
export const CatalogGiftView: FC<{}> = props =>
|
|
||||||
{
|
|
||||||
const { catalogState = null } = useCatalogContext();
|
|
||||||
const { giftConfiguration = null } = catalogState;
|
|
||||||
|
|
||||||
const [ isVisible, setIsVisible ] = useState<boolean>(false);
|
|
||||||
const [ pageId, setPageId ] = useState<number>(0);
|
|
||||||
const [ offerId, setOfferId ] = useState<number>(0);
|
|
||||||
|
|
||||||
const [ receiverName, setReceiverName ] = useState<string>('');
|
|
||||||
const [ showMyFace, setShowMyFace ] = useState<boolean>(true);
|
|
||||||
const [ message, setMessage ] = useState<string>('');
|
|
||||||
const [ colors, setColors ] = useState<{ id: number, color: string }[]>([]);
|
|
||||||
const [ selectedBoxIndex, setSelectedBoxIndex ] = useState<number>(0);
|
|
||||||
const [ selectedRibbonIndex, setSelectedRibbonIndex ] = useState<number>(0);
|
|
||||||
const [ selectedColorId, setSelectedColorId ] = useState<number>(0);
|
|
||||||
const [ maxBoxIndex, setMaxBoxIndex ] = useState<number>(0);
|
|
||||||
const [ maxRibbonIndex, setMaxRibbonIndex ] = useState<number>(0);
|
|
||||||
|
|
||||||
const [ receiverNotFound, setReceiverNotFound ] = useState<boolean>(false);
|
|
||||||
|
|
||||||
useEffect(() =>
|
|
||||||
{
|
|
||||||
setReceiverNotFound(false);
|
|
||||||
}, [ receiverName ]);
|
|
||||||
|
|
||||||
useEffect(() =>
|
|
||||||
{
|
|
||||||
if(!giftConfiguration) return;
|
|
||||||
|
|
||||||
setMaxBoxIndex(giftConfiguration.boxTypes.length - 1);
|
|
||||||
setMaxRibbonIndex(giftConfiguration.ribbonTypes.length - 1);
|
|
||||||
|
|
||||||
const newColors: { id: number, color: string }[] = [];
|
|
||||||
|
|
||||||
for(const colorId of giftConfiguration.stuffTypes)
|
|
||||||
{
|
|
||||||
const giftData = GetSessionDataManager().getFloorItemData(colorId);
|
|
||||||
|
|
||||||
if(!giftData) continue;
|
|
||||||
|
|
||||||
if(giftData.colors && giftData.colors.length > 0) newColors.push({ id: colorId, color: `#${giftData.colors[0].toString(16)}` });
|
|
||||||
}
|
|
||||||
|
|
||||||
if(newColors.length)
|
|
||||||
{
|
|
||||||
setSelectedColorId(newColors[0].id);
|
|
||||||
setColors(newColors);
|
|
||||||
}
|
|
||||||
}, [ giftConfiguration ]);
|
|
||||||
|
|
||||||
const close = useCallback(() =>
|
|
||||||
{
|
|
||||||
setIsVisible(false);
|
|
||||||
setPageId(0);
|
|
||||||
setOfferId(0);
|
|
||||||
setReceiverName('');
|
|
||||||
setShowMyFace(true);
|
|
||||||
setMessage('');
|
|
||||||
setSelectedBoxIndex(0);
|
|
||||||
setSelectedRibbonIndex(0);
|
|
||||||
|
|
||||||
if(colors.length) setSelectedColorId(colors[0].id);
|
|
||||||
}, [ colors ]);
|
|
||||||
|
|
||||||
const onCatalogEvent = useCallback((event: CatalogEvent) =>
|
|
||||||
{
|
|
||||||
switch(event.type)
|
|
||||||
{
|
|
||||||
case CatalogEvent.PURCHASE_SUCCESS:
|
|
||||||
close();
|
|
||||||
return;
|
|
||||||
case CatalogEvent.INIT_GIFT:
|
|
||||||
const castedEvent = (event as CatalogInitGiftEvent);
|
|
||||||
close();
|
|
||||||
|
|
||||||
setPageId(castedEvent.pageId);
|
|
||||||
setOfferId(castedEvent.offerId);
|
|
||||||
setIsVisible(true);
|
|
||||||
return;
|
|
||||||
case CatalogEvent.GIFT_RECEIVER_NOT_FOUND:
|
|
||||||
setReceiverNotFound(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}, [ close ]);
|
|
||||||
|
|
||||||
useUiEvent(CatalogEvent.PURCHASE_SUCCESS, onCatalogEvent);
|
|
||||||
useUiEvent(CatalogEvent.INIT_GIFT, onCatalogEvent);
|
|
||||||
useUiEvent(CatalogEvent.GIFT_RECEIVER_NOT_FOUND, onCatalogEvent);
|
|
||||||
|
|
||||||
const isBoxDefault = useMemo(() =>
|
|
||||||
{
|
|
||||||
return giftConfiguration ? giftConfiguration.defaultStuffTypes.findIndex(s => s === giftConfiguration.boxTypes[selectedBoxIndex]) > -1 : true;
|
|
||||||
}, [ giftConfiguration, selectedBoxIndex ]);
|
|
||||||
|
|
||||||
const boxName = useMemo(() =>
|
|
||||||
{
|
|
||||||
return isBoxDefault ? 'catalog.gift_wrapping_new.box.default' : `catalog.gift_wrapping_new.box.${selectedBoxIndex}`;
|
|
||||||
}, [ isBoxDefault, selectedBoxIndex ]);
|
|
||||||
|
|
||||||
const ribbonName = useMemo(() =>
|
|
||||||
{
|
|
||||||
return `catalog.gift_wrapping_new.ribbon.${selectedRibbonIndex}`;
|
|
||||||
}, [ selectedRibbonIndex ]);
|
|
||||||
|
|
||||||
const priceText = useMemo(() =>
|
|
||||||
{
|
|
||||||
return isBoxDefault ? 'catalog.gift_wrapping_new.freeprice' : 'catalog.gift_wrapping_new.price';
|
|
||||||
}, [ isBoxDefault ]);
|
|
||||||
|
|
||||||
const extraData = useMemo(() =>
|
|
||||||
{
|
|
||||||
if(!giftConfiguration) return '';
|
|
||||||
|
|
||||||
return ((giftConfiguration.boxTypes[selectedBoxIndex] * 1000) + giftConfiguration.ribbonTypes[selectedRibbonIndex]).toString();
|
|
||||||
}, [ giftConfiguration, selectedBoxIndex, selectedRibbonIndex ]);
|
|
||||||
|
|
||||||
const isColorable = useMemo(() =>
|
|
||||||
{
|
|
||||||
if(!giftConfiguration) return false;
|
|
||||||
|
|
||||||
const boxType = giftConfiguration.boxTypes[selectedBoxIndex];
|
|
||||||
|
|
||||||
return (boxType === 8 || (boxType >= 3 && boxType <= 6)) ? false : true;
|
|
||||||
}, [ giftConfiguration, selectedBoxIndex ]);
|
|
||||||
|
|
||||||
const handleAction = useCallback((action: string) =>
|
|
||||||
{
|
|
||||||
switch(action)
|
|
||||||
{
|
|
||||||
case 'prev_box':
|
|
||||||
setSelectedBoxIndex(value =>
|
|
||||||
{
|
|
||||||
return (value === 0 ? maxBoxIndex : value - 1);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
case 'next_box':
|
|
||||||
setSelectedBoxIndex(value =>
|
|
||||||
{
|
|
||||||
return (value === maxBoxIndex ? 0 : value + 1);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
case 'prev_ribbon':
|
|
||||||
setSelectedRibbonIndex(value =>
|
|
||||||
{
|
|
||||||
return (value === 0 ? maxRibbonIndex : value - 1);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
case 'next_ribbon':
|
|
||||||
setSelectedRibbonIndex(value =>
|
|
||||||
{
|
|
||||||
return (value === maxRibbonIndex ? 0 : value + 1);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
case 'buy':
|
|
||||||
if(!receiverName || receiverName.length === 0)
|
|
||||||
{
|
|
||||||
setReceiverNotFound(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SendMessageHook(new PurchaseFromCatalogAsGiftComposer(pageId, offerId, extraData, receiverName, message, selectedColorId, selectedBoxIndex, selectedRibbonIndex, showMyFace));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}, [ extraData, maxBoxIndex, maxRibbonIndex, message, offerId, pageId, receiverName, selectedBoxIndex, selectedColorId, selectedRibbonIndex, showMyFace ]);
|
|
||||||
|
|
||||||
if(!giftConfiguration || !giftConfiguration.isEnabled || !isVisible) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<NitroCardView uniqueKey="catalog-gift" className="nitro-catalog-gift" simple={ true }>
|
|
||||||
<NitroCardHeaderView headerText={ LocalizeText('catalog.gift_wrapping.title') } onCloseClick={ close } />
|
|
||||||
<NitroCardContentView className="text-black">
|
|
||||||
<div className="form-group">
|
|
||||||
<label>{ LocalizeText('catalog.gift_wrapping.receiver') }</label>
|
|
||||||
<input type="text" className={ 'form-control form-control-sm' + classNames({ ' is-invalid': receiverNotFound }) } value={ receiverName } onChange={ (e) => setReceiverName(e.target.value) } />
|
|
||||||
{ receiverNotFound && <div className="invalid-feedback">{ LocalizeText('catalog.gift_wrapping.receiver_not_found.title') }</div> }
|
|
||||||
</div>
|
|
||||||
<div className="mt-2">
|
|
||||||
<NitroLayoutGiftCardView figure={ GetSessionDataManager().figure } userName={ GetSessionDataManager().userName } message={ message } editable={ true } onChange={ (value) => setMessage(value) } />
|
|
||||||
</div>
|
|
||||||
<div className="form-check mt-1">
|
|
||||||
<input className="form-check-input" type="checkbox" name="showMyFace" checked={ showMyFace } onChange={ (e) => setShowMyFace(value => !value) } />
|
|
||||||
<label className="form-check-label">{ LocalizeText('catalog.gift_wrapping.show_face.title') }</label>
|
|
||||||
</div>
|
|
||||||
<div className="d-flex gap-2 mt-1 align-items-center">
|
|
||||||
<div className="gift-preview">
|
|
||||||
{ selectedColorId && <FurniImageView spriteId={ selectedColorId } type="s" extras={ extraData } /> }
|
|
||||||
</div>
|
|
||||||
<div className="d-flex flex-column gap-2">
|
|
||||||
<div className="d-flex gap-2">
|
|
||||||
<div className="btn-group">
|
|
||||||
<button className="btn btn-primary" onClick={ () => handleAction('prev_box') }><i className="fas fa-chevron-left" /></button>
|
|
||||||
<button className="btn btn-primary" onClick={ () => handleAction('next_box') }><i className="fas fa-chevron-right" /></button>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="fw-bold">{ LocalizeText(boxName) }</div>
|
|
||||||
<div className="d-flex align-items-center gap-1">{ LocalizeText(priceText, ['price'], [giftConfiguration.price.toString()]) }<CurrencyIcon type={ -1 } /></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="d-flex gap-2 align-items-center">
|
|
||||||
<div className="btn-group">
|
|
||||||
<button className="btn btn-primary" onClick={ () => handleAction('prev_ribbon') }><i className="fas fa-chevron-left" /></button>
|
|
||||||
<button className="btn btn-primary" onClick={ () => handleAction('next_ribbon') }><i className="fas fa-chevron-right" /></button>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="fw-bold">{ LocalizeText(ribbonName) }</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mt-1">
|
|
||||||
<div className="fw-bold">{ LocalizeText('catalog.gift_wrapping.pick_color') }</div>
|
|
||||||
<div className="btn-group w-100">
|
|
||||||
{ colors.map(color =>
|
|
||||||
{
|
|
||||||
return <button key={ color.id } className={ 'btn btn-dark btn-sm' + classNames({ ' active': color.id === selectedColorId }) } disabled={ !isColorable } style={{ backgroundColor: color.color }} onClick={ () => setSelectedColorId(color.id) }></button>
|
|
||||||
}) }
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="d-flex justify-content-between align-items-center mt-2">
|
|
||||||
<div className="text-decoration-underline cursor-pointer" onClick={ close }>{ LocalizeText('cancel') }</div>
|
|
||||||
<button className="btn btn-success" onClick={ () => handleAction('buy') }>{ LocalizeText('catalog.gift_wrapping.give_gift') }</button>
|
|
||||||
</div>
|
|
||||||
</NitroCardContentView>
|
|
||||||
</NitroCardView>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,9 +0,0 @@
|
|||||||
import { INodeData } from '@nitrots/nitro-renderer';
|
|
||||||
import { Dispatch, SetStateAction } from 'react';
|
|
||||||
|
|
||||||
export interface CatalogNavigationViewProps
|
|
||||||
{
|
|
||||||
page: INodeData;
|
|
||||||
pendingTree: INodeData[];
|
|
||||||
setPendingTree: Dispatch<SetStateAction<INodeData[]>>;
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
import { INodeData } from '@nitrots/nitro-renderer';
|
|
||||||
import { Dispatch, SetStateAction } from 'react';
|
|
||||||
|
|
||||||
export interface CatalogNavigationItemViewProps
|
|
||||||
{
|
|
||||||
page: INodeData;
|
|
||||||
isActive: boolean;
|
|
||||||
pendingTree: INodeData[];
|
|
||||||
setPendingTree: Dispatch<SetStateAction<INodeData[]>>;
|
|
||||||
setActiveChild: Dispatch<SetStateAction<INodeData>>;
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
import { INodeData } from '@nitrots/nitro-renderer';
|
|
||||||
import { Dispatch, SetStateAction } from 'react';
|
|
||||||
|
|
||||||
export interface CatalogNavigationSetViewProps
|
|
||||||
{
|
|
||||||
page: INodeData;
|
|
||||||
isFirstSet?: boolean;
|
|
||||||
pendingTree: INodeData[];
|
|
||||||
setPendingTree: Dispatch<SetStateAction<INodeData[]>>;
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
import { RoomPreviewer } from '@nitrots/nitro-renderer';
|
|
||||||
|
|
||||||
export interface CatalogPageViewProps
|
|
||||||
{
|
|
||||||
roomPreviewer: RoomPreviewer;
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
@import './frontpage4/CatalogLayoutFrontpage4View';
|
|
||||||
@import './info-loyalty/CatalogLayoutInfoLoyaltyView.scss';
|
|
||||||
@import './vip-buy/CatalogLayoutVipBuyView';
|
|
||||||
@import './marketplace/marketplace-item/MarketplaceItemView';
|
|
||||||
@import './marketplace/post-offer/MarketplacePostOfferView';
|
|
@ -1,6 +0,0 @@
|
|||||||
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
|
||||||
|
|
||||||
export interface CatalogLayoutBadgeDisplayViewProps extends CatalogLayoutProps
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
|
||||||
|
|
||||||
export interface CatalogLayoutDefaultViewProps extends CatalogLayoutProps
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
.nitro-front-page-item {
|
|
||||||
background-position: center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.front-page-item-caption {
|
|
||||||
position: absolute;
|
|
||||||
background: rgba(0, 0, 0, .5);
|
|
||||||
color: #fff;
|
|
||||||
font-size: 16px;
|
|
||||||
border-radius: 5px;
|
|
||||||
margin: 10px;
|
|
||||||
padding: 5px 15px;
|
|
||||||
bottom: 0;
|
|
||||||
text-shadow: 2px 2px rgba(0, 0, 0, .2);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
|
||||||
|
|
||||||
export interface CatalogLayoutFrontpage4ViewProps extends CatalogLayoutProps
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
import { FC, useMemo } from 'react';
|
|
||||||
import { GetConfiguration } from '../../../../../../../api';
|
|
||||||
import { NitroLayoutBase } from '../../../../../../../layout/base';
|
|
||||||
import { CatalogLayoutFrontPageItemViewProps } from './CatalogLayoutFrontPageItemView.types';
|
|
||||||
|
|
||||||
export const CatalogLayoutFrontPageItemView: FC<CatalogLayoutFrontPageItemViewProps> = props =>
|
|
||||||
{
|
|
||||||
const { item = null, className = '', style = null, ...rest } = props;
|
|
||||||
|
|
||||||
const getClassName = useMemo(() =>
|
|
||||||
{
|
|
||||||
let newClassName = 'position-relative rounded h-100 nitro-front-page-item';
|
|
||||||
|
|
||||||
if(className && className.length) newClassName += ' ' + className;
|
|
||||||
|
|
||||||
return newClassName;
|
|
||||||
}, [ className ]);
|
|
||||||
|
|
||||||
const getStyle = useMemo(() =>
|
|
||||||
{
|
|
||||||
const newStyle = { ...style };
|
|
||||||
|
|
||||||
newStyle.backgroundImage = `url('${ GetConfiguration<string>('image.library.url') }${ item.itemPromoImage }')`;
|
|
||||||
|
|
||||||
return newStyle;
|
|
||||||
}, [ style, item ]);
|
|
||||||
|
|
||||||
if(!item) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<NitroLayoutBase className={ getClassName } style={ getStyle } { ...rest }>
|
|
||||||
<div className="front-page-item-caption">{ item.itemName }</div>
|
|
||||||
</NitroLayoutBase>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
import { FrontPageItem } from '@nitrots/nitro-renderer';
|
|
||||||
import { DetailedHTMLProps, HTMLAttributes } from 'react';
|
|
||||||
|
|
||||||
export interface CatalogLayoutFrontPageItemViewProps extends DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>
|
|
||||||
{
|
|
||||||
item: FrontPageItem;
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
|
||||||
|
|
||||||
export interface CatalogLayoutGuildCustomFurniViewProps extends CatalogLayoutProps
|
|
||||||
{}
|
|
@ -1,4 +0,0 @@
|
|||||||
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
|
||||||
|
|
||||||
export interface CatalogLayoutGuildForumViewProps extends CatalogLayoutProps
|
|
||||||
{}
|
|
@ -1,29 +0,0 @@
|
|||||||
import { FC } from 'react';
|
|
||||||
import { CreateLinkEvent, LocalizeText } from '../../../../../../api';
|
|
||||||
import { NitroLayoutGrid, NitroLayoutGridColumn } from '../../../../../../layout';
|
|
||||||
import { NitroLayoutBase } from '../../../../../../layout/base';
|
|
||||||
import { GetCatalogPageImage, GetCatalogPageText } from '../../../../common/CatalogUtilities';
|
|
||||||
import { CatalogLayoutGuildFrontpageViewProps } from './CatalogLayoutGuildFrontpageView.types';
|
|
||||||
|
|
||||||
export const CatalogLayouGuildFrontpageView: FC<CatalogLayoutGuildFrontpageViewProps> = props =>
|
|
||||||
{
|
|
||||||
const { pageParser = null } = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<NitroLayoutGrid>
|
|
||||||
<NitroLayoutGridColumn className="bg-muted rounded p-2 text-black overflow-hidden" size={ 7 }>
|
|
||||||
<NitroLayoutBase dangerouslySetInnerHTML={ { __html: GetCatalogPageText(pageParser, 2) } } />
|
|
||||||
<NitroLayoutBase className="overflow-auto" dangerouslySetInnerHTML={ { __html: GetCatalogPageText(pageParser, 0) } } />
|
|
||||||
<NitroLayoutBase dangerouslySetInnerHTML={ { __html: GetCatalogPageText(pageParser, 1) } } />
|
|
||||||
</NitroLayoutGridColumn>
|
|
||||||
<NitroLayoutGridColumn size={ 5 }>
|
|
||||||
<div className="d-block mb-2">
|
|
||||||
<img alt="" src={ GetCatalogPageImage(pageParser, 1) } />
|
|
||||||
</div>
|
|
||||||
<div className="col position-relative d-flex flex-column justify-content-center" onClick={ () => CreateLinkEvent('groups/create') }>
|
|
||||||
<button className="btn btn-sm btn-primary mt-1">{ LocalizeText('catalog.start.guild.purchase.button') }</button>
|
|
||||||
</div>
|
|
||||||
</NitroLayoutGridColumn>
|
|
||||||
</NitroLayoutGrid>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
|
||||||
|
|
||||||
export interface CatalogLayoutGuildFrontpageViewProps extends CatalogLayoutProps
|
|
||||||
{}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user