@rpg-engine/long-bow 0.8.203 → 0.8.205

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 (43) hide show
  1. package/dist/components/Item/Inventory/ErrorBoundary.d.ts +2 -0
  2. package/dist/components/Marketplace/BuyOrderPanel.d.ts +2 -2
  3. package/dist/components/Marketplace/BuyOrderRows.d.ts +4 -4
  4. package/dist/components/Marketplace/HistoryPanel.d.ts +2 -2
  5. package/dist/components/Marketplace/MarketplaceRows.d.ts +2 -0
  6. package/dist/components/shared/MMORPGNumber.d.ts +6 -0
  7. package/dist/long-bow.cjs.development.js +465 -34317
  8. package/dist/long-bow.cjs.development.js.map +1 -1
  9. package/dist/long-bow.cjs.production.min.js +1 -1
  10. package/dist/long-bow.cjs.production.min.js.map +1 -1
  11. package/dist/long-bow.esm.js +466 -34318
  12. package/dist/long-bow.esm.js.map +1 -1
  13. package/dist/utils/numberUtils.d.ts +7 -0
  14. package/package.json +1 -1
  15. package/src/components/DCWallet/DCWalletContent.tsx +5 -3
  16. package/src/components/Item/Inventory/ErrorBoundary.tsx +25 -8
  17. package/src/components/Item/Inventory/ItemPropertyColorSelector.tsx +3 -1
  18. package/src/components/Marketplace/BuyOrderPanel.tsx +2 -2
  19. package/src/components/Marketplace/BuyOrderRows.tsx +13 -7
  20. package/src/components/Marketplace/CharacterDetailModal.tsx +99 -17
  21. package/src/components/Marketplace/CharacterListingModal.tsx +2 -1
  22. package/src/components/Marketplace/GroupedRowContainer.tsx +10 -10
  23. package/src/components/Marketplace/HistoryPanel.tsx +5 -5
  24. package/src/components/Marketplace/MarketplaceBuyModal.tsx +4 -4
  25. package/src/components/Marketplace/MarketplaceRows.tsx +13 -7
  26. package/src/components/Marketplace/__test__/CharacterDetailModal.spec.tsx +137 -0
  27. package/src/components/Store/CartView.tsx +2 -1
  28. package/src/components/Store/FeaturedBanner.tsx +1 -0
  29. package/src/components/Store/PaymentMethodModal.tsx +11 -3
  30. package/src/components/Store/StoreCharacterSkinRow.tsx +9 -3
  31. package/src/components/Store/StoreItemDetails.tsx +9 -1
  32. package/src/components/Store/StoreItemRow.tsx +13 -3
  33. package/src/components/Store/sections/StorePacksSection.tsx +7 -0
  34. package/src/components/Store/sections/StoreRedeemSection.tsx +2 -1
  35. package/src/components/shared/DCRateStrip.tsx +3 -2
  36. package/src/components/shared/MMORPGNumber.tsx +22 -0
  37. package/src/stories/Features/marketplace/CharacterDetailModal.stories.tsx +69 -15
  38. package/src/stories/Features/marketplace/CharacterMarketplace.stories.tsx +105 -11
  39. package/src/stories/Features/store/Store.stories.tsx +125 -35
  40. package/src/stories/Features/trading/Marketplace.stories.tsx +8 -4
  41. package/src/utils/__test__/atlasUtils.spec.ts +15 -0
  42. package/src/utils/atlasUtils.ts +78 -0
  43. package/src/utils/numberUtils.ts +31 -0
