@rpg-engine/long-bow 0.8.145 → 0.8.148
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Marketplace/BlueprintSearchModal.d.ts +2 -0
- package/dist/components/Marketplace/BlueprintTable.d.ts +9 -0
- package/dist/components/Marketplace/BuyOrderDetailsModal.d.ts +2 -0
- package/dist/components/Marketplace/BuyOrderPanel.d.ts +2 -0
- package/dist/long-bow.cjs.development.js +280 -151
- package/dist/long-bow.cjs.development.js.map +1 -1
- package/dist/long-bow.cjs.production.min.js +1 -1
- package/dist/long-bow.cjs.production.min.js.map +1 -1
- package/dist/long-bow.esm.js +280 -151
- package/dist/long-bow.esm.js.map +1 -1
- package/package.json +2 -2
- package/src/components/Marketplace/BlueprintSearchModal.tsx +77 -146
- package/src/components/Marketplace/BlueprintTable.tsx +158 -0
- package/src/components/Marketplace/BuyOrderDetailsModal.tsx +17 -10
- package/src/components/Marketplace/BuyOrderPanel.tsx +75 -22
- package/src/components/Marketplace/HistoryPanel.tsx +1 -1
- package/src/components/Marketplace/Marketplace.tsx +6 -1
- package/src/stories/Features/marketplace/BuyOrderPanel.stories.tsx +197 -81
- package/src/stories/Features/marketplace/HistoryPanel.stories.tsx +3 -3
- package/src/stories/Features/trading/Marketplace.stories.tsx +30 -7
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
IMarketplaceBlueprintSummary,
|
|
3
3
|
IMarketplaceBuyOrderItem,
|
|
4
|
+
MarketplaceBuyOrderStatus,
|
|
4
5
|
} from '@rpg-engine/shared';
|
|
5
6
|
import { Search } from 'pixelarticons/react/Search';
|
|
6
|
-
import React from 'react';
|
|
7
|
+
import React, { useEffect, useState } from 'react';
|
|
7
8
|
import styled from 'styled-components';
|
|
8
9
|
import { Pager } from '../Pager';
|
|
9
10
|
import { CTAButton } from '../shared/CTAButton/CTAButton';
|
|
11
|
+
import { SegmentedToggle } from '../shared/SegmentedToggle';
|
|
10
12
|
import { rarityColor } from '../Item/Inventory/ItemSlotRarity';
|
|
11
13
|
import { SpriteFromAtlas } from '../shared/SpriteFromAtlas';
|
|
12
14
|
import { BuyOrderDetailsModal } from './BuyOrderDetailsModal';
|
|
@@ -31,6 +33,8 @@ export interface IBuyOrderPanelProps {
|
|
|
31
33
|
yourBuyOrdersPage: number;
|
|
32
34
|
onYourBuyOrdersPageChange: (page: number) => void;
|
|
33
35
|
onCancelBuyOrder: (buyOrderId: string) => void;
|
|
36
|
+
enableHotkeys?: () => void;
|
|
37
|
+
disableHotkeys?: () => void;
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
const BUY_ORDERS_PER_PAGE = 5;
|
|
@@ -54,25 +58,48 @@ export const BuyOrderPanel: React.FC<IBuyOrderPanelProps> = (props) => {
|
|
|
54
58
|
yourBuyOrdersPage,
|
|
55
59
|
onYourBuyOrdersPageChange,
|
|
56
60
|
onCancelBuyOrder,
|
|
61
|
+
enableHotkeys,
|
|
62
|
+
disableHotkeys,
|
|
57
63
|
} = props;
|
|
58
64
|
|
|
65
|
+
// Local blueprint display: cleared immediately on Place Request so the
|
|
66
|
+
// panel returns to "Select Item" without waiting for the consumer to update
|
|
67
|
+
// the prop. Cancel keeps it shown so the user can reopen the modal.
|
|
68
|
+
const [displayedBlueprint, setDisplayedBlueprint] = useState(selectedBlueprint);
|
|
69
|
+
const [isDetailsOpen, setIsDetailsOpen] = useState(!!selectedBlueprint);
|
|
70
|
+
const [statusFilter, setStatusFilter] = useState<'Active' | 'Fulfilled' | 'Expired' | 'All'>('Active');
|
|
71
|
+
|
|
72
|
+
// Sync when consumer provides a new blueprint (e.g. after search selection)
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (selectedBlueprint) {
|
|
75
|
+
setDisplayedBlueprint(selectedBlueprint);
|
|
76
|
+
setIsDetailsOpen(true);
|
|
77
|
+
}
|
|
78
|
+
}, [selectedBlueprint]);
|
|
79
|
+
|
|
80
|
+
// Place request: clear display + close modal, then notify consumer
|
|
59
81
|
const handleConfirm = () => {
|
|
60
82
|
onPlaceBuyOrder();
|
|
83
|
+
setDisplayedBlueprint(undefined);
|
|
84
|
+
setIsDetailsOpen(false);
|
|
61
85
|
onCloseDetails?.();
|
|
86
|
+
enableHotkeys?.();
|
|
62
87
|
};
|
|
63
88
|
|
|
89
|
+
// Cancel: just close the modal, keep blueprint displayed for reopening
|
|
64
90
|
const handleCloseDetails = () => {
|
|
65
|
-
|
|
91
|
+
setIsDetailsOpen(false);
|
|
92
|
+
enableHotkeys?.();
|
|
66
93
|
};
|
|
67
94
|
|
|
68
95
|
return (
|
|
69
96
|
<PanelWrapper>
|
|
70
97
|
{/* ── Details modal ── */}
|
|
71
|
-
{
|
|
98
|
+
{displayedBlueprint && isDetailsOpen && (
|
|
72
99
|
<BuyOrderDetailsModal
|
|
73
100
|
isOpen
|
|
74
101
|
onClose={handleCloseDetails}
|
|
75
|
-
blueprint={
|
|
102
|
+
blueprint={displayedBlueprint}
|
|
76
103
|
quantity={currentQuantity}
|
|
77
104
|
maxPrice={currentMaxPrice}
|
|
78
105
|
rarity={selectedRarity}
|
|
@@ -82,18 +109,20 @@ export const BuyOrderPanel: React.FC<IBuyOrderPanelProps> = (props) => {
|
|
|
82
109
|
onConfirm={handleConfirm}
|
|
83
110
|
atlasJSON={atlasJSON}
|
|
84
111
|
atlasIMG={atlasIMG}
|
|
112
|
+
enableHotkeys={enableHotkeys}
|
|
113
|
+
disableHotkeys={disableHotkeys}
|
|
85
114
|
/>
|
|
86
115
|
)}
|
|
87
116
|
|
|
88
117
|
{/* ── Form row — outside scroll so dropdown isn't clipped ── */}
|
|
89
118
|
<FormRow>
|
|
90
|
-
{
|
|
119
|
+
{displayedBlueprint ? (
|
|
91
120
|
<SelectedBlueprintDisplay onPointerDown={onOpenBlueprintSearch}>
|
|
92
121
|
<RarityContainer $rarity={selectedRarity}>
|
|
93
122
|
<SpriteFromAtlas
|
|
94
123
|
atlasIMG={atlasIMG}
|
|
95
124
|
atlasJSON={atlasJSON}
|
|
96
|
-
spriteKey={
|
|
125
|
+
spriteKey={displayedBlueprint.texturePath || displayedBlueprint.key}
|
|
97
126
|
width={32}
|
|
98
127
|
height={32}
|
|
99
128
|
imgScale={2}
|
|
@@ -101,7 +130,7 @@ export const BuyOrderPanel: React.FC<IBuyOrderPanelProps> = (props) => {
|
|
|
101
130
|
/>
|
|
102
131
|
</RarityContainer>
|
|
103
132
|
<ChangeTextWrapper>
|
|
104
|
-
<BlueprintName>{
|
|
133
|
+
<BlueprintName>{displayedBlueprint.name}</BlueprintName>
|
|
105
134
|
<ChangeText>change</ChangeText>
|
|
106
135
|
</ChangeTextWrapper>
|
|
107
136
|
</SelectedBlueprintDisplay>
|
|
@@ -116,23 +145,40 @@ export const BuyOrderPanel: React.FC<IBuyOrderPanelProps> = (props) => {
|
|
|
116
145
|
</FormRow>
|
|
117
146
|
|
|
118
147
|
{/* ── Scrollable lists ──────────────────────────────── */}
|
|
148
|
+
<FilterRow>
|
|
149
|
+
<SectionTitle>Your Buy Requests</SectionTitle>
|
|
150
|
+
<SegmentedToggle
|
|
151
|
+
options={[
|
|
152
|
+
{ id: 'Active', label: 'Active' },
|
|
153
|
+
{ id: 'Fulfilled', label: 'Fulfilled' },
|
|
154
|
+
{ id: 'Expired', label: 'Expired' },
|
|
155
|
+
{ id: 'All', label: 'All' },
|
|
156
|
+
]}
|
|
157
|
+
activeId={statusFilter}
|
|
158
|
+
onChange={(id) => setStatusFilter(id as typeof statusFilter)}
|
|
159
|
+
/>
|
|
160
|
+
</FilterRow>
|
|
119
161
|
<ScrollArea id="MarketContainer">
|
|
120
162
|
<Section>
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
<
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
163
|
+
{(() => {
|
|
164
|
+
const filtered = statusFilter === 'All'
|
|
165
|
+
? yourBuyOrders
|
|
166
|
+
: yourBuyOrders.filter(o => o.status === MarketplaceBuyOrderStatus[statusFilter as keyof typeof MarketplaceBuyOrderStatus]);
|
|
167
|
+
return filtered.length === 0 ? (
|
|
168
|
+
<EmptyState>No requests yet</EmptyState>
|
|
169
|
+
) : (
|
|
170
|
+
filtered.map((order) => (
|
|
171
|
+
<BuyOrderRow
|
|
172
|
+
key={order._id}
|
|
173
|
+
buyOrder={order}
|
|
174
|
+
atlasJSON={atlasJSON}
|
|
175
|
+
atlasIMG={atlasIMG}
|
|
176
|
+
isOwn
|
|
177
|
+
onCancel={onCancelBuyOrder}
|
|
178
|
+
/>
|
|
179
|
+
))
|
|
180
|
+
);
|
|
181
|
+
})()}
|
|
136
182
|
{yourBuyOrdersTotal > BUY_ORDERS_PER_PAGE && (
|
|
137
183
|
<PagerRow>
|
|
138
184
|
<Pager
|
|
@@ -170,6 +216,13 @@ const FormRow = styled.div`
|
|
|
170
216
|
overflow: visible;
|
|
171
217
|
`;
|
|
172
218
|
|
|
219
|
+
const FilterRow = styled.div`
|
|
220
|
+
display: flex;
|
|
221
|
+
align-items: center;
|
|
222
|
+
justify-content: space-between;
|
|
223
|
+
padding: 0 4px;
|
|
224
|
+
`;
|
|
225
|
+
|
|
173
226
|
const ScrollArea = styled.div`
|
|
174
227
|
display: flex;
|
|
175
228
|
flex-direction: column;
|
|
@@ -160,7 +160,7 @@ export const HistoryPanel: React.FC<IHistoryPanelProps> = ({
|
|
|
160
160
|
|
|
161
161
|
{tx.goldAmount > 0 && (
|
|
162
162
|
<PriceSection>
|
|
163
|
-
{tx.currency === 'dc' ? (
|
|
163
|
+
{tx.metadata?.['currency'] === 'dc' ? (
|
|
164
164
|
// Show DC only
|
|
165
165
|
<DCPriceRow>
|
|
166
166
|
{atlasIMG && atlasJSON && (
|
|
@@ -164,6 +164,7 @@ export const Marketplace: React.FC<IMarketPlaceProps> = props => {
|
|
|
164
164
|
|
|
165
165
|
const handleBlueprintSelect = (blueprint: IMarketplaceBlueprintSummary) => {
|
|
166
166
|
setIsBlueprintSearchOpen(false);
|
|
167
|
+
props.enableHotkeys?.();
|
|
167
168
|
onBlueprintSelect?.(blueprint);
|
|
168
169
|
};
|
|
169
170
|
|
|
@@ -239,10 +240,12 @@ export const Marketplace: React.FC<IMarketPlaceProps> = props => {
|
|
|
239
240
|
yourBuyOrdersPage={yourBuyOrdersPage}
|
|
240
241
|
onYourBuyOrdersPageChange={onYourBuyOrdersPageChange ?? (() => {})}
|
|
241
242
|
onCancelBuyOrder={onCancelBuyOrder ?? (() => {})}
|
|
243
|
+
enableHotkeys={props.enableHotkeys}
|
|
244
|
+
disableHotkeys={props.disableHotkeys}
|
|
242
245
|
/>
|
|
243
246
|
<BlueprintSearchModal
|
|
244
247
|
isOpen={isBlueprintSearchOpen}
|
|
245
|
-
onClose={() => setIsBlueprintSearchOpen(false)}
|
|
248
|
+
onClose={() => { setIsBlueprintSearchOpen(false); props.enableHotkeys?.(); }}
|
|
246
249
|
onSelect={handleBlueprintSelect}
|
|
247
250
|
onSearch={onBlueprintSearch ?? (() => {})}
|
|
248
251
|
blueprints={blueprintSearchResults}
|
|
@@ -251,6 +254,8 @@ export const Marketplace: React.FC<IMarketPlaceProps> = props => {
|
|
|
251
254
|
isLoading={blueprintSearchIsLoading}
|
|
252
255
|
atlasJSON={props.atlasJSON}
|
|
253
256
|
atlasIMG={props.atlasIMG}
|
|
257
|
+
enableHotkeys={props.enableHotkeys}
|
|
258
|
+
disableHotkeys={props.disableHotkeys}
|
|
254
259
|
/>
|
|
255
260
|
</>
|
|
256
261
|
)}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
+
IMarketplaceBlueprintSearchRequest,
|
|
2
3
|
IMarketplaceBlueprintSummary,
|
|
3
4
|
IMarketplaceBuyOrderItem,
|
|
4
5
|
MarketplaceBuyOrderStatus,
|
|
5
6
|
} from '@rpg-engine/shared';
|
|
6
7
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
7
8
|
import React, { useState } from 'react';
|
|
9
|
+
import { BlueprintSearchModal } from '../../../components/Marketplace/BlueprintSearchModal';
|
|
8
10
|
import { BuyOrderPanel } from '../../../components/Marketplace/BuyOrderPanel';
|
|
9
11
|
import { RPGUIRoot } from '../../../components/RPGUI/RPGUIRoot';
|
|
10
12
|
import atlasJSON from '../../../mocks/atlas/items/items.json';
|
|
@@ -44,6 +46,36 @@ const mockSelectedBlueprint: IMarketplaceBlueprintSummary = {
|
|
|
44
46
|
texturePath: 'swords/broad-sword.png',
|
|
45
47
|
};
|
|
46
48
|
|
|
49
|
+
const mockBlueprintSearchResults: IMarketplaceBlueprintSummary[] = [
|
|
50
|
+
{
|
|
51
|
+
key: 'items/broad-sword',
|
|
52
|
+
name: 'Broad Sword',
|
|
53
|
+
type: 'Weapon',
|
|
54
|
+
subType: 'Sword',
|
|
55
|
+
tier: 1,
|
|
56
|
+
textureAtlas: 'items',
|
|
57
|
+
texturePath: 'swords/broad-sword.png',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
key: 'items/angelic-sword',
|
|
61
|
+
name: 'Angelic Sword',
|
|
62
|
+
type: 'Weapon',
|
|
63
|
+
subType: 'Sword',
|
|
64
|
+
tier: 5,
|
|
65
|
+
textureAtlas: 'items',
|
|
66
|
+
texturePath: 'swords/angelic-sword.png',
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
key: 'items/leather-armor',
|
|
70
|
+
name: 'Leather Armor',
|
|
71
|
+
type: 'Armor',
|
|
72
|
+
subType: 'Body',
|
|
73
|
+
tier: 1,
|
|
74
|
+
textureAtlas: 'items',
|
|
75
|
+
texturePath: 'armors/leather-armor.png',
|
|
76
|
+
},
|
|
77
|
+
];
|
|
78
|
+
|
|
47
79
|
const mockBuyOrders: IMarketplaceBuyOrderItem[] = [
|
|
48
80
|
{
|
|
49
81
|
_id: 'order-1',
|
|
@@ -84,113 +116,197 @@ const mockBuyOrders: IMarketplaceBuyOrderItem[] = [
|
|
|
84
116
|
|
|
85
117
|
const DefaultWrapper = () => {
|
|
86
118
|
const [selectedBlueprint, setSelectedBlueprint] = useState<IMarketplaceBlueprintSummary | undefined>();
|
|
119
|
+
const [isBlueprintSearchOpen, setIsBlueprintSearchOpen] = useState(false);
|
|
87
120
|
const [quantity, setQuantity] = useState(0);
|
|
88
121
|
const [maxPrice, setMaxPrice] = useState(0);
|
|
89
122
|
const [rarity, setRarity] = useState('');
|
|
90
123
|
const [yourOrders, setYourOrders] = useState<IMarketplaceBuyOrderItem[]>(mockBuyOrders);
|
|
91
124
|
|
|
125
|
+
const handleBlueprintSelect = (blueprint: IMarketplaceBlueprintSummary) => {
|
|
126
|
+
setIsBlueprintSearchOpen(false);
|
|
127
|
+
setSelectedBlueprint(blueprint);
|
|
128
|
+
};
|
|
129
|
+
|
|
92
130
|
return (
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
<>
|
|
132
|
+
<BuyOrderPanel
|
|
133
|
+
atlasJSON={atlasJSON}
|
|
134
|
+
atlasIMG={atlasIMG}
|
|
135
|
+
selectedBlueprint={selectedBlueprint}
|
|
136
|
+
onOpenBlueprintSearch={() => setIsBlueprintSearchOpen(true)}
|
|
137
|
+
onCloseDetails={() => {
|
|
138
|
+
setSelectedBlueprint(undefined);
|
|
139
|
+
setQuantity(0);
|
|
140
|
+
setMaxPrice(0);
|
|
141
|
+
setRarity('');
|
|
142
|
+
}}
|
|
143
|
+
onQuantityChange={setQuantity}
|
|
144
|
+
onMaxPriceChange={setMaxPrice}
|
|
145
|
+
onRarityChange={setRarity}
|
|
146
|
+
onPlaceBuyOrder={() => {
|
|
147
|
+
if (selectedBlueprint) {
|
|
148
|
+
const newOrder: IMarketplaceBuyOrderItem = {
|
|
149
|
+
_id: `order-${Date.now()}`,
|
|
150
|
+
owner: 'character-1',
|
|
151
|
+
itemBlueprintKey: selectedBlueprint.key,
|
|
152
|
+
itemRarity: rarity as any,
|
|
153
|
+
maxPrice,
|
|
154
|
+
escrowedGold: maxPrice,
|
|
155
|
+
fee: Math.floor(maxPrice * 0.05),
|
|
156
|
+
status: MarketplaceBuyOrderStatus.Active,
|
|
157
|
+
createdAt: new Date(),
|
|
158
|
+
updatedAt: new Date(),
|
|
159
|
+
};
|
|
160
|
+
setYourOrders(prev => [newOrder, ...prev]);
|
|
161
|
+
}
|
|
162
|
+
}}
|
|
163
|
+
currentQuantity={quantity}
|
|
164
|
+
currentMaxPrice={maxPrice}
|
|
165
|
+
selectedRarity={rarity}
|
|
166
|
+
yourBuyOrders={yourOrders}
|
|
167
|
+
yourBuyOrdersTotal={yourOrders.length}
|
|
168
|
+
yourBuyOrdersPage={1}
|
|
169
|
+
onYourBuyOrdersPageChange={(page) => console.log('Page:', page)}
|
|
170
|
+
onCancelBuyOrder={(id) => setYourOrders(prev => prev.filter(o => o._id !== id))}
|
|
171
|
+
/>
|
|
172
|
+
<BlueprintSearchModal
|
|
173
|
+
isOpen={isBlueprintSearchOpen}
|
|
174
|
+
onClose={() => setIsBlueprintSearchOpen(false)}
|
|
175
|
+
onSelect={handleBlueprintSelect}
|
|
176
|
+
onSearch={(req: IMarketplaceBlueprintSearchRequest) => console.log('search:', req)}
|
|
177
|
+
blueprints={mockBlueprintSearchResults}
|
|
178
|
+
totalCount={mockBlueprintSearchResults.length}
|
|
179
|
+
currentPage={1}
|
|
180
|
+
isLoading={false}
|
|
181
|
+
atlasJSON={atlasJSON}
|
|
182
|
+
atlasIMG={atlasIMG}
|
|
183
|
+
/>
|
|
184
|
+
</>
|
|
133
185
|
);
|
|
134
186
|
};
|
|
135
187
|
|
|
136
188
|
const WithBlueprintWrapper = () => {
|
|
137
189
|
const [selectedBlueprint, setSelectedBlueprint] = useState<IMarketplaceBlueprintSummary | undefined>(mockSelectedBlueprint);
|
|
190
|
+
const [isBlueprintSearchOpen, setIsBlueprintSearchOpen] = useState(false);
|
|
138
191
|
const [quantity, setQuantity] = useState(0);
|
|
139
192
|
const [maxPrice, setMaxPrice] = useState(0);
|
|
140
193
|
const [rarity, setRarity] = useState('');
|
|
141
194
|
const [yourOrders, setYourOrders] = useState<IMarketplaceBuyOrderItem[]>(mockBuyOrders);
|
|
142
195
|
|
|
196
|
+
const handleBlueprintSelect = (blueprint: IMarketplaceBlueprintSummary) => {
|
|
197
|
+
setIsBlueprintSearchOpen(false);
|
|
198
|
+
setSelectedBlueprint(blueprint);
|
|
199
|
+
};
|
|
200
|
+
|
|
143
201
|
return (
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
202
|
+
<>
|
|
203
|
+
<BuyOrderPanel
|
|
204
|
+
atlasJSON={atlasJSON}
|
|
205
|
+
atlasIMG={atlasIMG}
|
|
206
|
+
selectedBlueprint={selectedBlueprint}
|
|
207
|
+
onOpenBlueprintSearch={() => setIsBlueprintSearchOpen(true)}
|
|
208
|
+
onCloseDetails={() => {
|
|
209
|
+
setSelectedBlueprint(undefined);
|
|
210
|
+
setQuantity(0);
|
|
211
|
+
setMaxPrice(0);
|
|
212
|
+
setRarity('');
|
|
213
|
+
}}
|
|
214
|
+
onQuantityChange={setQuantity}
|
|
215
|
+
onMaxPriceChange={setMaxPrice}
|
|
216
|
+
onRarityChange={setRarity}
|
|
217
|
+
onPlaceBuyOrder={() => {
|
|
218
|
+
if (selectedBlueprint) {
|
|
219
|
+
const newOrder: IMarketplaceBuyOrderItem = {
|
|
220
|
+
_id: `order-${Date.now()}`,
|
|
221
|
+
owner: 'character-1',
|
|
222
|
+
itemBlueprintKey: selectedBlueprint.key,
|
|
223
|
+
itemRarity: rarity as any,
|
|
224
|
+
maxPrice,
|
|
225
|
+
escrowedGold: maxPrice,
|
|
226
|
+
fee: Math.floor(maxPrice * 0.05),
|
|
227
|
+
status: MarketplaceBuyOrderStatus.Active,
|
|
228
|
+
createdAt: new Date(),
|
|
229
|
+
updatedAt: new Date(),
|
|
230
|
+
};
|
|
231
|
+
setYourOrders(prev => [newOrder, ...prev]);
|
|
232
|
+
}
|
|
233
|
+
}}
|
|
234
|
+
currentQuantity={quantity}
|
|
235
|
+
currentMaxPrice={maxPrice}
|
|
236
|
+
selectedRarity={rarity}
|
|
237
|
+
yourBuyOrders={yourOrders}
|
|
238
|
+
yourBuyOrdersTotal={yourOrders.length}
|
|
239
|
+
yourBuyOrdersPage={1}
|
|
240
|
+
onYourBuyOrdersPageChange={(page) => console.log('Page:', page)}
|
|
241
|
+
onCancelBuyOrder={(id) => setYourOrders(prev => prev.filter(o => o._id !== id))}
|
|
242
|
+
/>
|
|
243
|
+
<BlueprintSearchModal
|
|
244
|
+
isOpen={isBlueprintSearchOpen}
|
|
245
|
+
onClose={() => setIsBlueprintSearchOpen(false)}
|
|
246
|
+
onSelect={handleBlueprintSelect}
|
|
247
|
+
onSearch={(req: IMarketplaceBlueprintSearchRequest) => console.log('search:', req)}
|
|
248
|
+
blueprints={mockBlueprintSearchResults}
|
|
249
|
+
totalCount={mockBlueprintSearchResults.length}
|
|
250
|
+
currentPage={1}
|
|
251
|
+
isLoading={false}
|
|
252
|
+
atlasJSON={atlasJSON}
|
|
253
|
+
atlasIMG={atlasIMG}
|
|
254
|
+
/>
|
|
255
|
+
</>
|
|
168
256
|
);
|
|
169
257
|
};
|
|
170
258
|
|
|
171
259
|
const EmptyWrapper = () => {
|
|
260
|
+
const [isBlueprintSearchOpen, setIsBlueprintSearchOpen] = useState(false);
|
|
261
|
+
const [selectedBlueprint, setSelectedBlueprint] = useState<IMarketplaceBlueprintSummary | undefined>();
|
|
172
262
|
const [quantity, setQuantity] = useState(0);
|
|
173
263
|
const [maxPrice, setMaxPrice] = useState(0);
|
|
174
264
|
const [rarity, setRarity] = useState('');
|
|
175
265
|
|
|
266
|
+
const handleBlueprintSelect = (blueprint: IMarketplaceBlueprintSummary) => {
|
|
267
|
+
setIsBlueprintSearchOpen(false);
|
|
268
|
+
setSelectedBlueprint(blueprint);
|
|
269
|
+
};
|
|
270
|
+
|
|
176
271
|
return (
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
272
|
+
<>
|
|
273
|
+
<BuyOrderPanel
|
|
274
|
+
atlasJSON={atlasJSON}
|
|
275
|
+
atlasIMG={atlasIMG}
|
|
276
|
+
selectedBlueprint={selectedBlueprint}
|
|
277
|
+
onOpenBlueprintSearch={() => setIsBlueprintSearchOpen(true)}
|
|
278
|
+
onCloseDetails={() => {
|
|
279
|
+
setSelectedBlueprint(undefined);
|
|
280
|
+
setQuantity(0);
|
|
281
|
+
setMaxPrice(0);
|
|
282
|
+
setRarity('');
|
|
283
|
+
}}
|
|
284
|
+
onQuantityChange={setQuantity}
|
|
285
|
+
onMaxPriceChange={setMaxPrice}
|
|
286
|
+
onRarityChange={setRarity}
|
|
287
|
+
onPlaceBuyOrder={() => console.log('place order')}
|
|
288
|
+
currentQuantity={quantity}
|
|
289
|
+
currentMaxPrice={maxPrice}
|
|
290
|
+
selectedRarity={rarity}
|
|
291
|
+
yourBuyOrders={[]}
|
|
292
|
+
yourBuyOrdersTotal={0}
|
|
293
|
+
yourBuyOrdersPage={1}
|
|
294
|
+
onYourBuyOrdersPageChange={(page) => console.log('Page:', page)}
|
|
295
|
+
onCancelBuyOrder={(id) => console.log('Cancel:', id)}
|
|
296
|
+
/>
|
|
297
|
+
<BlueprintSearchModal
|
|
298
|
+
isOpen={isBlueprintSearchOpen}
|
|
299
|
+
onClose={() => setIsBlueprintSearchOpen(false)}
|
|
300
|
+
onSelect={handleBlueprintSelect}
|
|
301
|
+
onSearch={(req: IMarketplaceBlueprintSearchRequest) => console.log('search:', req)}
|
|
302
|
+
blueprints={mockBlueprintSearchResults}
|
|
303
|
+
totalCount={mockBlueprintSearchResults.length}
|
|
304
|
+
currentPage={1}
|
|
305
|
+
isLoading={false}
|
|
306
|
+
atlasJSON={atlasJSON}
|
|
307
|
+
atlasIMG={atlasIMG}
|
|
308
|
+
/>
|
|
309
|
+
</>
|
|
194
310
|
);
|
|
195
311
|
};
|
|
196
312
|
|
|
@@ -41,7 +41,7 @@ const mockTransactions: IMarketplaceTransaction[] = [
|
|
|
41
41
|
itemKey: 'items/iron-sword.png',
|
|
42
42
|
itemName: 'Iron Sword',
|
|
43
43
|
counterpartName: 'SomePlayer',
|
|
44
|
-
currency: 'gold',
|
|
44
|
+
metadata: { currency: 'gold' },
|
|
45
45
|
createdAt: daysAgo(0),
|
|
46
46
|
updatedAt: daysAgo(0),
|
|
47
47
|
},
|
|
@@ -52,7 +52,7 @@ const mockTransactions: IMarketplaceTransaction[] = [
|
|
|
52
52
|
itemKey: 'items/leather-armor.png',
|
|
53
53
|
itemName: 'Leather Armor',
|
|
54
54
|
counterpartName: 'AnotherPlayer',
|
|
55
|
-
currency: 'dc',
|
|
55
|
+
metadata: { currency: 'dc' },
|
|
56
56
|
createdAt: daysAgo(1),
|
|
57
57
|
updatedAt: daysAgo(1),
|
|
58
58
|
},
|
|
@@ -63,7 +63,7 @@ const mockTransactions: IMarketplaceTransaction[] = [
|
|
|
63
63
|
itemKey: 'items/angelic-sword.png',
|
|
64
64
|
itemName: 'Angelic Sword',
|
|
65
65
|
counterpartName: 'DCBuyer',
|
|
66
|
-
currency: 'dc',
|
|
66
|
+
metadata: { currency: 'dc' },
|
|
67
67
|
createdAt: daysAgo(2),
|
|
68
68
|
updatedAt: daysAgo(2),
|
|
69
69
|
},
|
|
@@ -49,9 +49,9 @@ const mockOpenBuyOrders: IMarketplaceBuyOrderItem[] = [
|
|
|
49
49
|
];
|
|
50
50
|
|
|
51
51
|
const mockTransactions: IMarketplaceTransaction[] = [
|
|
52
|
-
{ owner: 'player-1', type: MarketplaceTransactionType.Purchase, goldAmount: 450, itemKey: 'items/broad-sword', itemName: 'Broad Sword', counterpartName: 'MerchantKing', currency: 'gold', createdAt: daysAgo(1), updatedAt: daysAgo(1) },
|
|
53
|
-
{ owner: 'player-1', type: MarketplaceTransactionType.Sale, goldAmount: 1200, itemKey: 'items/angelic-sword', itemName: 'Angelic Sword', counterpartName: 'ShadowBlade', currency: 'dc', createdAt: daysAgo(2), updatedAt: daysAgo(2) },
|
|
54
|
-
{ owner: 'player-1', type: MarketplaceTransactionType.Purchase, goldAmount: 275000, itemKey: 'items/angelic-sword', itemName: 'Angelic Sword', counterpartName: 'DCBuyer', currency: 'dc', createdAt: daysAgo(3), updatedAt: daysAgo(3) },
|
|
52
|
+
{ owner: 'player-1', type: MarketplaceTransactionType.Purchase, goldAmount: 450, itemKey: 'items/broad-sword', itemName: 'Broad Sword', counterpartName: 'MerchantKing', metadata: { currency: 'gold' }, createdAt: daysAgo(1), updatedAt: daysAgo(1) },
|
|
53
|
+
{ owner: 'player-1', type: MarketplaceTransactionType.Sale, goldAmount: 1200, itemKey: 'items/angelic-sword', itemName: 'Angelic Sword', counterpartName: 'ShadowBlade', metadata: { currency: 'dc' }, createdAt: daysAgo(2), updatedAt: daysAgo(2) },
|
|
54
|
+
{ owner: 'player-1', type: MarketplaceTransactionType.Purchase, goldAmount: 275000, itemKey: 'items/angelic-sword', itemName: 'Angelic Sword', counterpartName: 'DCBuyer', metadata: { currency: 'dc' }, createdAt: daysAgo(3), updatedAt: daysAgo(3) },
|
|
55
55
|
{ owner: 'player-1', type: MarketplaceTransactionType.ListingFee, goldAmount: 60, itemKey: 'items/angelic-sword', itemName: 'Angelic Sword', createdAt: daysAgo(2), updatedAt: daysAgo(2) },
|
|
56
56
|
{ owner: 'player-1', type: MarketplaceTransactionType.Withdrawal, goldAmount: 3200, createdAt: daysAgo(4), updatedAt: daysAgo(4) },
|
|
57
57
|
{ owner: 'player-1', type: MarketplaceTransactionType.Expired, goldAmount: 0, itemKey: 'items/leather-armor', itemName: 'Leather Armor', createdAt: daysAgo(7), updatedAt: daysAgo(7) },
|
|
@@ -69,6 +69,7 @@ const Template: Story = () => {
|
|
|
69
69
|
const [blueprintResults, setBlueprintResults] = React.useState<IMarketplaceBlueprintSummary[]>([]);
|
|
70
70
|
const [blueprintLoading, setBlueprintLoading] = React.useState(false);
|
|
71
71
|
const [historyType, setHistoryType] = React.useState('All');
|
|
72
|
+
const [yourBuyOrders, setYourBuyOrders] = React.useState<IMarketplaceBuyOrderItem[]>(mockYourBuyOrders);
|
|
72
73
|
|
|
73
74
|
const handleBlueprintSearch = (request: any) => {
|
|
74
75
|
console.log('blueprint search:', request);
|
|
@@ -132,12 +133,34 @@ const Template: Story = () => {
|
|
|
132
133
|
onBuyOrderQuantityChange={setQuantity}
|
|
133
134
|
onBuyOrderMaxPriceChange={setMaxPrice}
|
|
134
135
|
onBuyOrderRarityChange={setSelectedRarity}
|
|
135
|
-
onPlaceBuyOrder={() =>
|
|
136
|
-
|
|
137
|
-
|
|
136
|
+
onPlaceBuyOrder={() => {
|
|
137
|
+
if (selectedBlueprint) {
|
|
138
|
+
const newOrder: IMarketplaceBuyOrderItem = {
|
|
139
|
+
_id: `bo-${Date.now()}`,
|
|
140
|
+
owner: 'player-1',
|
|
141
|
+
itemBlueprintKey: selectedBlueprint.key,
|
|
142
|
+
itemRarity: selectedRarity as any,
|
|
143
|
+
maxPrice,
|
|
144
|
+
escrowedGold: maxPrice,
|
|
145
|
+
fee: Math.floor(maxPrice * 0.05),
|
|
146
|
+
status: MarketplaceBuyOrderStatus.Active,
|
|
147
|
+
createdAt: new Date(),
|
|
148
|
+
updatedAt: new Date(),
|
|
149
|
+
};
|
|
150
|
+
setYourBuyOrders(prev => [newOrder, ...prev]);
|
|
151
|
+
}
|
|
152
|
+
}}
|
|
153
|
+
onClearBuyOrderBlueprint={() => {
|
|
154
|
+
setSelectedBlueprint(undefined);
|
|
155
|
+
setQuantity(1);
|
|
156
|
+
setMaxPrice(0);
|
|
157
|
+
setSelectedRarity('');
|
|
158
|
+
}}
|
|
159
|
+
yourBuyOrders={yourBuyOrders}
|
|
160
|
+
yourBuyOrdersTotal={yourBuyOrders.length}
|
|
138
161
|
yourBuyOrdersPage={1}
|
|
139
162
|
onYourBuyOrdersPageChange={p => console.log('your orders page:', p)}
|
|
140
|
-
onCancelBuyOrder={id =>
|
|
163
|
+
onCancelBuyOrder={id => setYourBuyOrders(prev => prev.filter(o => o._id !== id))}
|
|
141
164
|
openBuyOrders={mockOpenBuyOrders}
|
|
142
165
|
openBuyOrdersTotal={mockOpenBuyOrders.length}
|
|
143
166
|
openBuyOrdersPage={1}
|