Catalog layout updates

This commit is contained in:
Bill 2021-09-22 22:16:09 -04:00
parent 34ab1e6fd3
commit e94977f775
34 changed files with 263 additions and 327 deletions

View File

@ -15,6 +15,10 @@
min-height: 28px;
}
textarea {
resize: none;
}
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {

View File

@ -4,7 +4,7 @@ import { NitroCardGridItemViewProps } from './NitroCardGridItemView.types';
export const NitroCardGridItemView: FC<NitroCardGridItemViewProps> = props =>
{
const { itemImage = undefined, itemColor = undefined, itemActive = false, itemCount = 1, itemUniqueNumber = 0, itemUnseen = false, className = '', style = {}, children = null, ...rest } = props;
const { itemImage = undefined, itemColor = undefined, itemActive = false, itemCount = 1, itemUniqueNumber = -2, itemUnseen = false, className = '', style = {}, children = null, ...rest } = props;
const getClassName = useMemo(() =>
{
@ -12,6 +12,8 @@ export const NitroCardGridItemView: FC<NitroCardGridItemViewProps> = props =>
if(itemActive) newClassName += ' active';
if(itemUniqueNumber === -1) newClassName += ' unique-item sold-out';
if(itemUniqueNumber > 0) newClassName += ' unique-item';
if(itemUnseen) newClassName += ' unseen';

View File

@ -0,0 +1,47 @@
import { FurnitureType } from '@nitrots/nitro-renderer';
import { GetConfiguration, GetRoomEngine, GetSessionDataManager } from '../../../api';
export const GetProductIconUrl = (furniClassId: number, productType: string, customParams: string = null) =>
{
switch(productType.toUpperCase())
{
case FurnitureType.BADGE:
return GetSessionDataManager().getBadgeUrl(customParams);
case FurnitureType.ROBOT:
return undefined;
case FurnitureType.FLOOR:
return GetRoomEngine().getFurnitureFloorIconUrl(furniClassId);
case FurnitureType.WALL: {
const furniData = GetSessionDataManager().getWallItemData(furniClassId);
let iconName = '';
if(furniData)
{
switch(furniData.className)
{
case 'floor':
iconName = [ 'th', furniData.className, customParams ].join('_');
break;
case 'wallpaper':
iconName = [ 'th', 'wall', customParams ].join('_');
break;
case 'landscape':
iconName = [ 'th', furniData.className, customParams.replace('.', '_'), '001' ].join('_');
break;
}
if(iconName !== '')
{
const assetUrl = GetConfiguration<string>('catalog.asset.url');
return `${ assetUrl }/${ iconName }.png`;
}
}
return GetRoomEngine().getFurnitureWallIconUrl(furniClassId, customParams);
}
}
return null;
}

View File

@ -0,0 +1,19 @@
import { FC } from 'react';
import { GetCatalogPageImage, GetCatalogPageText } from '../../common/CatalogUtilities';
import { CatalogPageDetailsViewProps } from './CatalogPageDetailsView.types';
export const CatalogPageDetailsView: FC<CatalogPageDetailsViewProps> = props =>
{
const { pageParser = null } = props;
if(!pageParser) return null;
const imageUrl = GetCatalogPageImage(pageParser, 1);
return (
<div className="d-flex flex-column justify-content-center align-items-center gap-2 w-100 h-100">
{ imageUrl && <img alt="" src={ imageUrl } /> }
<div className="d-flex flex-column fs-6 text-center text-black lh-sm" dangerouslySetInnerHTML={ { __html: GetCatalogPageText(pageParser, 0) } } />
</div>
);
}

View File

@ -0,0 +1,6 @@
import { CatalogPageMessageParser } from '@nitrots/nitro-renderer';
export interface CatalogPageDetailsViewProps
{
pageParser: CatalogPageMessageParser;
}

View File

@ -1,6 +1,5 @@
@import './layout/CatalogLayout';
@import './offer/CatalogPageOfferView';
@import './offers/CatalogPageOffersView';
@import './product/CatalogProductView';
@import './purchase/CatalogPurchaseView';
@import './search-result/CatalogLayoutSearchResultView';

View File

@ -1,10 +1,5 @@
@import './badge-display/CatalogLayoutBadgeDisplayView';
@import './default/CatalogLayoutDefaultView';
@import './frontpage4/CatalogLayoutFrontpage4View';
@import './pets/CatalogLayoutPetView';
@import './pets3/CatalogLayoutPets3View';
@import './single-bundle/CatalogLayoutSingleBundleView';
@import './spaces-new/CatalogLayoutSpacesView';
@import './trophies/CatalogLayoutTrophiesView';
@import './vip-buy/CatalogLayoutVipBuyView';
@import './info-loyalty/CatalogLayoutInfoLoyaltyView.scss';

View File

@ -1,7 +0,0 @@
.nitro-catalog-layout-badge-display {
.inventory-badge-grid {
height: 200px;
max-height: 200px;
}
}

View File

@ -1,5 +1,5 @@
import { StringDataType } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { FC, useCallback, useEffect, useState } from 'react';
import { LocalizeText } from '../../../../../../api';
import { InventoryBadgesUpdatedEvent, SetRoomPreviewerStuffDataEvent } from '../../../../../../events';
import { InventoryBadgesRequestEvent } from '../../../../../../events/inventory/InventoryBadgesRequestEvent';
@ -7,11 +7,9 @@ import { dispatchUiEvent, useUiEvent } from '../../../../../../hooks';
import { NitroCardGridItemView } from '../../../../../../layout/card/grid/item/NitroCardGridItemView';
import { NitroCardGridView } from '../../../../../../layout/card/grid/NitroCardGridView';
import { BadgeImageView } from '../../../../../shared/badge-image/BadgeImageView';
import { GetOfferName } from '../../../../common/CatalogUtilities';
import { useCatalogContext } from '../../../../context/CatalogContext';
import { CatalogRoomPreviewerView } from '../../../catalog-room-previewer/CatalogRoomPreviewerView';
import { CatalogPageOffersView } from '../../offers/CatalogPageOffersView';
import { CatalogPurchaseView } from '../../purchase/CatalogPurchaseView';
import { CatalogProductPreviewView } from '../../product-preview/CatalogProductPreviewView';
import { CatalogLayoutBadgeDisplayViewProps } from './CatalogLayoutBadgeDisplayView.types';
export const CatalogLayoutBadgeDisplayView: FC<CatalogLayoutBadgeDisplayViewProps> = props =>
@ -32,18 +30,6 @@ export const CatalogLayoutBadgeDisplayView: FC<CatalogLayoutBadgeDisplayViewProp
useUiEvent(InventoryBadgesUpdatedEvent.BADGES_UPDATED, onInventoryBadgesUpdatedEvent);
const badgeElements = useMemo(() =>
{
return badges.map(code =>
{
return (
<NitroCardGridItemView key={ code } itemActive={ (currentBadge === code) } onMouseDown={ event => setCurrentBadge(code) }>
<BadgeImageView badgeCode={ code } />
</NitroCardGridItemView>
);
});
}, [ badges, currentBadge ]);
useEffect(() =>
{
dispatchUiEvent(new InventoryBadgesRequestEvent(InventoryBadgesRequestEvent.REQUEST_BADGES));
@ -67,22 +53,26 @@ export const CatalogLayoutBadgeDisplayView: FC<CatalogLayoutBadgeDisplayViewProp
}, [ currentBadge, activeOffer, roomPreviewer ]);
return (
<div className="row h-100 nitro-catalog-layout-badge-display">
<div className="d-flex flex-column col-7 h-100">
<CatalogPageOffersView offers={ pageParser.offers } />
<div className="d-flex flex-column mt-2">
<div className="row h-100">
<div className="d-flex flex-column col-7 gap-2 h-100">
<CatalogPageOffersView className="flex-shrink-0" offers={ pageParser.offers } />
<div className="d-flex flex-column overflow-hidden">
<div className="text-black fw-bold">{ LocalizeText('catalog_selectbadge') }</div>
<NitroCardGridView className="inventory-badge-grid">
{ badgeElements }
<NitroCardGridView>
{ badges && (badges.length > 0) && badges.map(code =>
{
return (
<NitroCardGridItemView key={ code } itemActive={ (currentBadge === code) } onMouseDown={ event => setCurrentBadge(code) }>
<BadgeImageView badgeCode={ code } />
</NitroCardGridItemView>
);
}) }
</NitroCardGridView>
</div>
</div>
{ product &&
<div className="position-relative d-flex flex-column col">
<CatalogRoomPreviewerView roomPreviewer={ roomPreviewer } height={ 140 } />
<div className="fs-6 text-black mt-1 overflow-hidden">{ GetOfferName(activeOffer) }</div>
<CatalogPurchaseView offer={ activeOffer } pageId={ pageParser.pageId } extra={ currentBadge } disabled={ !currentBadge } />
</div> }
<div className="position-relative d-flex flex-column col-5">
<CatalogProductPreviewView pageParser={ pageParser } activeOffer={ activeOffer } roomPreviewer={ roomPreviewer } extra={ currentBadge } disabled={ !currentBadge } />
</div>
</div>
);
}

View File

@ -1,10 +1,7 @@
import { FC } from 'react';
import { LimitedEditionCompletePlateView } from '../../../../../shared/limited-edition/complete-plate/LimitedEditionCompletePlateView';
import { GetCatalogPageImage, GetCatalogPageText, GetOfferName } from '../../../../common/CatalogUtilities';
import { useCatalogContext } from '../../../../context/CatalogContext';
import { CatalogRoomPreviewerView } from '../../../catalog-room-previewer/CatalogRoomPreviewerView';
import { CatalogPageOffersView } from '../../offers/CatalogPageOffersView';
import { CatalogPurchaseView } from '../../purchase/CatalogPurchaseView';
import { CatalogProductPreviewView } from '../../product-preview/CatalogProductPreviewView';
import { CatalogLayoutDefaultViewProps } from './CatalogLayoutDefaultView.types';
export const CatalogLayoutDefaultView: FC<CatalogLayoutDefaultViewProps> = props =>
@ -17,24 +14,12 @@ export const CatalogLayoutDefaultView: FC<CatalogLayoutDefaultViewProps> = props
return (
<div className="row h-100">
<div className="col-7 h-100">
<div className="d-flex flex-column col-7 h-100">
<CatalogPageOffersView offers={ pageParser.offers } />
</div>
{ !product &&
<div className="position-relative d-flex flex-column col-5 justify-content-center align-items-center overflow-hidden">
<div className="d-block mb-2">
<img alt="" src={ GetCatalogPageImage(pageParser, 1) } />
</div>
<div className="fs-6 text-center text-black lh-sm overflow-hidden">{ GetCatalogPageText(pageParser, 0) }</div>
</div> }
{ product &&
<div className="position-relative d-flex flex-column col-5">
<CatalogRoomPreviewerView roomPreviewer={ roomPreviewer } height={ 140 } />
{ product.uniqueLimitedItem &&
<LimitedEditionCompletePlateView uniqueLimitedItemsLeft={ product.uniqueLimitedItemsLeft } uniqueLimitedSeriesSize={ product.uniqueLimitedSeriesSize } /> }
<div className="fs-6 text-black mt-1 overflow-hidden">{ GetOfferName(activeOffer) }</div>
<CatalogPurchaseView offer={ activeOffer } pageId={ pageParser.pageId } />
</div> }
<CatalogProductPreviewView pageParser={ pageParser } activeOffer={ activeOffer } roomPreviewer={ roomPreviewer } />
</div>
</div>
);
}

View File

@ -2,11 +2,13 @@ import { ColorConverter, GetSellablePetPalettesComposer, SellablePetPaletteData
import { FC, useEffect, useMemo, useState } from 'react';
import { GetProductDataForLocalization, LocalizeText } from '../../../../../../api';
import { SendMessageHook } from '../../../../../../hooks/messages/message-event';
import { NitroCardGridItemView, NitroCardGridView } from '../../../../../../layout';
import { PetImageView } from '../../../../../shared/pet-image/PetImageView';
import { GetCatalogPageImage, GetCatalogPageText, GetPetAvailableColors, GetPetIndexFromLocalization } from '../../../../common/CatalogUtilities';
import { GetPetAvailableColors, GetPetIndexFromLocalization } from '../../../../common/CatalogUtilities';
import { useCatalogContext } from '../../../../context/CatalogContext';
import { CatalogActions } from '../../../../reducers/CatalogReducer';
import { CatalogRoomPreviewerView } from '../../../catalog-room-previewer/CatalogRoomPreviewerView';
import { CatalogPageDetailsView } from '../../../page-details/CatalogPageDetailsView';
import { CatalogLayoutPetViewProps } from './CatalogLayoutPetView.types';
import { CatalogLayoutPetPurchaseView } from './purchase/CatalogLayoutPetPurchaseView';
@ -139,35 +141,30 @@ export const CatalogLayoutPetView: FC<CatalogLayoutPetViewProps> = props =>
if(!activeOffer) return null;
return (
<div className="row h-100 nitro-catalog-layout-pets">
<div className="col-7 h-100">
<div className="row row-cols-5 align-content-start g-0 mb-n1 w-100 h-100 overflow-auto catalog-offers-container single-bundle-items-container">
{ colorsShowing && (sellableColors.length > 0) && sellableColors.map((colorSet, index) =>
{
return <div key={ index } className="col pe-1 pb-1 catalog-offer-item-container">
<div className={ 'position-relative border border-2 rounded catalog-offer-item cursor-pointer ' + ((selectedColorIndex === index) ? 'active ' : '') } style={ { backgroundColor: ColorConverter.int2rgb(colorSet[0]) } } onClick={ event => setSelectedColorIndex(index) }>
</div>
</div>;
})}
<div className="row h-100">
<div className="d-flex flex-column col-7 h-100">
<NitroCardGridView columns={ 5 }>
{ !colorsShowing && (sellablePalettes.length > 0) && sellablePalettes.map((palette, index) =>
{
return <div key={ index } className="col pe-1 pb-1 catalog-offer-item-container">
<div className={ 'position-relative border border-2 rounded catalog-offer-item cursor-pointer ' + ((selectedPaletteIndex === index) ? 'active ' : '') } onClick={ event => setSelectedPaletteIndex(index) }>
return (
<NitroCardGridItemView key={ index } itemActive={ (selectedPaletteIndex === index) } onClick={ event => setSelectedPaletteIndex(index) }>
<PetImageView typeId={ petIndex } paletteId={ palette.paletteId } direction={ 2 } headOnly={ true } />
</div>
</div>;
</NitroCardGridItemView>
);
})}
{ colorsShowing && (sellableColors.length > 0) && sellableColors.map((colorSet, index) =>
{
return (
<NitroCardGridItemView key={ index } itemActive={ (selectedColorIndex === index) } itemColor={ ColorConverter.int2rgb(colorSet[0]) } onClick={ event => setSelectedColorIndex(index) } />
);
})}
</NitroCardGridView>
</div>
</div>
{ (petIndex === -1) &&
<div className="position-relative d-flex flex-column col-5 justify-content-center align-items-center">
<div className="d-block mb-2">
<img alt="" src={ GetCatalogPageImage(pageParser, 1) } />
</div>
<div className="fs-6 text-center text-black lh-sm overflow-hidden">{ GetCatalogPageText(pageParser, 0) }</div>
</div> }
{ (petIndex >= 0) &&
<div className="position-relative d-flex flex-column col-5">
{ (petIndex === -1) &&
<CatalogPageDetailsView pageParser={ pageParser } /> }
{ (petIndex >= 0) &&
<>
<CatalogRoomPreviewerView roomPreviewer={ roomPreviewer } height={ 140 }>
{ (petIndex > -1 && petIndex <= 7) &&
<button type="button" className= { 'btn btn-primary btn-sm color-button ' + (colorsShowing ? 'active ' : '') } onClick={ event => setColorsShowing(!colorsShowing) }>
@ -176,7 +173,8 @@ export const CatalogLayoutPetView: FC<CatalogLayoutPetViewProps> = props =>
</CatalogRoomPreviewerView>
<div className="fs-6 text-black mt-1 overflow-hidden">{ petBreedName }</div>
<CatalogLayoutPetPurchaseView offer={ activeOffer } pageId={ pageParser.pageId } extra={ petPurchaseString } />
</div> }
</> }
</div>
</div>
);
}

View File

@ -6,6 +6,7 @@ import { useUiEvent } from '../../../../../../../hooks/events/ui/ui-event';
import { SendMessageHook } from '../../../../../../../hooks/messages/message-event';
import { CurrencyIcon } from '../../../../../../shared/currency-icon/CurrencyIcon';
import { CatalogPurchaseButtonView } from '../../../purchase/purchase-button/CatalogPurchaseButtonView';
import { CatalogPurchaseGiftButtonView } from '../../../purchase/purchase-gift-button/CatalogPurchaseGiftButtonView';
import { CatalogPetNameApprovalView } from '../name-approval/CatalogPetNameApprovalView';
import { CatalogLayoutPetPurchaseViewProps } from './CatalogLayoutPetPurchaseView.types';
@ -59,6 +60,8 @@ export const CatalogLayoutPetPurchaseView: FC<CatalogLayoutPetPurchaseViewProps>
</div>
<div className="d-flex flex-column mt-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 } /> }
</div>
</div>
</>

View File

@ -1,2 +0,0 @@
.nitro-catalog-layout-pets2 {
}

View File

@ -1,23 +1,8 @@
import { FC } from 'react';
import { GetCatalogPageImage, GetCatalogPageText } from '../../../../common/CatalogUtilities';
import { CatalogLayoutPets3View } from '../pets3/CatalogLayoutPets3View';
import { CatalogLayoutPets2ViewProps } from './CatalogLayoutPets2View.types';
export const CatalogLayoutPets2View: FC<CatalogLayoutPets2ViewProps> = props =>
{
const { pageParser = null } = props;
return (
<div className="h-100 nitro-catalog-layout-pets3 bg-muted p-1 rounded text-black">
<div className="overflow-auto h-100 d-flex flex-column">
<div className="d-flex flex-row">
<div className="d-block mb-1 me-1">
<img alt="" src={ GetCatalogPageImage(pageParser, 1) } />
</div>
<h6 className="align-self-center">{ GetCatalogPageText(pageParser, 1) }</h6>
</div>
<div dangerouslySetInnerHTML={ { __html: GetCatalogPageText(pageParser, 2) } } />
{GetCatalogPageText(pageParser, 3) && <div className="mt-auto bg-secondary text-white rounded p-1 text-center" dangerouslySetInnerHTML={{ __html: GetCatalogPageText(pageParser, 3) }} />}
</div>
</div>
);
return <CatalogLayoutPets3View { ...props } />
}

View File

@ -1,2 +0,0 @@
.nitro-catalog-layout-pets3 {
}

View File

@ -6,17 +6,23 @@ export const CatalogLayoutPets3View: FC<CatalogLayoutPets3ViewProps> = props =>
{
const { pageParser = null } = props;
const imageUrl = GetCatalogPageImage(pageParser, 1);
return (
<div className="h-100 nitro-catalog-layout-pets3 bg-muted p-1 rounded text-black">
<div className="overflow-auto h-100 d-flex flex-column">
<div className="d-flex flex-row">
<div className="d-block mb-1 me-1">
<img alt="" src={ GetCatalogPageImage(pageParser, 1) } />
</div>
<h6 className="align-self-center">{ GetCatalogPageText(pageParser, 1) }</h6>
<div className="row h-100">
<div className="d-flex flex-column col-12 h-100">
<div className="d-flex flex-column bg-muted rounded text-black gap-2 p-2 h-100">
<div className="d-flex flex-row align-items-center gap-2">
{ imageUrl && <img alt="" src={ GetCatalogPageImage(pageParser, 1) } /> }
<div className="fs-5" dangerouslySetInnerHTML={ { __html: GetCatalogPageText(pageParser, 1) } } />
</div>
<div className="d-flex flex-column align-items-center flex-grow-1 overflow-auto">
<div dangerouslySetInnerHTML={ { __html: GetCatalogPageText(pageParser, 2) } } />
{GetCatalogPageText(pageParser, 3) && <div className="mt-auto bg-secondary text-white rounded p-1 text-center" dangerouslySetInnerHTML={{ __html: GetCatalogPageText(pageParser, 3) }} />}
</div>
<div className="d-flex flex-row align-items-center">
<div className="fw-bold" dangerouslySetInnerHTML={ { __html: GetCatalogPageText(pageParser, 3) } } />
</div>
</div>
</div>
</div>
);

View File

@ -1,6 +0,0 @@
.nitro-catalog-layout-single-bundle {
.single-bundle-items-container {
}
}

View File

@ -1,6 +1,7 @@
import { FC } from 'react';
import { GetCatalogPageImage, GetCatalogPageText } from '../../../../common/CatalogUtilities';
import { NitroCardGridView } from '../../../../../../layout';
import { useCatalogContext } from '../../../../context/CatalogContext';
import { CatalogPageDetailsView } from '../../../page-details/CatalogPageDetailsView';
import { CatalogProductView } from '../../product/CatalogProductView';
import { CatalogPurchaseView } from '../../purchase/CatalogPurchaseView';
import { CatalogLayoutSingleBundleViewProps } from './CatalogLayoutSingleBundleView.types';
@ -12,20 +13,17 @@ export const CatalogLayoutSingleBundleView: FC<CatalogLayoutSingleBundleViewProp
const { activeOffer = null } = catalogState;
return (
<div className="row h-100 nitro-catalog-layout-single-bundle">
<div className="col-7 h-100">
<div className="row row-cols-5 align-content-start g-0 mb-n1 w-100 catalog-offers-container single-bundle-items-container h-100 overflow-auto">
<div className="row h-100">
<div className="d-flex flex-column col-7 h-100">
<NitroCardGridView columns={ 5 }>
{ activeOffer && activeOffer.products && (activeOffer.products.length > 0) && activeOffer.products.map((product, index) =>
{
return <CatalogProductView key={ index } isActive={ false } product={ product } />
}) }
</NitroCardGridView>
</div>
</div>
<div className="position-relative d-flex flex-column col-5 justify-content-center align-items-center h-100 overflow-auto">
<div className="d-block mb-2">
<img alt="" src={ GetCatalogPageImage(pageParser, 1) } />
</div>
<div className="fs-6 text-center text-black lh-sm overflow-hidden">{ GetCatalogPageText(pageParser, 0) }</div>
<div className="position-relative d-flex flex-column col-5">
<CatalogPageDetailsView pageParser={ pageParser } />
{ activeOffer && <CatalogPurchaseView offer={ activeOffer } pageId={ pageParser.pageId } /> }
</div>
</div>

View File

@ -1,3 +0,0 @@
.nitro-catalog-layout-spaces {
}

View File

@ -1,12 +1,10 @@
import { CatalogPageMessageOfferData, IFurnitureData } from '@nitrots/nitro-renderer';
import { FC, useEffect, useState } from 'react';
import { GetSessionDataManager, LocalizeText } from '../../../../../../api';
import { GetCatalogPageImage, GetCatalogPageText, GetOfferName } from '../../../../common/CatalogUtilities';
import { ProductTypeEnum } from '../../../../common/ProductTypeEnum';
import { useCatalogContext } from '../../../../context/CatalogContext';
import { CatalogRoomPreviewerView } from '../../../catalog-room-previewer/CatalogRoomPreviewerView';
import { CatalogPageOffersView } from '../../offers/CatalogPageOffersView';
import { CatalogPurchaseView } from '../../purchase/CatalogPurchaseView';
import { CatalogProductPreviewView } from '../../product-preview/CatalogProductPreviewView';
import { CatalogLayoutSpacesViewProps } from './CatalogLayoutSpacesView.types';
export const CatalogLayoutSpacesView: FC<CatalogLayoutSpacesViewProps> = props =>
@ -68,9 +66,9 @@ export const CatalogLayoutSpacesView: FC<CatalogLayoutSpacesViewProps> = props =
const product = ((activeOffer && activeOffer.products[0]) || null);
return (
<div className="row h-100 nitro-catalog-layout-spaces">
<div className="d-flex col-7 flex-column h-100 overflow-hidden">
<div className="btn-group mx-auto mb-1 w-100">
<div className="row h-100">
<div className="d-flex flex-column col-7 gap-2 h-100">
<div className="btn-group w-100">
{ groupNames.map((name, index) =>
{
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>
@ -78,19 +76,9 @@ export const CatalogLayoutSpacesView: FC<CatalogLayoutSpacesViewProps> = props =
</div>
<CatalogPageOffersView offers={ groups[activeGroupIndex] } />
</div>
{ !product &&
<div className="position-relative d-flex flex-column col-5 justify-content-center align-items-center">
<div className="d-block mb-2">
<img alt="" src={ GetCatalogPageImage(pageParser, 1) } />
<div className="position-relative d-flex flex-column col-5">
<CatalogProductPreviewView pageParser={ pageParser } activeOffer={ activeOffer } roomPreviewer={ roomPreviewer } />
</div>
<div className="fs-6 text-center text-black lh-sm overflow-hidden">{ GetCatalogPageText(pageParser, 0) }</div>
</div> }
{ product &&
<div className="position-relative d-flex flex-column col">
<CatalogRoomPreviewerView roomPreviewer={ roomPreviewer } height={ 140 } />
<div className="fs-6 text-black mt-1 overflow-hidden">{ GetOfferName(activeOffer) }</div>
<CatalogPurchaseView offer={ activeOffer } pageId={ pageParser.pageId } />
</div> }
</div>
);
}

View File

@ -1,8 +0,0 @@
.nitro-catalog-layout-trophies {
textarea {
height: 119px;
resize: none;
}
}

View File

@ -1,9 +1,7 @@
import { FC, useState } from 'react';
import { GetOfferName } from '../../../../common/CatalogUtilities';
import { useCatalogContext } from '../../../../context/CatalogContext';
import { CatalogRoomPreviewerView } from '../../../catalog-room-previewer/CatalogRoomPreviewerView';
import { CatalogPageOffersView } from '../../offers/CatalogPageOffersView';
import { CatalogPurchaseView } from '../../purchase/CatalogPurchaseView';
import { CatalogProductPreviewView } from '../../product-preview/CatalogProductPreviewView';
import { CatalogLayoutTrophiesViewProps } from './CatalogLayoutTrophiesView.types';
export const CatalogLayoutTrophiesView: FC<CatalogLayoutTrophiesViewProps> = props =>
@ -16,19 +14,14 @@ export const CatalogLayoutTrophiesView: FC<CatalogLayoutTrophiesViewProps> = pro
const product = ((activeOffer && activeOffer.products[0]) || null);
return (
<div className="row h-100 nitro-catalog-layout-trophies">
<div className="d-flex flex-column col-7 h-100">
<div className="row h-100">
<div className="d-flex flex-column col-7 gap-2 h-100">
<CatalogPageOffersView offers={ pageParser.offers } />
<div className="d-flex mt-2">
<textarea className="form-control w-100" defaultValue={ trophyText || '' } onChange={ event => setTrophyText(event.target.value) }></textarea>
<textarea className="flex-grow-1 form-control w-100" defaultValue={ trophyText || '' } onChange={ event => setTrophyText(event.target.value) } />
</div>
<div className="position-relative d-flex flex-column col-5">
<CatalogProductPreviewView pageParser={ pageParser } activeOffer={ activeOffer } roomPreviewer={ roomPreviewer } extra={ trophyText } />
</div>
{ product &&
<div className="position-relative d-flex flex-column col">
<CatalogRoomPreviewerView roomPreviewer={ roomPreviewer } height={ 140 } />
<div className="fs-6 text-black mt-1 overflow-hidden">{ GetOfferName(activeOffer) }</div>
<CatalogPurchaseView offer={ activeOffer } pageId={ pageParser.pageId } extra={ trophyText } />
</div> }
</div>
);
}

View File

@ -26,7 +26,6 @@ export const CatalogPageOfferView: FC<CatalogPageOfferViewProps> = props =>
});
return;
case MouseEventType.MOUSE_DOWN:
console.log('ye')
setMouseDown(true);
return;
case MouseEventType.MOUSE_UP:
@ -42,5 +41,5 @@ export const CatalogPageOfferView: FC<CatalogPageOfferViewProps> = props =>
if(!product) return null;
return <CatalogProductView isActive={ isActive } product={ product } onMouseEvent={ onMouseEvent } />
return <CatalogProductView isActive={ isActive } product={ product } onClick={ onMouseEvent } onMouseDown={ onMouseEvent } onMouseUp={ onMouseEvent } onMouseOut={ onMouseEvent } />
}

View File

@ -1,32 +1,21 @@
import { FC, useEffect } from 'react';
import { FC } from 'react';
import { NitroCardGridView } from '../../../../../layout';
import { useCatalogContext } from '../../../context/CatalogContext';
import { CatalogPageOfferView } from '../offer/CatalogPageOfferView';
import { CatalogPageOffersViewProps } from './CatalogPageOffersView.types';
export const CatalogPageOffersView: FC<CatalogPageOffersViewProps> = props =>
{
const { offers = [] } = props;
const { offers = [], ...rest } = props;
const { catalogState = null, dispatchCatalogState = null } = useCatalogContext();
const { activeOffer = null } = catalogState;
useEffect(() =>
{
if(!offers || !offers.length) return;
// dispatchCatalogState({
// type: CatalogActions.SET_CATALOG_ACTIVE_OFFER,
// payload: {
// activeOffer: offers[0]
// }
// });
}, [ offers, dispatchCatalogState ]);
return (
<div className="row row-cols-5 align-content-start g-0 mb-n1 w-100 catalog-offers-container h-100 overflow-auto">
<NitroCardGridView columns={ 5 } { ...rest }>
{ offers && (offers.length > 0) && offers.map((offer, index) =>
{
return <CatalogPageOfferView key={ index } isActive={ (activeOffer === offer) } offer={ offer } />
})}
</div>
</NitroCardGridView>
);
}

View File

@ -1,6 +1,7 @@
import { CatalogPageMessageOfferData } from '@nitrots/nitro-renderer';
import { DetailsHTMLAttributes } from 'react';
export interface CatalogPageOffersViewProps
export interface CatalogPageOffersViewProps extends DetailsHTMLAttributes<HTMLDivElement>
{
offers: CatalogPageMessageOfferData[];
}

View File

@ -0,0 +1,27 @@
import { FC } from 'react';
import { LimitedEditionCompletePlateView } from '../../../../shared/limited-edition/complete-plate/LimitedEditionCompletePlateView';
import { GetOfferName } from '../../../common/CatalogUtilities';
import { CatalogRoomPreviewerView } from '../../catalog-room-previewer/CatalogRoomPreviewerView';
import { CatalogPageDetailsView } from '../../page-details/CatalogPageDetailsView';
import { CatalogPurchaseView } from '../purchase/CatalogPurchaseView';
import { CatalogProductPreviewViewProps } from './CatalogProductPreviewView.types';
export const CatalogProductPreviewView: FC<CatalogProductPreviewViewProps> = props =>
{
const { pageParser = null, activeOffer = null, roomPreviewer = null, extra = '', disabled = false, children = null } = props;
const product = ((activeOffer && activeOffer.products[0]) || null);
if(!product) return <CatalogPageDetailsView pageParser={ pageParser } />;
return (
<>
<CatalogRoomPreviewerView roomPreviewer={ roomPreviewer } height={ 140 } />
{ product.uniqueLimitedItem &&
<LimitedEditionCompletePlateView uniqueLimitedItemsLeft={ product.uniqueLimitedItemsLeft } uniqueLimitedSeriesSize={ product.uniqueLimitedSeriesSize } /> }
<div className="fs-6 text-black mt-1 overflow-hidden">{ GetOfferName(activeOffer) }</div>
{ children }
<CatalogPurchaseView offer={ activeOffer } pageId={ pageParser.pageId } extra={ extra } disabled={ disabled } />
</>
);
}

View File

@ -0,0 +1,10 @@
import { CatalogPageMessageOfferData, CatalogPageMessageParser, RoomPreviewer } from '@nitrots/nitro-renderer';
export interface CatalogProductPreviewViewProps
{
pageParser: CatalogPageMessageParser;
activeOffer: CatalogPageMessageOfferData;
roomPreviewer: RoomPreviewer;
extra?: string;
disabled?: boolean;
}

View File

@ -1,79 +1,36 @@
import { FurnitureType } from '@nitrots/nitro-renderer';
import { FC, useMemo } from 'react';
import { GetConfiguration, GetRoomEngine, GetSessionDataManager } from '../../../../../api';
import { NitroCardGridItemView } from '../../../../../layout';
import { AvatarImageView } from '../../../../shared/avatar-image/AvatarImageView';
import { LimitedEditionStyledNumberView } from '../../../../shared/limited-edition/styled-number/LimitedEditionStyledNumberView';
import { GetProductIconUrl } from '../../../common/GetProuductIconUrl';
import { ProductTypeEnum } from '../../../common/ProductTypeEnum';
import { CatalogProductViewProps } from './CatalogProductView.types';
export const CatalogProductView: FC<CatalogProductViewProps> = props =>
{
const { isActive = false, product = null, onMouseEvent = null } = props;
const { isActive = false, product = null, ...rest } = props;
const iconUrl = useMemo(() =>
{
if(!product) return null;
const productType = product.productType.toUpperCase();
return GetProductIconUrl(product.furniClassId, product.productType, product.extraParam);
}, [ product ]);
switch(productType)
const getUniqueNumber = useMemo(() =>
{
case FurnitureType.BADGE:
return GetSessionDataManager().getBadgeUrl(product.extraParam);
case FurnitureType.ROBOT:
return null;
case FurnitureType.FLOOR:
return GetRoomEngine().getFurnitureFloorIconUrl(product.furniClassId);
case FurnitureType.WALL: {
const furniData = GetSessionDataManager().getWallItemData(product.furniClassId);
if(!product.uniqueLimitedItem) return 0;
let iconName = '';
if(!product.uniqueLimitedItemsLeft) return -1;
if(furniData)
{
switch(furniData.className)
{
case 'floor':
iconName = ['th', furniData.className, product.extraParam].join('_');
break;
case 'wallpaper':
iconName = ['th', 'wall', product.extraParam].join('_');
break;
case 'landscape':
iconName = ['th', furniData.className, product.extraParam.replace('.', '_'), '001'].join('_');
break;
}
if(iconName !== '')
{
const assetUrl = GetConfiguration<string>('catalog.asset.url');
return `${ assetUrl }/${ iconName }.png`;
}
}
return GetRoomEngine().getFurnitureWallIconUrl(product.furniClassId, product.extraParam);
}
}
return null;
return product.uniqueLimitedSeriesSize;
}, [ product ]);
if(!product) return null;
const imageUrl = (iconUrl && iconUrl.length) ? `url(${ iconUrl })` : null;
return (
<div className="col pe-1 pb-1 catalog-offer-item-container">
<div className={ 'position-relative border border-2 rounded catalog-offer-item cursor-pointer ' + (isActive ? 'active ' : '') + (product.uniqueLimitedItem ? 'unique-item ' : '') + ((product.uniqueLimitedItem && !product.uniqueLimitedItemsLeft) ? 'sold-out ' : '') } style={ { backgroundImage: imageUrl }} onClick={ onMouseEvent } onMouseDown={ onMouseEvent } onMouseUp={ onMouseEvent } onMouseOut={ onMouseEvent }>
{ !imageUrl && (product.productType === ProductTypeEnum.ROBOT) &&
<NitroCardGridItemView itemImage={ iconUrl } itemActive={ isActive } itemCount={ product.productCount } itemUniqueNumber={ getUniqueNumber } { ...rest }>
{ (product.productType === ProductTypeEnum.ROBOT) &&
<AvatarImageView figure={ product.extraParam } direction={ 3 } headOnly={ true } /> }
{ (product.productCount > 1) && <span className="position-absolute badge border bg-danger px-1 rounded-circle">{ product.productCount }</span> }
{ product.uniqueLimitedItem &&
<div className="position-absolute unique-item-counter">
<LimitedEditionStyledNumberView value={ product.uniqueLimitedSeriesSize } />
</div> }
</div>
</div>
</NitroCardGridItemView>
);
}

View File

@ -1,9 +1,8 @@
import { CatalogPageMessageProductData } from '@nitrots/nitro-renderer';
import { MouseEventHandler } from 'react';
import { DetailsHTMLAttributes } from 'react';
export interface CatalogProductViewProps
export interface CatalogProductViewProps extends DetailsHTMLAttributes<HTMLDivElement>
{
isActive: boolean;
product: CatalogPageMessageProductData;
onMouseEvent?: MouseEventHandler<Element>;
}

View File

@ -17,7 +17,7 @@ export const CatalogLayoutSearchResultView: FC<CatalogLayoutSearchResultViewProp
return (
<div className="row h-100">
<div className="col-7 h-100">
<div className="d-flex flex-column col-7 h-100">
<CatalogSearchResultOffersView offers={ furnitureDatas } />
</div>
{ product &&

View File

@ -1,7 +1,10 @@
import { FurnitureType, GetProductOfferComposer, MouseEventType } from '@nitrots/nitro-renderer';
import { FC, MouseEvent, useCallback } from 'react';
import { GetConfiguration, GetRoomEngine, GetSessionDataManager } from '../../../../../../api';
import { GetProductOfferComposer, MouseEventType } from '@nitrots/nitro-renderer';
import { FC, MouseEvent, useCallback, useMemo } from 'react';
import { SendMessageHook } from '../../../../../../hooks/messages/message-event';
import { NitroCardGridItemView } from '../../../../../../layout';
import { AvatarImageView } from '../../../../../shared/avatar-image/AvatarImageView';
import { GetProductIconUrl } from '../../../../common/GetProuductIconUrl';
import { ProductTypeEnum } from '../../../../common/ProductTypeEnum';
import { CatalogSearchResultOfferViewProps } from './CatalogSearchResultOfferView.types';
export const CatalogSearchResultOfferView: FC<CatalogSearchResultOfferViewProps> = props =>
@ -15,62 +18,22 @@ export const CatalogSearchResultOfferView: FC<CatalogSearchResultOfferViewProps>
case MouseEventType.MOUSE_DOWN:
SendMessageHook(new GetProductOfferComposer(offer.purchaseOfferId));
return;
case MouseEventType.MOUSE_UP:
return;
case MouseEventType.ROLL_OUT:
return;
}
}, [ offer ]);
function getIconUrl(): string
const iconUrl = useMemo(() =>
{
const productType = offer.type.toUpperCase();
if(!offer) return null;
switch(productType)
{
case FurnitureType.BADGE:
return GetSessionDataManager().getBadgeUrl(offer.customParams);
case FurnitureType.FLOOR:
return GetRoomEngine().getFurnitureFloorIconUrl(offer.id);
case FurnitureType.WALL:
const furniData = GetSessionDataManager().getWallItemData(offer.id);
return GetProductIconUrl(offer.id, offer.type, offer.customParams);
}, [ offer ]);
let iconName = '';
if(furniData)
{
switch(furniData.className)
{
case 'floor':
iconName = ['th', furniData.className, offer.customParams ].join('_');
break;
case 'wallpaper':
iconName = ['th', 'wall', offer.customParams ].join('_');
break;
case 'landscape':
iconName = ['th', furniData.className, ((offer.customParams && offer.customParams.replace('.', '_')) || null), '001'].join('_');
break;
}
if(iconName !== '')
{
const assetUrl = GetConfiguration<string>('catalog.asset.url');
return `${ assetUrl }/${ iconName }.png`;
}
}
return GetRoomEngine().getFurnitureWallIconUrl(offer.id, offer.customParams);
}
return '';
}
const imageUrl = `url(${ getIconUrl() })`;
if(!offer) return null;
return (
<div className="col pe-1 pb-1 catalog-offer-item-container">
<div className={ 'position-relative border border-2 rounded catalog-offer-item cursor-pointer ' + (isActive ? 'active ' : '') } style={ { backgroundImage: imageUrl }} onMouseDown={ onMouseEvent } onMouseUp={ onMouseEvent } onMouseOut={ onMouseEvent } />
</div>
<NitroCardGridItemView itemImage={ iconUrl } itemActive={ isActive } onMouseDown={ onMouseEvent }>
{ (offer.type === ProductTypeEnum.ROBOT) &&
<AvatarImageView figure={ offer.customParams } direction={ 3 } headOnly={ true } /> }
</NitroCardGridItemView>
);
}

