@rpg-engine/long-bow 0.8.141 → 0.8.146

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.
Files changed (64) hide show
  1. package/dist/components/Marketplace/BlueprintSearchModal.d.ts +17 -0
  2. package/dist/components/Marketplace/BlueprintTable.d.ts +9 -0
  3. package/dist/components/Marketplace/BuyOrderDetailsModal.d.ts +17 -0
  4. package/dist/components/Marketplace/BuyOrderPanel.d.ts +24 -0
  5. package/dist/components/Marketplace/BuyOrderRows.d.ts +13 -0
  6. package/dist/components/Marketplace/BuyPanel.d.ts +9 -1
  7. package/dist/components/Marketplace/HistoryPanel.d.ts +18 -0
  8. package/dist/components/Marketplace/ManagmentPanel.d.ts +3 -2
  9. package/dist/components/Marketplace/Marketplace.d.ts +35 -2
  10. package/dist/components/Marketplace/MarketplaceSettingsPanel.d.ts +2 -1
  11. package/dist/components/Store/PaymentMethodModal.d.ts +1 -0
  12. package/dist/components/shared/LabelPill/LabelPill.d.ts +9 -0
  13. package/dist/components/shared/LabelPill/index.d.ts +1 -0
  14. package/dist/components/shared/SegmentedToggle/SegmentedToggle.d.ts +12 -0
  15. package/dist/components/shared/SegmentedToggle/index.d.ts +1 -0
  16. package/dist/index.d.ts +4 -0
  17. package/dist/long-bow.cjs.development.js +11573 -1288
  18. package/dist/long-bow.cjs.development.js.map +1 -1
  19. package/dist/long-bow.cjs.production.min.js +1 -1
  20. package/dist/long-bow.cjs.production.min.js.map +1 -1
  21. package/dist/long-bow.esm.js +11562 -1290
  22. package/dist/long-bow.esm.js.map +1 -1
  23. package/dist/stories/Features/marketplace/BlueprintSearchModal.stories.d.ts +1 -0
  24. package/dist/stories/Features/marketplace/BuyOrderPanel.stories.d.ts +1 -0
  25. package/dist/stories/Features/marketplace/BuyOrderRows.stories.d.ts +1 -0
  26. package/dist/stories/Features/marketplace/HistoryPanel.stories.d.ts +1 -0
  27. package/dist/stories/Features/trading/MarketplaceRows.stories.d.ts +2 -1
  28. package/dist/stories/UI/buttonsAndInputs/SegmentedToggle.stories.d.ts +6 -0
  29. package/dist/stories/UI/text/LabelPill.stories.d.ts +7 -0
  30. package/dist/utils/atlasUtils.d.ts +2 -0
  31. package/package.json +2 -2
  32. package/src/components/Marketplace/BlueprintSearchModal.tsx +295 -0
  33. package/src/components/Marketplace/BlueprintTable.tsx +158 -0
  34. package/src/components/Marketplace/BuyOrderDetailsModal.tsx +306 -0
  35. package/src/components/Marketplace/BuyOrderPanel.tsx +284 -0
  36. package/src/components/Marketplace/BuyOrderRows.tsx +287 -0
  37. package/src/components/Marketplace/BuyPanel.tsx +406 -166
  38. package/src/components/Marketplace/HistoryPanel.tsx +422 -0
  39. package/src/components/Marketplace/ManagmentPanel.tsx +13 -15
  40. package/src/components/Marketplace/Marketplace.tsx +181 -30
  41. package/src/components/Marketplace/MarketplaceBuyModal.tsx +1 -0
  42. package/src/components/Marketplace/MarketplaceRows.tsx +41 -10
  43. package/src/components/Marketplace/MarketplaceSettingsPanel.tsx +4 -3
  44. package/src/components/Store/CartView.tsx +11 -0
  45. package/src/components/Store/PaymentMethodModal.tsx +26 -9
  46. package/src/components/shared/LabelPill/LabelPill.tsx +45 -0
  47. package/src/components/shared/LabelPill/index.ts +1 -0
  48. package/src/components/shared/SegmentedToggle/SegmentedToggle.tsx +61 -0
  49. package/src/components/shared/SegmentedToggle/index.ts +1 -0
  50. package/src/components/shared/SpriteFromAtlas.tsx +7 -2
  51. package/src/index.tsx +4 -0
  52. package/src/mocks/atlas/items/items.json +33998 -25238
  53. package/src/mocks/atlas/items/items.png +0 -0
  54. package/src/mocks/itemContainer.mocks.ts +31 -0
  55. package/src/stories/Features/marketplace/BlueprintSearchModal.stories.tsx +145 -0
  56. package/src/stories/Features/marketplace/BuyOrderPanel.stories.tsx +323 -0
  57. package/src/stories/Features/marketplace/BuyOrderRows.stories.tsx +116 -0
  58. package/src/stories/Features/marketplace/HistoryPanel.stories.tsx +157 -0
  59. package/src/stories/Features/trading/Marketplace.stories.tsx +130 -0
  60. package/src/stories/Features/trading/MarketplaceRows.stories.tsx +11 -0
  61. package/src/stories/UI/buttonsAndInputs/SegmentedToggle.stories.tsx +54 -0
  62. package/src/stories/UI/text/LabelPill.stories.tsx +43 -0
  63. package/src/utils/__test__/atlasUtils.spec.ts +26 -0
  64. package/src/utils/atlasUtils.ts +80 -0