@@ -0,0 +1,137 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+ // @ts-nocheck
5
+ import { ICharacterListing } from '@rpg-engine/shared';
6
+ import React from 'react';
7
+ import ReactDOM from 'react-dom';
8
+ import { act } from 'react-dom/test-utils';
9
+ import { CharacterDetailModal } from '../CharacterDetailModal';
10
+
11
+ jest.mock('../../Abstractions/ModalPortal', () => ({
12
+ __esModule: true,
13
+ default: ({ children }) => <>{children}</>,
14
+ }));
15
+
16
+ jest.mock('../../ConfirmModal', () => ({
17
+ ConfirmModal: ({ message }) => (
18
+ <div data-testid="confirm-modal">{message}</div>
19
+ ),
20
+ }));
21
+
22
+ jest.mock('../../shared/CTAButton/CTAButton', () => ({
23
+ CTAButton: ({ label, onClick, disabled }) => (
24
+ <button disabled={disabled} onClick={onClick}>
25
+ {label}
26
+ </button>
27
+ ),
28
+ }));
29
+
30
+ jest.mock('../../shared/SpriteFromAtlas', () => ({
31
+ SpriteFromAtlas: ({ spriteKey }) => (
32
+ <div data-testid="sprite-from-atlas" data-sprite-key={spriteKey} />
33
+ ),
34
+ }));
35
+
36
+ jest.mock('pixelarticons/react/ShoppingBag', () => ({
37
+ ShoppingBag: () => <svg data-testid="shopping-bag-icon" />,
38
+ }));
39
+
40
+ jest.mock('react-icons/fa', () => ({
41
+ FaTimes: () => <svg data-testid="close-icon" />,
42
+ }));
43
+
44
+ describe('CharacterDetailModal', () => {
45
+ let container;
46
+
47
+ const listing: ICharacterListing = {
48
+ _id: 'listing-1',
49
+ character: 'character-1',
50
+ seller: 'seller-1',
51
+ listedByCharacterName: 'TraderJoe',
52
+ price: 42000,
53
+ isBeingBought: false,
54
+ characterSnapshot: {
55
+ name: 'Sir Galahad',
56
+ level: 25,
57
+ class: 'Warrior',
58
+ race: 'Human',
59
+ faction: 'Alliance',
60
+ mode: 'Standard',
61
+ skills: { level: 25, sword: 10, shielding: 8 },
62
+ equipment: [
63
+ {
64
+ slot: 'rightHand',
65
+ itemName: 'Broad Sword',
66
+ itemKey: 'broad-sword',
67
+ rarity: 'Common',
68
+ },
69
+ {
70
+ slot: 'head',
71
+ itemName: 'Iron Helmet',
72
+ itemKey: 'iron-helmet',
73
+ rarity: 'Rare',
74
+ },
75
+ {
76
+ slot: 'armor',
77
+ itemName: 'Jacket',
78
+ itemKey: 'jacket',
79
+ rarity: 'Uncommon',
80
+ },
81
+ ],
82
+ textureKey: 'black-knight',
83
+ },
84
+ createdAt: new Date(),
85
+ updatedAt: new Date(),
86
+ };
87
+
88
+ beforeEach(() => {
89
+ container = document.createElement('div');
90
+ document.body.appendChild(container);
91
+ });
92
+
93
+ afterEach(() => {
94
+ ReactDOM.unmountComponentAtNode(container);
95
+ container.remove();
96
+ jest.clearAllMocks();
97
+ });
98
+
99
+ it('renders equipment rows from the API snapshot and requests atlas sprites for each item', () => {
100
+ act(() => {
101
+ ReactDOM.render(
102
+ <CharacterDetailModal
103
+ listing={listing}
104
+ isOpen
105
+ onClose={jest.fn()}
106
+ onBuy={jest.fn()}
107
+ atlasJSON={{}}
108
+ atlasIMG=""
109
+ characterAtlasJSON={{}}
110
+ characterAtlasIMG=""
111
+ />,
112
+ container
113
+ );
114
+ });
115
+
116
+ expect(container.textContent).toContain('Broad Sword');
117
+ expect(container.textContent).toContain('Iron Helmet');
118
+ expect(container.textContent).toContain('Jacket');
119
+ expect(container.textContent).toContain('Right Hand');
120
+ expect(container.textContent).toContain('Head');
121
+ expect(container.textContent).toContain('Armor');
122
+
123
+ const spriteKeys = Array.from(
124
+ container.querySelectorAll('[data-testid="sprite-from-atlas"]')
125
+ ).map((node) => node.getAttribute('data-sprite-key'));
126
+
127
+ expect(spriteKeys).toEqual(
128
+ expect.arrayContaining([
129
+ 'black-knight/down/standing/0.png',
130
+ 'broad-sword',
131
+ 'iron-helmet',
132
+ 'jacket',
133
+ 'others/definya-coin.png',
134
+ ])
135
+ );
136
+ });
137
+ });
@@ -8,6 +8,7 @@ import { CTAButton } from '../shared/CTAButton/CTAButton';
8
8
  import { SpriteFromAtlas } from '../shared/SpriteFromAtlas';