View File

@ -1,14 +1,15 @@
import { GetProductOfferComposer } from '@nitrots/nitro-renderer';
import { FC, useEffect } from 'react';
import { SendMessageHook } from '../../../../../../hooks/messages/message-event';
import { NitroCardGridView } from '../../../../../../layout';
import { useCatalogContext } from '../../../../context/CatalogContext';
import { CatalogSearchResultOfferView } from '../offer/CatalogSearchResultOfferView';
import { CatalogSearchResultOffersViewProps } from './CatalogSearchResultOffersView.types';
export const CatalogSearchResultOffersView: FC<CatalogSearchResultOffersViewProps> = props =>
{
const { offers = [] } = props;
const { catalogState } = useCatalogContext();
const { offers = [], ...rest } = props;
const { catalogState = null } = useCatalogContext();
const { activeOffer = null } = catalogState;
useEffect(() =>
@ -19,13 +20,13 @@ export const CatalogSearchResultOffersView: FC<CatalogSearchResultOffersViewProp
}, [ offers ]);
return (
<div className="row row-cols-5 align-content-start g-0 mb-n1 w-100 catalog-offers-container h-100">
<NitroCardGridView columns={ 5 } { ...rest }>
{ offers && (offers.length > 0) && offers.map((offer, index) =>
{
const isActive = (activeOffer && (activeOffer.products[0].furniClassId === offer.id));
return <CatalogSearchResultOfferView key={ index } isActive={ isActive } offer={ offer } />
})}
</div>
</NitroCardGridView>
);
}

View File

@ -117,7 +117,7 @@ export const InventoryFurnitureView: FC<InventoryFurnitureViewProps> = props =>
return (
<div className="row h-100">
<div className="col-7 d-flex flex-column h-100">
<div className="d-flex flex-column col-7 h-100">
<InventoryFurnitureSearchView groupItems={ groupItems } setGroupItems={ setFilteredGroupItems } />
<InventoryFurnitureResultsView groupItems={ filteredGroupItems } />
</div>