mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-30 08:50:51 +01:00
changes
This commit is contained in:
parent
1546a0446b
commit
70c1690e39
@ -0,0 +1,6 @@
|
|||||||
|
export interface IMarketplaceSearchOptions {
|
||||||
|
query: string;
|
||||||
|
type: number;
|
||||||
|
minPrice: number;
|
||||||
|
maxPrice: number;
|
||||||
|
}
|
@ -2,8 +2,8 @@ import { IObjectData } from '@nitrots/nitro-renderer';
|
|||||||
|
|
||||||
export class MarketplaceOfferData
|
export class MarketplaceOfferData
|
||||||
{
|
{
|
||||||
public static TYPE_LANDSCAPE: number = 1;
|
public static readonly TYPE_FLOOR: number = 1;
|
||||||
public static TYPE_FLOOR: number = 2;
|
public static readonly TYPE_WALL: number = 2;
|
||||||
|
|
||||||
private _offerId: number;
|
private _offerId: number;
|
||||||
private _furniId: number;
|
private _furniId: number;
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
export class MarketplaceSearchType
|
||||||
|
{
|
||||||
|
public static readonly BY_ACTIVITY = 1;
|
||||||
|
public static readonly BY_VALUE = 2;
|
||||||
|
public static readonly ADVANCED = 3;
|
||||||
|
}
|
@ -1,10 +1,12 @@
|
|||||||
|
import { CancelMarketplaceOfferMessageComposer } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback } from 'react';
|
import { FC, useCallback } from 'react';
|
||||||
import { GetRoomEngine, LocalizeText } from '../../../../../../../api';
|
import { GetRoomEngine, LocalizeText } from '../../../../../../../api';
|
||||||
|
import { SendMessageHook } from '../../../../../../../hooks';
|
||||||
import { NitroCardGridItemView } from '../../../../../../../layout';
|
import { NitroCardGridItemView } from '../../../../../../../layout';
|
||||||
import { MarketplaceOfferData } from '../common/MarketplaceOfferData';
|
import { MarketplaceOfferData } from '../common/MarketplaceOfferData';
|
||||||
|
|
||||||
export const OWN_OFFER = 1;
|
export const OWN_OFFER = 1;
|
||||||
export const OTHER_OFFER = 2;
|
export const PUBLIC_OFFER = 2;
|
||||||
|
|
||||||
export interface MarketplaceItemViewProps
|
export interface MarketplaceItemViewProps
|
||||||
{
|
{
|
||||||
@ -14,7 +16,7 @@ export interface MarketplaceItemViewProps
|
|||||||
|
|
||||||
export const MarketplaceItemView: FC<MarketplaceItemViewProps> = props =>
|
export const MarketplaceItemView: FC<MarketplaceItemViewProps> = props =>
|
||||||
{
|
{
|
||||||
const { offerData = null, type = OTHER_OFFER } = props;
|
const { offerData = null, type = PUBLIC_OFFER } = props;
|
||||||
|
|
||||||
const getImageUrlForOffer = useCallback( () =>
|
const getImageUrlForOffer = useCallback( () =>
|
||||||
{
|
{
|
||||||
@ -22,9 +24,9 @@ export const MarketplaceItemView: FC<MarketplaceItemViewProps> = props =>
|
|||||||
|
|
||||||
switch(offerData.furniType)
|
switch(offerData.furniType)
|
||||||
{
|
{
|
||||||
case 1:
|
case MarketplaceOfferData.TYPE_FLOOR:
|
||||||
return GetRoomEngine().getFurnitureFloorIconUrl(offerData.furniId);
|
return GetRoomEngine().getFurnitureFloorIconUrl(offerData.furniId);
|
||||||
case 2:
|
case MarketplaceOfferData.TYPE_WALL:
|
||||||
return GetRoomEngine().getFurnitureWallIconUrl(offerData.furniId, offerData.extraData);
|
return GetRoomEngine().getFurnitureWallIconUrl(offerData.furniId, offerData.extraData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,6 +55,8 @@ export const MarketplaceItemView: FC<MarketplaceItemViewProps> = props =>
|
|||||||
{
|
{
|
||||||
if(!offerData) return '';
|
if(!offerData) return '';
|
||||||
|
|
||||||
|
if(offerData.timeLeftMinutes <= 0) return LocalizeText('catalog.marketplace.offer.expired');
|
||||||
|
|
||||||
const time = Math.max(1, offerData.timeLeftMinutes);
|
const time = Math.max(1, offerData.timeLeftMinutes);
|
||||||
const hours = Math.floor(time / 60);
|
const hours = Math.floor(time / 60);
|
||||||
const minutes = time - (hours * 60);
|
const minutes = time - (hours * 60);
|
||||||
@ -66,10 +70,15 @@ export const MarketplaceItemView: FC<MarketplaceItemViewProps> = props =>
|
|||||||
return LocalizeText('catalog.marketplace.offer.time_left', ['time'], [text] );
|
return LocalizeText('catalog.marketplace.offer.time_left', ['time'], [text] );
|
||||||
}, [offerData]);
|
}, [offerData]);
|
||||||
|
|
||||||
|
const takeItemBack = useCallback(() =>
|
||||||
|
{
|
||||||
|
SendMessageHook(new CancelMarketplaceOfferMessageComposer(offerData.offerId));
|
||||||
|
}, [offerData.offerId])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NitroCardGridItemView className='w-100 marketplace-item'>
|
<NitroCardGridItemView className='w-100 marketplace-item align-items-center'>
|
||||||
<img src={ getImageUrlForOffer() } className='mx-3'/>
|
<img src={ getImageUrlForOffer() } className='mx-3' alt='' />
|
||||||
<div className='h-100 flex-grow-1 lh-1'>
|
<div className='h-100 flex-grow-1 justify-content-center '>
|
||||||
<div className='fw-bold'>{getMarketplaceOfferTitle()}</div>
|
<div className='fw-bold'>{getMarketplaceOfferTitle()}</div>
|
||||||
<div className='fst-italic fs-6'>{getMarketplaceOfferDescription()}</div>
|
<div className='fst-italic fs-6'>{getMarketplaceOfferDescription()}</div>
|
||||||
|
|
||||||
@ -78,14 +87,18 @@ export const MarketplaceItemView: FC<MarketplaceItemViewProps> = props =>
|
|||||||
<div>{ offerTime() }</div>
|
<div>{ offerTime() }</div>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
{ type === OTHER_OFFER && <>
|
{ type === PUBLIC_OFFER && <>
|
||||||
<div>{ LocalizeText('catalog.marketplace.offer.price_public_item', ['price', 'average'], [offerData.price.toString(), offerData.averagePrice.toString() ]) }</div>
|
<div>{ LocalizeText('catalog.marketplace.offer.price_public_item', ['price', 'average'], [offerData.price.toString(), offerData.averagePrice.toString() ]) }</div>
|
||||||
<div>{ LocalizeText('catalog.marketplace.offer_count', ['count'], [offerData.offerCount.toString()]) }</div>
|
<div>{ LocalizeText('catalog.marketplace.offer_count', ['count'], [offerData.offerCount.toString()]) }</div>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div className='btn-group-vertical mx-1'>
|
<div className='btn-group-vertical mx-1 gap-2'>
|
||||||
{ type === OWN_OFFER && <button className='btn btn-secondary btn-sm'>{LocalizeText('catalog.marketplace.offer.pick')}</button>}
|
{ type === OWN_OFFER && <button className='btn btn-secondary btn-sm' onClick={ takeItemBack }>{ LocalizeText('catalog.marketplace.offer.pick') }</button>}
|
||||||
|
{ type === PUBLIC_OFFER && <>
|
||||||
|
<button className='btn btn-secondary btn-sm'>{ LocalizeText('buy') }</button>
|
||||||
|
<button className='btn btn-secondary btn-sm' disabled={true}>{ LocalizeText('catalog.marketplace.view_more') }</button>
|
||||||
|
</>}
|
||||||
</div>
|
</div>
|
||||||
</NitroCardGridItemView>)
|
</NitroCardGridItemView>)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { GetMarketplaceOwnOffersMessageComposer, MarketplaceOwnOffersEvent } from '@nitrots/nitro-renderer';
|
import { GetMarketplaceOwnOffersMessageComposer, MarketplaceOwnOffersEvent, RedeemMarketplaceOfferCreditsMessageComposer } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback, useState } from 'react';
|
import { FC, useCallback, useState } from 'react';
|
||||||
import { LocalizeText } from '../../../../../../../api';
|
import { LocalizeText } from '../../../../../../../api';
|
||||||
import { BatchUpdates, CreateMessageHook, SendMessageHook, UseMountEffect } from '../../../../../../../hooks';
|
import { BatchUpdates, CreateMessageHook, SendMessageHook, UseMountEffect } from '../../../../../../../hooks';
|
||||||
@ -6,6 +6,7 @@ import { NitroCardGridView } from '../../../../../../../layout';
|
|||||||
import { NitroLayoutBase } from '../../../../../../../layout/base';
|
import { NitroLayoutBase } from '../../../../../../../layout/base';
|
||||||
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 { MarketplaceItemView, OWN_OFFER } from '../marketplace-item/MarketplaceItemView';
|
import { MarketplaceItemView, OWN_OFFER } from '../marketplace-item/MarketplaceItemView';
|
||||||
|
|
||||||
export interface CatalogLayoutMarketplaceOwnItemsViewProps extends CatalogLayoutProps
|
export interface CatalogLayoutMarketplaceOwnItemsViewProps extends CatalogLayoutProps
|
||||||
@ -46,13 +47,39 @@ export const CatalogLayoutMarketplaceOwnItemsView: FC<CatalogLayoutMarketplaceOw
|
|||||||
|
|
||||||
CreateMessageHook(MarketplaceOwnOffersEvent, onMarketPlaceOwnOffersEvent);
|
CreateMessageHook(MarketplaceOwnOffersEvent, onMarketPlaceOwnOffersEvent);
|
||||||
|
|
||||||
|
const redeemSoldOffers = useCallback(() =>
|
||||||
|
{
|
||||||
|
setOffers(prev =>
|
||||||
|
{
|
||||||
|
const newVal = new Map(prev);
|
||||||
|
|
||||||
|
const idsToDelete = [];
|
||||||
|
|
||||||
|
for(const offer of newVal.values())
|
||||||
|
{
|
||||||
|
if(offer.status === MarketPlaceOfferState.SOLD)
|
||||||
|
{
|
||||||
|
idsToDelete.push(offer.offerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const offerId of idsToDelete)
|
||||||
|
{
|
||||||
|
newVal.delete(offerId);
|
||||||
|
}
|
||||||
|
return newVal;
|
||||||
|
})
|
||||||
|
|
||||||
|
SendMessageHook(new RedeemMarketplaceOfferCreditsMessageComposer());
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{ (creditsWaiting <= 0) && <NitroLayoutBase className='text-black'>{LocalizeText('catalog.marketplace.redeem.no_sold_items')}</NitroLayoutBase>}
|
{ (creditsWaiting <= 0) && <NitroLayoutBase className='text-black'>{LocalizeText('catalog.marketplace.redeem.no_sold_items')}</NitroLayoutBase>}
|
||||||
|
|
||||||
{ (creditsWaiting > 0) && <NitroLayoutBase className='text-black'>{LocalizeText('catalog.marketplace.redeem.get_credits', ['count', 'credits'], ['0', creditsWaiting.toString()])}</NitroLayoutBase>}
|
{ (creditsWaiting > 0) && <NitroLayoutBase className='text-black'>{LocalizeText('catalog.marketplace.redeem.get_credits', ['count', 'credits'], ['0', creditsWaiting.toString()])}</NitroLayoutBase>}
|
||||||
|
|
||||||
<button className='btn btn-primary btn-sm mx-auto' disabled={creditsWaiting <= 0}>{LocalizeText('catalog.marketplace.offer.redeem')}</button>
|
<button className='btn btn-primary btn-sm mx-auto' disabled={creditsWaiting <= 0} onClick={redeemSoldOffers}>{LocalizeText('catalog.marketplace.offer.redeem')}</button>
|
||||||
|
|
||||||
<div className='text-black'>{LocalizeText('catalog.marketplace.items_found', ['count'], [offers.size.toString()])}</div>
|
<div className='text-black'>{LocalizeText('catalog.marketplace.items_found', ['count'], [offers.size.toString()])}</div>
|
||||||
<NitroCardGridView columns={1} className='text-black'>
|
<NitroCardGridView columns={1} className='text-black'>
|
||||||
|
@ -1,7 +1,18 @@
|
|||||||
import { FC } from 'react';
|
import { GetMarketplaceOffersMessageComposer, MarketPlaceOffersEvent } from '@nitrots/nitro-renderer';
|
||||||
import { UseMountEffect } from '../../../../../../../hooks';
|
import { FC, useCallback, useMemo, useState } from 'react';
|
||||||
|
import { LocalizeText } from '../../../../../../../api';
|
||||||
|
import { BatchUpdates, CreateMessageHook, SendMessageHook } from '../../../../../../../hooks';
|
||||||
|
import { NitroCardGridView } from '../../../../../../../layout';
|
||||||
import { CatalogLayoutProps } from '../../CatalogLayout.types';
|
import { CatalogLayoutProps } from '../../CatalogLayout.types';
|
||||||
|
import { IMarketplaceSearchOptions } from '../common/IMarketplaceSearchOptions';
|
||||||
|
import { MarketplaceOfferData } from '../common/MarketplaceOfferData';
|
||||||
|
import { MarketplaceSearchType } from '../common/MarketplaceSearchType';
|
||||||
|
import { MarketplaceItemView, PUBLIC_OFFER } from '../marketplace-item/MarketplaceItemView';
|
||||||
|
import { SearchFormView } from './SearchFormView';
|
||||||
|
|
||||||
|
const SORT_TYPES_VALUE = [1, 2];
|
||||||
|
const SORT_TYPES_ACTIVITY = [3, 4, 5, 6];
|
||||||
|
const SORT_TYPES_ADVANCED = [1, 2, 3, 4, 5, 6];
|
||||||
export interface CatalogLayoutMarketplacePublicItemsViewProps extends CatalogLayoutProps
|
export interface CatalogLayoutMarketplacePublicItemsViewProps extends CatalogLayoutProps
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -9,10 +20,73 @@ export interface CatalogLayoutMarketplacePublicItemsViewProps extends CatalogLay
|
|||||||
|
|
||||||
export const CatalogLayoutMarketplacePublicItemsView: FC<CatalogLayoutMarketplacePublicItemsViewProps> = props =>
|
export const CatalogLayoutMarketplacePublicItemsView: FC<CatalogLayoutMarketplacePublicItemsViewProps> = props =>
|
||||||
{
|
{
|
||||||
UseMountEffect(() =>
|
const [ searchType, setSearchType ] = useState(MarketplaceSearchType.BY_ACTIVITY);
|
||||||
|
const [ totalItemsFound, setTotalItemsFound ] = useState(0);
|
||||||
|
const [ offers, setOffers ] = useState(new Map<number, MarketplaceOfferData>());
|
||||||
|
|
||||||
|
const requestOffers = useCallback((options: IMarketplaceSearchOptions) =>
|
||||||
{
|
{
|
||||||
//request stuff
|
SendMessageHook(new GetMarketplaceOffersMessageComposer(options.minPrice, options.maxPrice, options.query, options.type))
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const getSortTypes = useMemo( () =>
|
||||||
|
{
|
||||||
|
switch(searchType)
|
||||||
|
{
|
||||||
|
case MarketplaceSearchType.BY_ACTIVITY:
|
||||||
|
return SORT_TYPES_ACTIVITY;
|
||||||
|
case MarketplaceSearchType.BY_VALUE:
|
||||||
|
return SORT_TYPES_VALUE;
|
||||||
|
case MarketplaceSearchType.ADVANCED:
|
||||||
|
return SORT_TYPES_ADVANCED;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}, [searchType]);
|
||||||
|
|
||||||
|
const onMarketPlaceOffersEvent = useCallback( (event: MarketPlaceOffersEvent) =>
|
||||||
|
{
|
||||||
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
if(!parser) return;
|
||||||
|
|
||||||
|
const latestOffers = new Map<number, MarketplaceOfferData>();
|
||||||
|
parser.offers.forEach(entry =>
|
||||||
|
{
|
||||||
|
const offerEntry = new MarketplaceOfferData(entry.offerId, entry.furniId, entry.furniType, entry.extraData, entry.stuffData, entry.price, entry.status, entry.averagePrice, entry.offerCount);
|
||||||
|
offerEntry.timeLeftMinutes = entry.timeLeftMinutes;
|
||||||
|
latestOffers.set(entry.offerId, offerEntry);
|
||||||
});
|
});
|
||||||
|
|
||||||
return null;
|
BatchUpdates(() =>
|
||||||
|
{
|
||||||
|
setTotalItemsFound(parser.totalItemsFound);
|
||||||
|
setOffers(latestOffers);
|
||||||
|
});
|
||||||
|
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
CreateMessageHook(MarketPlaceOffersEvent, onMarketPlaceOffersEvent);
|
||||||
|
|
||||||
|
return (<>
|
||||||
|
<div className="btn-group" role="group">
|
||||||
|
<button type="button" className={`btn btn-primary ${searchType === MarketplaceSearchType.BY_ACTIVITY ? 'active' : ''}`} onClick={() => setSearchType(MarketplaceSearchType.BY_ACTIVITY)}>
|
||||||
|
{ LocalizeText('catalog.marketplace.search_by_activity') }
|
||||||
|
</button>
|
||||||
|
<button type="button" className={`btn btn-primary ${searchType === MarketplaceSearchType.BY_VALUE ? 'active' : ''}`} onClick={() => setSearchType(MarketplaceSearchType.BY_VALUE)}>
|
||||||
|
{ LocalizeText('catalog.marketplace.search_by_value') }
|
||||||
|
</button>
|
||||||
|
<button type="button" className={`btn btn-primary ${searchType === MarketplaceSearchType.ADVANCED ? 'active' : ''}`} onClick={() => setSearchType(MarketplaceSearchType.ADVANCED)}>
|
||||||
|
{ LocalizeText('catalog.marketplace.search_advanced') }
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<SearchFormView sortTypes={ getSortTypes } searchType={ searchType } onSearch={ requestOffers }/>
|
||||||
|
|
||||||
|
<div className='text-black'>{LocalizeText('catalog.marketplace.items_found', ['count'], [offers.size.toString()])}</div>
|
||||||
|
<NitroCardGridView columns={1} className='text-black'>
|
||||||
|
{
|
||||||
|
Array.from(offers.values()).map( (entry, index) => <MarketplaceItemView key={ index } offerData={ entry } type={ PUBLIC_OFFER } />)
|
||||||
|
}
|
||||||
|
</NitroCardGridView>
|
||||||
|
</>);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
import { FC, useCallback, useEffect, useState } from 'react';
|
||||||
|
import { LocalizeText } from '../../../../../../../api';
|
||||||
|
import { IMarketplaceSearchOptions } from '../common/IMarketplaceSearchOptions';
|
||||||
|
import { MarketplaceSearchType } from '../common/MarketplaceSearchType';
|
||||||
|
|
||||||
|
export interface SearchFormViewProps
|
||||||
|
{
|
||||||
|
searchType: number;
|
||||||
|
sortTypes: number[];
|
||||||
|
onSearch(options: IMarketplaceSearchOptions): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SearchFormView: FC<SearchFormViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { searchType = null, sortTypes = null, onSearch = null } = props;
|
||||||
|
const [ sortType, setSortType ] = useState(sortTypes ? sortTypes[0] : 3); // first item of SORT_TYPES_ACTIVITY
|
||||||
|
const [ searchQuery, setSearchQuery ] = useState('');
|
||||||
|
const [ min, setMin ] = useState(0);
|
||||||
|
const [ max, setMax ] = useState(0);
|
||||||
|
|
||||||
|
const onSortTypeChange = useCallback((sortType: number) =>
|
||||||
|
{
|
||||||
|
setSortType(sortType);
|
||||||
|
if(searchType === MarketplaceSearchType.BY_ACTIVITY || searchType === MarketplaceSearchType.BY_VALUE)
|
||||||
|
onSearch({ minPrice: -1, maxPrice: -1, query: '', type: sortType });
|
||||||
|
}, [onSearch, searchType]);
|
||||||
|
|
||||||
|
const onClickSearch = useCallback(() =>
|
||||||
|
{
|
||||||
|
const minPrice = min > 0 ? min : -1;
|
||||||
|
const maxPrice = max > 0 ? max : -1;
|
||||||
|
|
||||||
|
onSearch({ minPrice: minPrice, maxPrice: maxPrice, type: sortType, query: searchQuery })
|
||||||
|
}, [max, min, onSearch, searchQuery, sortType]);
|
||||||
|
|
||||||
|
useEffect( () =>
|
||||||
|
{
|
||||||
|
if(!sortTypes || !sortTypes.length) return;
|
||||||
|
|
||||||
|
const sortType = sortTypes[0];
|
||||||
|
setSortType(sortType);
|
||||||
|
|
||||||
|
if(searchType === MarketplaceSearchType.BY_ACTIVITY || MarketplaceSearchType.BY_VALUE === searchType)
|
||||||
|
onSearch({ minPrice: -1, maxPrice: -1, query: '', type: sortType });
|
||||||
|
}, [onSearch, searchType, sortTypes]);
|
||||||
|
|
||||||
|
return (<>
|
||||||
|
<div className="d-flex flex-row text-black">
|
||||||
|
<div className="mr-2 align-self-center col-4" style={ { whiteSpace: 'nowrap' } }>{ LocalizeText('catalog.marketplace.sort_order') }</div>
|
||||||
|
<select className="form-control form-control-sm" value={sortType} onChange={ (event) => onSortTypeChange(parseInt(event.target.value)) }>
|
||||||
|
{ sortTypes.map( (type, index) => <option key={index} value={type}>{ LocalizeText(`catalog.marketplace.sort.${type}`) }</option>)}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
{ searchType === MarketplaceSearchType.ADVANCED && <>
|
||||||
|
<div className="d-flex flex-row text-black">
|
||||||
|
<div className="mr-2 align-self-center col-4" style={ { whiteSpace: 'nowrap' } }>{ LocalizeText('catalog.marketplace.search_name') }</div>
|
||||||
|
<input className="form-control form-control-sm" type="text" value={ searchQuery} onChange={event => setSearchQuery(event.target.value)}/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="d-flex flex-row text-black">
|
||||||
|
<div className="mr-2 align-self-center col-4" style={ { whiteSpace: 'nowrap' } }>{ LocalizeText('catalog.marketplace.search_price') }</div>
|
||||||
|
<input className="form-control form-control-sm" type="number" min={0} value={ min } onChange={ event => setMin(event.target.valueAsNumber) } />
|
||||||
|
<input className="form-control form-control-sm" type="number" min={0} value={ max } onChange={ event => setMax(event.target.valueAsNumber) } />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button className="btn btn-secondary btn-sm float-end mx-auto" onClick={onClickSearch}>{ LocalizeText('generic.search') }</button>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</>);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user