9
9
  import { ITrustSignal, TrustBar } from './TrustBar';
10
10
  import { PurchaseSuccess } from './PurchaseSuccess';
11
+ import { MMORPGNumber } from '../../components/shared/MMORPGNumber';
11
12
 
12
13
  // Local cart item interface
13
14
  interface ICartItem {
@@ -228,7 +229,7 @@ export const CartView: React.FC<ICartViewProps> = ({
228
229
  {dcTotal > 0 && (
229
230
  <TotalRow>
230
231
  <span>DC:</span>
231
- <span>{dcTotal.toLocaleString()} DC</span>
232
+ <span><MMORPGNumber value={dcTotal} /> DC</span>
232
233
  </TotalRow>
233
234
  )}
234
235
  <TotalRow $isTotal>
@@ -267,4 +267,5 @@ const CardActions = styled.div`
267
267
  display: flex;
268
268
  gap: 0.4rem;
269
269
  justify-content: flex-end;
270
+ flex-shrink: 0;
270
271
  `;
@@ -3,6 +3,7 @@ import { FaTimes } from 'react-icons/fa';
3
3
  import styled from 'styled-components';
4
4
  import ModalPortal from '../Abstractions/ModalPortal';
5
5
  import { Button, ButtonTypes } from '../Button';
6
+ import { MMORPGNumber } from '../../components/shared/MMORPGNumber';
6
7
 
7
8
  export interface IPaymentMethodModalProps {
8
9
  dcBalance: number;
@@ -53,9 +54,16 @@ export const PaymentMethodModal: React.FC<IPaymentMethodModalProps> = ({
53
54
  }, [selected, dcDisabled, onPayWithDC, onPayWithCard, onPayWithPix]);
54
55
 
55
56
  const dcSubText =
56
- dcRequired !== undefined
57
- ? `${dcBalance.toLocaleString()} DC available · ${dcRequired.toLocaleString()} DC needed`
58
- : `${dcBalance.toLocaleString()} DC available`;
57
+ dcRequired !== undefined ? (
58
+ <>
59
+ <MMORPGNumber value={dcBalance} /> DC available ·{' '}
60
+ <MMORPGNumber value={dcRequired} /> DC needed
61
+ </>
62
+ ) : (
63
+ <>
64
+ <MMORPGNumber value={dcBalance} /> DC available
65
+ </>
66
+ );
59
67
 
60
68
  return (
61
69
  <ModalPortal>
@@ -10,6 +10,7 @@ import { SelectArrow } from '../Arrow/SelectArrow';
10
10
  import { ICharacterProps } from '../Character/CharacterSelection';
11
11
  import { CTAButton } from '../shared/CTAButton/CTAButton';
12
12
  import { SpriteFromAtlas } from '../shared/SpriteFromAtlas';
13
+ import { MMORPGNumber } from '../../components/shared/MMORPGNumber';
13
14
  import { SimpleTooltip } from '../shared/SimpleTooltip';
14
15
  import { useCharacterSkinNavigation } from '../../hooks/useCharacterSkinNavigation';
15
16
 
@@ -128,7 +129,7 @@ export const StoreCharacterSkinRow: React.FC<IStoreCharacterSkinRowProps> = ({
128
129
  <DCCoinWrapper $scale={0.8}>
129
130
  <SpriteFromAtlas atlasIMG={atlasIMG} atlasJSON={atlasJSON} spriteKey="others/definya-coin.png" imgScale={0.8} />
130
131
  </DCCoinWrapper>
131
- {originalPrice.toLocaleString()}
132
+ <MMORPGNumber value={originalPrice} />
132
133
  </>
133
134
  ) : `$${originalPrice.toFixed(2)}`}
134
135
  </OriginalPrice>
@@ -140,7 +141,7 @@ export const StoreCharacterSkinRow: React.FC<IStoreCharacterSkinRowProps> = ({
140
141
  <SpriteFromAtlas atlasIMG={atlasIMG} atlasJSON={atlasJSON} spriteKey="others/definya-coin.png" imgScale={1} />
141
142
  </SimpleTooltip>
142
143
  </DCCoinWrapper>
143
- {item.price.toLocaleString()}
144
+ <MMORPGNumber value={item.price} />
144
145
  </ItemPrice>
145
146
  ) : (
146
147
  <ItemPrice $onSale={originalPrice != null} style={{ display: 'flex', alignItems: 'center', gap: '2px' }}>
@@ -153,7 +154,7 @@ export const StoreCharacterSkinRow: React.FC<IStoreCharacterSkinRowProps> = ({
153
154
  <SpriteFromAtlas atlasIMG={atlasIMG} atlasJSON={atlasJSON} spriteKey="others/definya-coin.png" imgScale={0.9} />
154
155
  </SimpleTooltip>
155
156
  </DCCoinWrapper>
156
- {((item as any).dcPrice as number).toLocaleString()}
157
+ <MMORPGNumber value={(item as any).dcPrice as number} />
157
158
  </>
158
159
  ) : ''}
159
160
  </ItemPrice>
@@ -226,6 +227,7 @@ const ItemDetails = styled.div`
226
227
  display: flex;
227
228
  flex-direction: column;
228
229
  gap: 0.35rem;
230
+ min-width: 0;
229
231
  `;
230
232
 
231
233
  const ItemName = styled.div`
@@ -233,6 +235,9 @@ const ItemName = styled.div`
233
235
  font-size: 0.8125rem;
234
236
  color: #ffffff;
235
237
  text-shadow: 1px 1px 0 rgba(0,0,0,0.5);
238
+ white-space: nowrap;
239
+ overflow: hidden;
240
+ text-overflow: ellipsis;
236
241
  `;
237
242
 
238
243
  const PriceRow = styled.div`
@@ -268,6 +273,7 @@ const Controls = styled.div`
268
273
  align-items: center;
269
274
  gap: 0.5rem;
270
275
  min-width: fit-content;
276
+ flex-shrink: 0;
271
277
  `;
272
278
 
273
279
  const DCCoinWrapper = styled.span<{ $scale?: number }>`
@@ -3,6 +3,7 @@ import React from 'react';
3
3
  import { FaArrowLeft, FaCartPlus } from 'react-icons/fa';
4
4
  import styled from 'styled-components';
5
5
  import { CTAButton } from '../shared/CTAButton/CTAButton';
6
+ import { MMORPGNumber } from '../../components/shared/MMORPGNumber';
6
7
 
7
8
  interface IStoreItemDetailsProps {
8
9
  item: IProductBlueprint | (IItemPack & { name: string; texturePath: string });
@@ -46,7 +47,14 @@ export const StoreItemDetails: React.FC<IStoreItemDetailsProps> = ({
46
47
  <ItemName>{item.name}</ItemName>
47
48
  <ItemPrice>
48
49
  {currencySymbol}{'priceUSD' in item ? item.priceUSD : ((item as any).regionalPrice ?? item.price)}
49
- {(item as any).dcPrice ? ` · ${((item as any).dcPrice as number).toLocaleString()} DC` : ''}
50
+ {(item as any).dcPrice ? (
51
+ <>
52
+ {' '}
53
+ · <MMORPGNumber value={(item as any).dcPrice as number} /> DC
54
+ </>
55
+ ) : (
56
+ ''
57
+ )}
50
58
  </ItemPrice>
51
59
  <Description>{item.description}</Description>
52
60
  </ItemInfo>
@@ -9,6 +9,7 @@ import { SpriteFromAtlas } from '../shared/SpriteFromAtlas';
9
9
  import { SimpleTooltip } from '../shared/SimpleTooltip';
10
10
  import { useQuantityControl } from '../../hooks/useQuantityControl';
11
11
  import { IStoreBadge, StoreBadges } from './StoreBadges';
12
+ import { MMORPGNumber } from '../../components/shared/MMORPGNumber';
12
13
 
13
14
  interface IStoreItemRowProps {
14
15
  item: IProductBlueprint;
@@ -118,7 +119,7 @@ export const StoreItemRow: React.FC<IStoreItemRowProps> = ({
118
119
  <DCCoinWrapper $scale={0.8}>
119
120
  <SpriteFromAtlas atlasIMG={atlasIMG} atlasJSON={atlasJSON} spriteKey="others/definya-coin.png" imgScale={0.8} />
120
121
  </DCCoinWrapper>
121
- {originalPrice.toLocaleString()}
122
+ <MMORPGNumber value={originalPrice} />
122
123
  </>
123
124
  ) : `${currencySymbol}${originalPrice.toFixed(2)}`}
124
125
  </OriginalPrice>
@@ -130,7 +131,7 @@ export const StoreItemRow: React.FC<IStoreItemRowProps> = ({
130
131
  <SpriteFromAtlas atlasIMG={atlasIMG} atlasJSON={atlasJSON} spriteKey="others/definya-coin.png" imgScale={1} />
131
132
  </SimpleTooltip>
132
133
  </DCCoinWrapper>
133
- {item.price.toLocaleString()}
134
+ <MMORPGNumber value={item.price} />
134
135
  </ItemPrice>
135
136
  ) : (
136
137
  <ItemPrice $onSale={originalPrice != null} style={{ display: 'flex', alignItems: 'center', gap: '2px' }}>
@@ -143,7 +144,7 @@ export const StoreItemRow: React.FC<IStoreItemRowProps> = ({
143
144
  <SpriteFromAtlas atlasIMG={atlasIMG} atlasJSON={atlasJSON} spriteKey="others/definya-coin.png" imgScale={0.9} />
144
145
  </SimpleTooltip>
145
146
  </DCCoinWrapper>
146
- {((item as any).dcPrice as number).toLocaleString()}
147
+ <MMORPGNumber value={(item as any).dcPrice as number} />
147
148
  </>
148
149
  ) : ''}
149
150
  </ItemPrice>
@@ -244,6 +245,7 @@ const ItemDetails = styled.div`
244
245
  display: flex;
245
246
  flex-direction: column;
246
247
  gap: 0.35rem;
248
+ min-width: 0;
247
249
  `;
248
250
 
249
251
  const ItemName = styled.div`
@@ -251,6 +253,9 @@ const ItemName = styled.div`
251
253
  font-size: 0.8125rem;
252
254
  color: #ffffff;
253
255
  text-shadow: 1px 1px 0 rgba(0,0,0,0.5);
256
+ white-space: nowrap;
257
+ overflow: hidden;
258
+ text-overflow: ellipsis;
254
259
  `;
255
260
 
256
261
  const PriceRow = styled.div`
@@ -281,6 +286,10 @@ const ItemDescription = styled.div`
281
286
  color: rgba(255, 255, 255, 0.85);
282
287
  line-height: 1.4;
283
288
  margin-top: 2px;
289
+ display: -webkit-box;
290
+ -webkit-line-clamp: 2;
291
+ -webkit-box-orient: vertical;
292
+ overflow: hidden;
284
293
  `;
285
294
 
286
295
  const DCCoinWrapper = styled.span<{ $scale?: number }>`
@@ -298,6 +307,7 @@ const Controls = styled.div`
298
307
  align-items: center;
299
308
  gap: 0.75rem;
300
309
  min-width: fit-content;
310
+ flex-shrink: 0;
301
311
  `;
302
312
 
303
313
  const ArrowsContainer = styled.div`
@@ -214,6 +214,9 @@ const PackName = styled.div`
214
214
  color: #ffffff;
215
215
  text-shadow: 1px 1px 0 rgba(0,0,0,0.5);
216
216
  letter-spacing: 0.5px;
217
+ white-space: nowrap;
218
+ overflow: hidden;
219
+ text-overflow: ellipsis;
217
220
  `;
218
221
 
219
222
  const PackPriceRow = styled.div`
@@ -242,6 +245,10 @@ const PackDescription = styled.div`
242
245
  color: rgba(255, 255, 255, 0.85);
243
246
  line-height: 1.5;
244
247
  margin-top: 2px;
248
+ display: -webkit-box;
249
+ -webkit-line-clamp: 2;
250
+ -webkit-box-orient: vertical;
251
+ overflow: hidden;
245
252
  `;
246
253
 
247
254
  const Controls = styled.div`
@@ -3,6 +3,7 @@ import { FaCheckCircle, FaExclamationCircle, FaTicketAlt } from 'react-icons/fa'
3
3
  import styled, { keyframes } from 'styled-components';
4
4
  import { uiColors } from '../../../constants/uiColors';
5
5
  import { CTAButton } from '../../shared/CTAButton/CTAButton';
6
+ import { MMORPGNumber } from '../../../components/shared/MMORPGNumber';
6
7
 
7
8
  type RedeemStatus = 'idle' | 'loading' | 'success' | 'error';
8
9
 
@@ -68,7 +69,7 @@ export const StoreRedeemSection: React.FC<IStoreRedeemSectionProps> = ({
68
69
  </SuccessIcon>
69
70
  <SuccessTitle>Code Redeemed!</SuccessTitle>
70
71
  {dcAmount != null && (
71
- <DCAmountDisplay>+{dcAmount.toLocaleString()} DC</DCAmountDisplay>
72
+ <DCAmountDisplay>+<MMORPGNumber value={dcAmount} /> DC</DCAmountDisplay>
72
73
  )}
73
74
  <SuccessHint>Your wallet balance has been updated.</SuccessHint>
74
75
  <ButtonWrapper>
@@ -1,6 +1,7 @@
1
1
  import { DC_TO_GOLD_SWAP_RATE } from '@rpg-engine/shared';
2
2
  import React from 'react';
3
3
  import styled from 'styled-components';
4
+ import { MMORPGNumber } from '../../components/shared/MMORPGNumber';
4
5
 
5
6
  /** 100 DC = $1 USD */
6
7
  const DC_TO_USD = 100;
@@ -10,7 +11,7 @@ export const DCRateStrip: React.FC = () => (
10
11
  <RateItem>
11
12
  <RateNum>1 DC</RateNum>
12
13
  <RateSep>=</RateSep>
13
- <RateVal>{DC_TO_GOLD_SWAP_RATE.toLocaleString()} Gold</RateVal>
14
+ <RateVal><MMORPGNumber value={DC_TO_GOLD_SWAP_RATE} /> Gold</RateVal>
14
15
  </RateItem>
15
16
  <RateDivider />
16
17
  <RateItem>
@@ -22,7 +23,7 @@ export const DCRateStrip: React.FC = () => (
22
23
  <RateItem>
23
24
  <RateNum>1 USD</RateNum>
24
25
  <RateSep>=</RateSep>
25
- <RateVal>{(DC_TO_USD * DC_TO_GOLD_SWAP_RATE / 1000).toFixed(0)}K Gold</RateVal>
26
+ <RateVal><MMORPGNumber value={DC_TO_USD * DC_TO_GOLD_SWAP_RATE} /> Gold</RateVal>
26
27
  </RateItem>
27
28
  </RateStrip>
28
29
  );
@@ -0,0 +1,22 @@
1
+ import React from 'react';
2
+ import { formatMMORPGNumber } from '../../utils/numberUtils';
3
+ import { SimpleTooltip } from './SimpleTooltip';
4
+
5
+ interface IMMORPGNumberProps {
6
+ value: number;
7
+ }
8
+
9
+ export const MMORPGNumber: React.FC<IMMORPGNumberProps> = ({ value }) => {
10
+ const formatted = formatMMORPGNumber(value);
11
+ const isShortened = formatted !== value.toString();
12
+
13
+ if (!isShortened) {
14
+ return <>{formatted}</>;
15
+ }
16
+
17
+ return (
18
+ <SimpleTooltip content={value.toLocaleString()}>
19
+ <span style={{ cursor: 'help' }}>{formatted}</span>
20
+ </SimpleTooltip>
21
+ );
22
+ };
@@ -2,7 +2,10 @@ import { ICharacterListing } from '@rpg-engine/shared';
2
2
  import { Meta, Story } from '@storybook/react';
3
3
  import React, { useState } from 'react';
4
4
  import { RPGUIRoot } from '../../..';
5
- import { CharacterDetailModal, ICharacterDetailModalProps } from '../../../components/Marketplace/CharacterDetailModal';
5
+ import {
6
+ CharacterDetailModal,
7
+ ICharacterDetailModalProps,
8
+ } from '../../../components/Marketplace/CharacterDetailModal';
6
9
  import entitiesAtlasJSON from '../../../mocks/atlas/entities/entities.json';
7
10
  import entitiesAtlasIMG from '../../../mocks/atlas/entities/entities.png';
8
11
  import itemsAtlasJSON from '../../../mocks/atlas/items/items.json';
@@ -31,11 +34,28 @@ const warriorListing: ICharacterListing = {
31
34
  race: 'Human',
32
35
  faction: 'Alliance',
33
36
  mode: 'Standard',
34
- skills: { level: 25, sword: 10, shielding: 8, strength: 18, dexterity: 7, resistance: 15 },
37
+ skills: {
38
+ level: 25,
39
+ sword: 10,
40
+ shielding: 8,
41
+ strength: 18,
42
+ dexterity: 7,
43
+ resistance: 15,
44
+ },
35
45
  equipment: [
36
- { slot: 'rightHand', itemName: 'Broad Sword', itemKey: 'broad-sword', rarity: 'Common' },
37
- { slot: 'armor', itemName: 'Steel Armor', itemKey: 'steel-armor', rarity: 'Rare' },
38
- { slot: 'head', itemName: 'Iron Helm', itemKey: 'iron-helm', rarity: 'Uncommon' },
46
+ {
47
+ slot: 'rightHand',
48
+ itemName: 'Broad Sword',
49
+ itemKey: 'broad-sword',
50
+ rarity: 'Common',
51
+ },
52
+ { slot: 'armor', itemName: 'Jacket', itemKey: 'jacket', rarity: 'Rare' },
53
+ {
54
+ slot: 'head',
55
+ itemName: 'Iron Helmet',
56
+ itemKey: 'iron-helmet',
57
+ rarity: 'Uncommon',
58
+ },
39
59
  ],
40
60
  textureKey: 'black-knight',
41
61
  },
@@ -57,11 +77,27 @@ const hardcoreListing: ICharacterListing = {
57
77
  race: 'Elf',
58
78
  faction: 'Horde',
59
79
  mode: 'Hardcore',
60
- skills: { level: 40, magic: 20, magicResistance: 18, dexterity: 12, stamina: 15 },
80
+ skills: {
81
+ level: 40,
82
+ magic: 20,
83
+ magicResistance: 18,
84
+ dexterity: 12,
85
+ stamina: 15,
86
+ },
61
87
  equipment: [
62
- { slot: 'rightHand', itemName: 'Staff of Flames', itemKey: 'fire-staff', rarity: 'Epic' },
63
- { slot: 'armor', itemName: 'Arcane Robe', itemKey: 'arcane-robe', rarity: 'Legendary' },
64
- { slot: 'neck', itemName: 'Mana Amulet', itemKey: 'mana-amulet', rarity: 'Rare' },
88
+ {
89
+ slot: 'rightHand',
90
+ itemName: 'Fire Staff',
91
+ itemKey: 'fire-staff',
92
+ rarity: 'Epic',
93
+ },
94
+ {
95
+ slot: 'armor',
96
+ itemName: 'Mystic Cape',
97
+ itemKey: 'mystic-cape',
98
+ rarity: 'Legendary',
99
+ },
100
+ { slot: 'neck', itemName: 'Bandana', itemKey: 'bandana', rarity: 'Rare' },
65
101
  ],
66
102
  textureKey: 'pink-mage-1',
67
103
  },
@@ -83,13 +119,22 @@ const pendingListing: ICharacterListing = {
83
119
  },
84
120
  };
85
121
 
86
- const ModalWrapper: React.FC<{ listing: ICharacterListing }> = ({ listing }) => {
122
+ const ModalWrapper: React.FC<{ listing: ICharacterListing }> = ({
123
+ listing,
124
+ }) => {
87
125
  const [isOpen, setIsOpen] = useState(true);
88
126
  const [lastAction, setLastAction] = useState<string | null>(null);
89
127
 
90
128
  return (
91
129
  <RPGUIRoot>
92
- <div style={{ padding: '20px', fontFamily: 'monospace', fontSize: '12px', color: '#fff' }}>
130
+ <div
131
+ style={{
132
+ padding: '20px',
133
+ fontFamily: 'monospace',
134
+ fontSize: '12px',
135
+ color: '#fff',
136
+ }}
137
+ >
93
138
  {lastAction ? (
94
139
  <p>✅ {lastAction}</p>
95
140
  ) : (
@@ -97,7 +142,10 @@ const ModalWrapper: React.FC<{ listing: ICharacterListing }> = ({ listing }) =>
97
142
  )}
98
143
  <button
99
144
  style={{ marginTop: 8, padding: '6px 12px', cursor: 'pointer' }}
100
- onClick={() => { setIsOpen(true); setLastAction(null); }}
145
+ onClick={() => {
146
+ setIsOpen(true);
147
+ setLastAction(null);
148
+ }}
101
149
  >
102
150
  Reopen Modal
103
151
  </button>
@@ -119,11 +167,17 @@ const ModalWrapper: React.FC<{ listing: ICharacterListing }> = ({ listing }) =>
119
167
  );
120
168
  };
121
169
 
122
- export const WarriorListing: Story = () => <ModalWrapper listing={warriorListing} />;
170
+ export const WarriorListing: Story = () => (
171
+ <ModalWrapper listing={warriorListing} />
172
+ );
123
173
  WarriorListing.storyName = 'Standard warrior listing';
124
174
 
125
- export const HardcoreMageListing: Story = () => <ModalWrapper listing={hardcoreListing} />;
175
+ export const HardcoreMageListing: Story = () => (
176
+ <ModalWrapper listing={hardcoreListing} />
177
+ );
126
178
  HardcoreMageListing.storyName = 'Hardcore mage — high price';
127
179
 
128
- export const PendingPurchase: Story = () => <ModalWrapper listing={pendingListing} />;
180
+ export const PendingPurchase: Story = () => (
181
+ <ModalWrapper listing={pendingListing} />
182
+ );
129
183
  PendingPurchase.storyName = 'Listing with pending purchase (buy disabled)';