Binary file
@@ -650,6 +650,37 @@ export const items: IItem[] = [
650
650
  fullDescription: '',
651
651
  usableEffectDescription: 'Regenerates 10 HP and Mana 5 times',
652
652
  },
653
+ {
654
+ type: ItemType.Consumable,
655
+ subType: ItemSubType.Food,
656
+ rarity: 'Common',
657
+ textureAtlas: 'items',
658
+ allowedEquipSlotType: [],
659
+ maxStackSize: 100000,
660
+ isUsable: false,
661
+ isStorable: true,
662
+ isItemContainer: false,
663
+ isSolid: false,
664
+ requiredAmmoKeys: [],
665
+ isTwoHanded: false,
666
+ hasUseWith: false,
667
+ entityEffects: [],
668
+ entityEffectChance: 0,
669
+ _id: '99999-stack-test',
670
+ key: 'apple',
671
+ texturePath: 'foods/apple.png',
672
+ textureKey: 'apple',
673
+ isEquipable: false,
674
+ isStackable: true,
675
+ name: 'Apple',
676
+ description: 'A red apple.',
677
+ weight: 0.05,
678
+ stackQty: 99999,
679
+ attack: 0,
680
+ defense: 0,
681
+ fullDescription: '',
682
+ usableEffectDescription: 'Regenerates 10 HP and Mana 5 times',
683
+ },
653
684
  ];
654
685
 
655
686
  export const itemContainerMock = (
@@ -0,0 +1,145 @@
1
+ import { IMarketplaceBlueprintSearchRequest, IMarketplaceBlueprintSummary } from '@rpg-engine/shared';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import React from 'react';
4
+ import { BlueprintSearchModal } from '../../../components/Marketplace/BlueprintSearchModal';
5
+ import { RPGUIRoot } from '../../../components/RPGUI/RPGUIRoot';
6
+ import itemsAtlasJSON from '../../../mocks/atlas/items/items.json';
7
+ import itemsAtlasIMG from '../../../mocks/atlas/items/items.png';
8
+
9
+ const meta = {
10
+ title: 'Features/Marketplace/BlueprintSearchModal',
11
+ component: BlueprintSearchModal,
12
+ parameters: {
13
+ layout: 'centered',
14
+ },
15
+ decorators: [
16
+ (Story) => (
17
+ <RPGUIRoot>
18
+ <div style={{ width: '800px', height: '600px', position: 'relative' }}>
19
+ <Story />
20
+ </div>
21
+ </RPGUIRoot>
22
+ ),
23
+ ],
24
+ } satisfies Meta<typeof BlueprintSearchModal>;
25
+
26
+ export default meta;
27
+ type Story = StoryObj<typeof BlueprintSearchModal>;
28
+
29
+ const mockBlueprints: IMarketplaceBlueprintSummary[] = [
30
+ {
31
+ key: 'items/broad-sword',
32
+ name: 'Broad Sword',
33
+ type: 'Weapon',
34
+ subType: 'Sword',
35
+ tier: 1,
36
+ textureAtlas: 'items',
37
+ texturePath: 'swords/broad-sword.png',
38
+ },
39
+ {
40
+ key: 'items/angelic-sword',
41
+ name: 'Angelic Sword',
42
+ type: 'Weapon',
43
+ subType: 'Sword',
44
+ tier: 5,
45
+ textureAtlas: 'items',
46
+ texturePath: 'swords/angelic-sword.png',
47
+ rarity: 'Epic',
48
+ },
49
+ {
50
+ key: 'items/leather-armor',
51
+ name: 'Leather Armor',
52
+ type: 'Armor',
53
+ subType: 'Body',
54
+ tier: 1,
55
+ textureAtlas: 'items',
56
+ texturePath: 'armors/leather-armor.png',
57
+ },
58
+ {
59
+ key: 'items/wooden-staff',
60
+ name: 'Wooden Staff',
61
+ type: 'Weapon',
62
+ subType: 'Staff',
63
+ tier: 1,
64
+ textureAtlas: 'items',
65
+ texturePath: 'staffs/wooden-staff.png',
66
+ rarity: 'Rare',
67
+ },
68
+ {
69
+ key: 'items/wooden-shield',
70
+ name: 'Wooden Shield',
71
+ type: 'Armor',
72
+ subType: 'Shield',
73
+ tier: 1,
74
+ textureAtlas: 'items',
75
+ texturePath: 'shields/wooden-shield.png',
76
+ },
77
+ ];
78
+
79
+ export const Default: Story = {
80
+ render: () => (
81
+ <BlueprintSearchModal
82
+ isOpen
83
+ onClose={() => console.log('close')}
84
+ onSelect={(blueprint) => console.log('selected:', blueprint.name)}
85
+ onSearch={(request: IMarketplaceBlueprintSearchRequest) => console.log('search:', request)}
86
+ blueprints={mockBlueprints}
87
+ totalCount={mockBlueprints.length}
88
+ currentPage={1}
89
+ isLoading={false}
90
+ atlasJSON={itemsAtlasJSON}
91
+ atlasIMG={itemsAtlasIMG}
92
+ />
93
+ ),
94
+ };
95
+
96
+ export const LoadingState: Story = {
97
+ render: () => (
98
+ <BlueprintSearchModal
99
+ isOpen
100
+ onClose={() => console.log('close')}
101
+ onSelect={(blueprint) => console.log('selected:', blueprint.name)}
102
+ onSearch={(request: IMarketplaceBlueprintSearchRequest) => console.log('search:', request)}
103
+ blueprints={[]}
104
+ totalCount={0}
105
+ currentPage={1}
106
+ isLoading
107
+ atlasJSON={itemsAtlasJSON}
108
+ atlasIMG={itemsAtlasIMG}
109
+ />
110
+ ),
111
+ };
112
+
113
+ export const EmptyResults: Story = {
114
+ render: () => (
115
+ <BlueprintSearchModal
116
+ isOpen
117
+ onClose={() => console.log('close')}
118
+ onSelect={(blueprint) => console.log('selected:', blueprint.name)}
119
+ onSearch={(request: IMarketplaceBlueprintSearchRequest) => console.log('search:', request)}
120
+ blueprints={[]}
121
+ totalCount={0}
122
+ currentPage={1}
123
+ isLoading={false}
124
+ atlasJSON={itemsAtlasJSON}
125
+ atlasIMG={itemsAtlasIMG}
126
+ />
127
+ ),
128
+ };
129
+
130
+ export const Closed: Story = {
131
+ render: () => (
132
+ <BlueprintSearchModal
133
+ isOpen={false}
134
+ onClose={() => console.log('close')}
135
+ onSelect={(blueprint) => console.log('selected:', blueprint.name)}
136
+ onSearch={(request: IMarketplaceBlueprintSearchRequest) => console.log('search:', request)}
137
+ blueprints={mockBlueprints}
138
+ totalCount={mockBlueprints.length}
139
+ currentPage={1}
140
+ isLoading={false}
141
+ atlasJSON={itemsAtlasJSON}
142
+ atlasIMG={itemsAtlasIMG}
143
+ />
144
+ ),
145
+ };
@@ -0,0 +1,323 @@
1
+ import {
2
+ IMarketplaceBlueprintSearchRequest,
3
+ IMarketplaceBlueprintSummary,
4
+ IMarketplaceBuyOrderItem,
5
+ MarketplaceBuyOrderStatus,
6
+ } from '@rpg-engine/shared';
7
+ import type { Meta, StoryObj } from '@storybook/react';
8
+ import React, { useState } from 'react';
9
+ import { BlueprintSearchModal } from '../../../components/Marketplace/BlueprintSearchModal';
10
+ import { BuyOrderPanel } from '../../../components/Marketplace/BuyOrderPanel';
11
+ import { RPGUIRoot } from '../../../components/RPGUI/RPGUIRoot';
12
+ import atlasJSON from '../../../mocks/atlas/items/items.json';
13
+ import atlasIMG from '../../../mocks/atlas/items/items.png';
14
+
15
+ const meta = {
16
+ title: 'Features/Marketplace/BuyOrderPanel',
17
+ component: BuyOrderPanel,
18
+ parameters: {
19
+ layout: 'centered',
20
+ },
21
+ decorators: [
22
+ (Story) => (
23
+ <RPGUIRoot>
24
+ <div style={{ width: '800px' }}>
25
+ <Story />
26
+ </div>
27
+ </RPGUIRoot>
28
+ ),
29
+ ],
30
+ } satisfies Meta<typeof BuyOrderPanel>;
31
+
32
+ export default meta;
33
+ type Story = StoryObj<typeof BuyOrderPanel>;
34
+
35
+ const now = new Date();
36
+ const weeksAgo = (weeks: number): Date =>
37
+ new Date(now.getTime() - weeks * 7 * 24 * 60 * 60 * 1000);
38
+
39
+ const mockSelectedBlueprint: IMarketplaceBlueprintSummary = {
40
+ key: 'items/broad-sword',
41
+ name: 'Broad Sword',
42
+ type: 'Weapon',
43
+ subType: 'Sword',
44
+ tier: 1,
45
+ textureAtlas: 'items',
46
+ texturePath: 'swords/broad-sword.png',
47
+ };
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
+
79
+ const mockBuyOrders: IMarketplaceBuyOrderItem[] = [
80
+ {
81
+ _id: 'order-1',
82
+ owner: 'character-1',
83
+ itemBlueprintKey: 'items/broad-sword',
84
+ itemRarity: 'Rare',
85
+ maxPrice: 500,
86
+ escrowedGold: 500,
87
+ fee: 25,
88
+ status: MarketplaceBuyOrderStatus.Active,
89
+ createdAt: weeksAgo(1),
90
+ updatedAt: weeksAgo(1),
91
+ },
92
+ {
93
+ _id: 'order-2',
94
+ owner: 'character-1',
95
+ itemBlueprintKey: 'swords/angelic-sword',
96
+ maxPrice: 2000,
97
+ escrowedGold: 2000,
98
+ fee: 100,
99
+ status: MarketplaceBuyOrderStatus.Fulfilled,
100
+ fulfilledBy: 'AnotherPlayer',
101
+ createdAt: weeksAgo(3),
102
+ updatedAt: weeksAgo(0),
103
+ },
104
+ {
105
+ _id: 'order-3',
106
+ owner: 'character-1',
107
+ itemBlueprintKey: 'armors/leather-armor',
108
+ maxPrice: 300,
109
+ escrowedGold: 0,
110
+ fee: 15,
111
+ status: MarketplaceBuyOrderStatus.Expired,
112
+ createdAt: weeksAgo(5),
113
+ updatedAt: weeksAgo(1),
114
+ },
115
+ ];
116
+
117
+ const DefaultWrapper = () => {
118
+ const [selectedBlueprint, setSelectedBlueprint] = useState<IMarketplaceBlueprintSummary | undefined>();
119
+ const [isBlueprintSearchOpen, setIsBlueprintSearchOpen] = useState(false);
120
+ const [quantity, setQuantity] = useState(0);
121
+ const [maxPrice, setMaxPrice] = useState(0);
122
+ const [rarity, setRarity] = useState('');
123
+ const [yourOrders, setYourOrders] = useState<IMarketplaceBuyOrderItem[]>(mockBuyOrders);
124
+
125
+ const handleBlueprintSelect = (blueprint: IMarketplaceBlueprintSummary) => {
126
+ setIsBlueprintSearchOpen(false);
127
+ setSelectedBlueprint(blueprint);
128
+ };
129
+
130
+ return (
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
+ </>
185
+ );
186
+ };
187
+
188
+ const WithBlueprintWrapper = () => {
189
+ const [selectedBlueprint, setSelectedBlueprint] = useState<IMarketplaceBlueprintSummary | undefined>(mockSelectedBlueprint);
190
+ const [isBlueprintSearchOpen, setIsBlueprintSearchOpen] = useState(false);
191
+ const [quantity, setQuantity] = useState(0);
192
+ const [maxPrice, setMaxPrice] = useState(0);
193
+ const [rarity, setRarity] = useState('');
194
+ const [yourOrders, setYourOrders] = useState<IMarketplaceBuyOrderItem[]>(mockBuyOrders);
195
+
196
+ const handleBlueprintSelect = (blueprint: IMarketplaceBlueprintSummary) => {
197
+ setIsBlueprintSearchOpen(false);
198
+ setSelectedBlueprint(blueprint);
199
+ };
200
+
201
+ return (
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
+ </>
256
+ );
257
+ };
258
+
259
+ const EmptyWrapper = () => {
260
+ const [isBlueprintSearchOpen, setIsBlueprintSearchOpen] = useState(false);
261
+ const [selectedBlueprint, setSelectedBlueprint] = useState<IMarketplaceBlueprintSummary | undefined>();
262
+ const [quantity, setQuantity] = useState(0);
263
+ const [maxPrice, setMaxPrice] = useState(0);
264
+ const [rarity, setRarity] = useState('');
265
+
266
+ const handleBlueprintSelect = (blueprint: IMarketplaceBlueprintSummary) => {
267
+ setIsBlueprintSearchOpen(false);
268
+ setSelectedBlueprint(blueprint);
269
+ };
270
+
271
+ return (
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
+ </>
310
+ );
311
+ };
312
+
313
+ export const Default: Story = {
314
+ render: () => <DefaultWrapper />,
315
+ };
316
+
317
+ export const WithSelectedBlueprint: Story = {
318
+ render: () => <WithBlueprintWrapper />,
319
+ };
320
+
321
+ export const EmptyOrders: Story = {
322
+ render: () => <EmptyWrapper />,
323
+ };
@@ -0,0 +1,116 @@
1
+ import { IMarketplaceBuyOrderItem, MarketplaceBuyOrderStatus } from '@rpg-engine/shared';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import React from 'react';
4
+ import { BuyOrderRow } from '../../../components/Marketplace/BuyOrderRows';
5
+ import { RPGUIRoot } from '../../../components/RPGUI/RPGUIRoot';
6
+ import atlasJSON from '../../../mocks/atlas/items/items.json';
7
+ import atlasIMG from '../../../mocks/atlas/items/items.png';
8
+
9
+ const meta = {
10
+ title: 'Features/Marketplace/BuyOrderRows',
11
+ component: BuyOrderRow,
12
+ parameters: {
13
+ layout: 'centered',
14
+ },
15
+ decorators: [
16
+ (Story) => (
17
+ <RPGUIRoot>
18
+ <div style={{ width: '800px' }}>
19
+ <Story />
20
+ </div>
21
+ </RPGUIRoot>
22
+ ),
23
+ ],
24
+ } satisfies Meta<typeof BuyOrderRow>;
25
+
26
+ export default meta;
27
+ type Story = StoryObj<typeof BuyOrderRow>;
28
+
29
+ const now = new Date();
30
+ const weeksAgo = (weeks: number): Date =>
31
+ new Date(now.getTime() - weeks * 7 * 24 * 60 * 60 * 1000);
32
+
33
+ const mockActiveBuyOrder: IMarketplaceBuyOrderItem = {
34
+ _id: 'order-1',
35
+ owner: 'character-1',
36
+ itemBlueprintKey: 'items/broad-sword',
37
+ itemRarity: 'Rare',
38
+ maxPrice: 500,
39
+ escrowedGold: 500,
40
+ fee: 25,
41
+ status: MarketplaceBuyOrderStatus.Active,
42
+ createdAt: weeksAgo(1),
43
+ updatedAt: weeksAgo(1),
44
+ };
45
+
46
+ const mockFulfilledBuyOrder: IMarketplaceBuyOrderItem = {
47
+ _id: 'order-2',
48
+ owner: 'character-1',
49
+ itemBlueprintKey: 'items/angelic-sword',
50
+ maxPrice: 2000,
51
+ escrowedGold: 2000,
52
+ fee: 100,
53
+ status: MarketplaceBuyOrderStatus.Fulfilled,
54
+ fulfilledBy: 'AnotherPlayer',
55
+ createdAt: weeksAgo(3),
56
+ updatedAt: weeksAgo(0),
57
+ };
58
+
59
+ const mockExpiredBuyOrder: IMarketplaceBuyOrderItem = {
60
+ _id: 'order-3',
61
+ owner: 'character-1',
62
+ itemBlueprintKey: 'items/leather-armor',
63
+ maxPrice: 300,
64
+ escrowedGold: 0,
65
+ fee: 15,
66
+ status: MarketplaceBuyOrderStatus.Expired,
67
+ createdAt: weeksAgo(5),
68
+ updatedAt: weeksAgo(1),
69
+ };
70
+
71
+ export const ActiveOrder: Story = {
72
+ render: () => (
73
+ <BuyOrderRow
74
+ buyOrder={mockActiveBuyOrder}
75
+ atlasJSON={atlasJSON}
76
+ atlasIMG={atlasIMG}
77
+ isOwn
78
+ onCancel={(id) => console.log('cancel order:', id)}
79
+ />
80
+ ),
81
+ };
82
+
83
+ export const FulfilledOrder: Story = {
84
+ render: () => (
85
+ <BuyOrderRow
86
+ buyOrder={mockFulfilledBuyOrder}
87
+ atlasJSON={atlasJSON}
88
+ atlasIMG={atlasIMG}
89
+ isOwn
90
+ />
91
+ ),
92
+ };
93
+
94
+ export const ExpiredOrder: Story = {
95
+ render: () => (
96
+ <BuyOrderRow
97
+ buyOrder={mockExpiredBuyOrder}
98
+ atlasJSON={atlasJSON}
99
+ atlasIMG={atlasIMG}
100
+ isOwn={false}
101
+ />
102
+ ),
103
+ };
104
+
105
+ export const OpenOrderForSellers: Story = {
106
+ render: () => (
107
+ <BuyOrderRow
108
+ buyOrder={mockActiveBuyOrder}
109
+ atlasJSON={atlasJSON}
110
+ atlasIMG={atlasIMG}
111
+ isOwn={false}
112
+ onFulfill={(id) => console.log('fulfill order:', id)}
113
+ showRequestTag
114
+ />
115
+ ),
116
+ };