@rpg-engine/long-bow 0.8.187 → 0.8.189

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.
@@ -7,4 +7,5 @@ export declare const CharacterBrowseEmpty: Story;
7
7
  export declare const CharacterBrowseFiltered: Story;
8
8
  export declare const MyCharacterListings: Story;
9
9
  export declare const MyCharacterListingsEmpty: Story;
10
+ export declare const MyCharacterListingsNoEligible: Story;
10
11
  export declare const CharacterListingPending: Story;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpg-engine/long-bow",
3
- "version": "0.8.187",
3
+ "version": "0.8.189",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -117,6 +117,7 @@ export const CharacterDetailModal: React.FC<ICharacterDetailModalProps> = ({
117
117
  imgScale={4}
118
118
  height={96}
119
119
  width={96}
120
+ centered
120
121
  />
121
122
  </SpriteContainer>
122
123
  <HeroInfo>
@@ -135,23 +136,22 @@ export const CharacterDetailModal: React.FC<ICharacterDetailModalProps> = ({
135
136
 
136
137
  <Divider />
137
138
 
138
- {topSkills.length > 0 && (
139
- <Section>
140
- <SectionTitle>Skills</SectionTitle>
141
- <SkillsGrid>
142
- {topSkills.map(([name, value]) => (
143
- <SkillRow key={name}>
144
- <SkillName>{name}</SkillName>
145
- <SkillValue>{value}</SkillValue>
146
- </SkillRow>
147
- ))}
148
- </SkillsGrid>
149
- </Section>
150
- )}
151
-
152
- {snap.equipment?.length > 0 && (
153
- <>
154
- <Divider />
139
+ <MetaColumns>
140
+ {topSkills.length > 0 && (
141
+ <Section>
142
+ <SectionTitle>Skills</SectionTitle>
143
+ <SkillsList>
144
+ {topSkills.map(([name, value]) => (
145
+ <SkillRow key={name}>
146
+ <SkillName>{name}</SkillName>
147
+ <SkillValue>{value}</SkillValue>
148
+ </SkillRow>
149
+ ))}
150
+ </SkillsList>
151
+ </Section>
152
+ )}
153
+
154
+ {snap.equipment?.length > 0 && (
155
155
  <Section>
156
156
  <SectionTitle>Equipment</SectionTitle>
157
157
  <EquipmentList>
@@ -164,8 +164,8 @@ export const CharacterDetailModal: React.FC<ICharacterDetailModalProps> = ({
164
164
  ))}
165
165
  </EquipmentList>
166
166
  </Section>
167
- </>
168
- )}
167
+ )}
168
+ </MetaColumns>
169
169
 
170
170
  <Divider />
171
171
 
@@ -179,6 +179,7 @@ export const CharacterDetailModal: React.FC<ICharacterDetailModalProps> = ({
179
179
  atlasJSON={atlasJSON}
180
180
  spriteKey="others/definya-coin.png"
181
181
  imgScale={1}
182
+ centered
182
183
  />
183
184
  </DCCoinWrapper>
184
185
  <PriceAmount>{formatDCAmount(listing.price)} DC</PriceAmount>
@@ -227,8 +228,8 @@ const ModalContent = styled.div`
227
228
  border: 2px solid #f59e0b;
228
229
  border-radius: 8px;
229
230
  padding: 20px 24px;
230
- width: 480px;
231
- max-width: 94%;
231
+ width: 580px;
232
+ max-width: 96%;
232
233
  max-height: 85dvh;
233
234
  display: flex;
234
235
  flex-direction: column;
@@ -348,10 +349,17 @@ const SectionTitle = styled.span`
348
349
  letter-spacing: 1px;
349
350
  `;
350
351
 
351
- const SkillsGrid = styled.div`
352
+ const MetaColumns = styled.div`
352
353
  display: grid;
353
- grid-template-columns: repeat(2, 1fr);
354
- gap: 4px 12px;
354
+ grid-template-columns: 1fr 1fr;
355
+ gap: 16px;
356
+ align-items: start;
357
+ `;
358
+
359
+ const SkillsList = styled.div`
360
+ display: flex;
361
+ flex-direction: column;
362
+ gap: 4px;
355
363
  `;
356
364
 
357
365
  const SkillRow = styled.div`
@@ -173,6 +173,7 @@ export const CharacterListingModal: React.FC<ICharacterListingModalProps> = ({
173
173
  atlasJSON={atlasJSON}
174
174
  spriteKey="others/definya-coin.png"
175
175
  imgScale={1}
176
+ centered
176
177
  />
177
178
  </DCCoinWrapper>
178
179
  <PricePreviewAmount>{Number(price).toLocaleString()} DC</PricePreviewAmount>
@@ -81,6 +81,7 @@ export const CharacterMarketplacePanel: React.FC<ICharacterMarketplacePanelProps
81
81
  imgScale={3}
82
82
  height={64}
83
83
  width={64}
84
+ centered
84
85
  />
85
86
  );
86
87
  };
@@ -147,6 +148,7 @@ export const CharacterMarketplacePanel: React.FC<ICharacterMarketplacePanelProps
147
148
  atlasJSON={atlasJSON}
148
149
  spriteKey="others/definya-coin.png"
149
150
  imgScale={1}
151
+ centered
150
152
  />
151
153
  </DCCoinWrapper>
152
154
  {formatDCAmount(listing.price)} DC
@@ -342,6 +342,8 @@ export const Marketplace: React.FC<IMarketPlaceProps> = props => {
342
342
  itemsPerPage={10}
343
343
  onPageChange={onMyCharacterListingsPageChange ?? (() => {})}
344
344
  onCharacterDelist={onCharacterDelist ?? (() => {})}
345
+ accountCharacters={accountCharacters ?? []}
346
+ onCharacterList={onCharacterList ?? (() => {})}
345
347
  atlasJSON={props.atlasJSON}
346
348
  atlasIMG={props.atlasIMG}
347
349
  characterAtlasJSON={characterAtlasJSON ?? props.atlasJSON}
@@ -1,11 +1,14 @@
1
- import { formatDCAmount, ICharacterListing } from '@rpg-engine/shared';
1
+ import { formatDCAmount, ICharacter, ICharacterListing } from '@rpg-engine/shared';
2
2
  import { Delete } from 'pixelarticons/react/Delete';
3
+ import { ShoppingBag } from 'pixelarticons/react/ShoppingBag';
3
4
  import { User } from 'pixelarticons/react/User';
4
5
  import React, { useEffect, useRef, useState } from 'react';
5
6
  import styled from 'styled-components';
6
7
  import { ConfirmModal } from '../ConfirmModal';
8
+ import { Pagination } from '../shared/Pagination/Pagination';
7
9
  import { SpriteFromAtlas } from '../shared/SpriteFromAtlas';
8
10
  import { CTAButton } from '../shared/CTAButton/CTAButton';
11
+ import { CharacterListingModal } from './CharacterListingModal';
9
12
 
10
13
  export interface IMyCharacterListingsPanelProps {
11
14
  myCharacterListings: ICharacterListing[];
@@ -14,6 +17,9 @@ export interface IMyCharacterListingsPanelProps {
14
17
  itemsPerPage: number;
15
18
  onPageChange: (page: number) => void;
16
19
  onCharacterDelist: (listingId: string) => void;
20
+ /** Characters that can be listed (used for the List a Character modal) */
21
+ accountCharacters: ICharacter[];
22
+ onCharacterList: (characterId: string, price: number) => void;
17
23
  /** Items atlas — for UI sprites like the DC coin */
18
24
  atlasJSON: any;
19
25
  atlasIMG: any;
@@ -31,60 +37,81 @@ export const MyCharacterListingsPanel: React.FC<IMyCharacterListingsPanelProps>
31
37
  itemsPerPage,
32
38
  onPageChange,
33
39
  onCharacterDelist,
40
+ accountCharacters,
41
+ onCharacterList,
34
42
  atlasJSON,
35
43
  atlasIMG,
36
44
  characterAtlasJSON,
37
45
  characterAtlasIMG,
38
46
  enableHotkeys,
47
+ disableHotkeys,
39
48
  }) => {
40
- const [delistingListingId, setDelistingListingId] = useState<string | null>(null);
49
+ const [delistingId, setDelistingId] = useState<string | null>(null);
50
+ const [isListingModalOpen, setIsListingModalOpen] = useState(false);
41
51
  const itemsContainer = useRef<HTMLDivElement>(null);
42
52
 
43
53
  useEffect(() => {
44
54
  itemsContainer.current?.scrollTo(0, 0);
45
55
  }, [currentPage]);
46
56
 
47
- const handleDelistClick = (listingId: string) => {
48
- setDelistingListingId(listingId);
49
- };
50
-
51
57
  const handleDelistConfirm = () => {
52
- if (delistingListingId) {
53
- onCharacterDelist(delistingListingId);
54
- setDelistingListingId(null);
58
+ if (delistingId) {
59
+ onCharacterDelist(delistingId);
60
+ setDelistingId(null);
55
61
  enableHotkeys?.();
56
62
  }
57
63
  };
58
64
 
59
-
60
- const getFormattedDate = (date: Date) => {
61
- return new Date(date).toLocaleDateString();
62
- };
65
+ const eligibleCount = accountCharacters.filter(
66
+ c => !c.isListedForSale && !c.tradedAt
67
+ ).length;
63
68
 
64
69
  return (
65
70
  <>
66
- {delistingListingId && (
71
+ {delistingId && (
67
72
  <ConfirmModal
68
- onClose={() => {
69
- setDelistingListingId(null);
70
- enableHotkeys?.();
71
- }}
73
+ onClose={() => { setDelistingId(null); enableHotkeys?.(); }}
72
74
  onConfirm={handleDelistConfirm}
73
75
  message="Are you sure you want to delist this character?"
74
76
  />
75
77
  )}
76
78
 
77
- <ListingsContainer id="MarketContainer" ref={itemsContainer}>
79
+ <CharacterListingModal
80
+ isOpen={isListingModalOpen}
81
+ onClose={() => setIsListingModalOpen(false)}
82
+ accountCharacters={accountCharacters}
83
+ atlasJSON={atlasJSON}
84
+ atlasIMG={atlasIMG}
85
+ characterAtlasJSON={characterAtlasJSON}
86
+ characterAtlasIMG={characterAtlasIMG}
87
+ onCharacterList={(id, price) => {
88
+ onCharacterList(id, price);
89
+ setIsListingModalOpen(false);
90
+ }}
91
+ enableHotkeys={enableHotkeys}
92
+ disableHotkeys={disableHotkeys}
93
+ />
94
+
95
+ <Toolbar>
96
+ <ListCharacterBtn
97
+ icon={<ShoppingBag width={16} height={16} />}
98
+ label={`List a Character${eligibleCount > 0 ? ` (${eligibleCount} eligible)` : ''}`}
99
+ onClick={() => setIsListingModalOpen(true)}
100
+ disabled={eligibleCount === 0}
101
+ />
102
+ </Toolbar>
103
+
104
+ <ListingsContainer ref={itemsContainer}>
78
105
  {myCharacterListings.length === 0 ? (
79
106
  <EmptyState>
80
107
  <User width={32} height={32} />
81
- <EmptyText>You have no listed characters.</EmptyText>
82
- <EmptySubtext>Use the "List Character" tab to list a character for sale.</EmptySubtext>
108
+ <EmptyText>No listed characters.</EmptyText>
109
+ <EmptySubtext>Use the "List Character" tab to put a character up for sale.</EmptySubtext>
83
110
  </EmptyState>
84
111
  ) : (
85
112
  <ListingsGrid>
86
113
  {myCharacterListings.map((listing) => (
87
- <CharacterListingCard key={listing._id}>
114
+ <CharacterListingCard key={listing._id} $isBeingBought={listing.isBeingBought}>
88
115
  <CharacterSprite>
89
116
  <SpriteFromAtlas
90
117
  atlasIMG={characterAtlasIMG}
@@ -93,11 +120,14 @@ export const MyCharacterListingsPanel: React.FC<IMyCharacterListingsPanelProps>
93
120
  imgScale={3}
94
121
  height={64}
95
122
  width={64}
123
+ centered
96
124
  />
97
125
  </CharacterSprite>
98
126
  <CharacterInfo>
99
127
  <CharacterName>{listing.characterSnapshot.name || 'Unknown'}</CharacterName>
100
- <CharacterMeta>Level {listing.characterSnapshot.level}</CharacterMeta>
128
+ <CharacterMeta>
129
+ Lv.{listing.characterSnapshot.level} · {listing.characterSnapshot.class}
130
+ </CharacterMeta>
101
131
  <ListingPrice>
102
132
  <DCCoinWrapper>
103
133
  <SpriteFromAtlas
@@ -105,22 +135,24 @@ export const MyCharacterListingsPanel: React.FC<IMyCharacterListingsPanelProps>
105
135
  atlasJSON={atlasJSON}
106
136
  spriteKey="others/definya-coin.png"
107
137
  imgScale={1}
138
+ centered
108
139
  />
109
140
  </DCCoinWrapper>
110
141
  {formatDCAmount(listing.price)} DC
111
142
  </ListingPrice>
112
- <ListingMeta>Listed {getFormattedDate(listing.createdAt)}</ListingMeta>
113
- {listing.isBeingBought && (
114
- <ListingStatus $status="pending">Sale Pending</ListingStatus>
115
- )}
143
+ <ListingDate>
144
+ Listed {new Date(listing.createdAt).toLocaleDateString()}
145
+ </ListingDate>
116
146
  </CharacterInfo>
117
- <ActionButton
118
- icon={<Delete width={16} height={16} />}
147
+ <DelistBtn
148
+ icon={<Delete width={14} height={14} />}
119
149
  label="Delist"
120
- onClick={() => handleDelistClick(listing._id)}
121
- iconColor="#ef4444"
150
+ onClick={() => setDelistingId(listing._id)}
122
151
  disabled={listing.isBeingBought}
123
152
  />
153
+ {listing.isBeingBought && (
154
+ <PendingBadge>Pending</PendingBadge>
155
+ )}
124
156
  </CharacterListingCard>
125
157
  ))}
126
158
  </ListingsGrid>
@@ -129,18 +161,11 @@ export const MyCharacterListingsPanel: React.FC<IMyCharacterListingsPanelProps>
129
161
 
130
162
  {totalCount > itemsPerPage && (
131
163
  <PagerFooter>
132
- <Pagination>
133
- {Array.from({ length: Math.ceil(totalCount / itemsPerPage) }, (_, i) => i + 1).map(page => (
134
- <PageButton
135
- key={page}
136
- $active={currentPage === page}
137
- type="button"
138
- onClick={() => onPageChange(page)}
139
- >
140
- {page}
141
- </PageButton>
142
- ))}
143
- </Pagination>
164
+ <Pagination
165
+ currentPage={currentPage}
166
+ totalPages={Math.ceil(totalCount / itemsPerPage)}
167
+ onPageChange={onPageChange}
168
+ />
144
169
  </PagerFooter>
145
170
  )}
146
171
  </>
@@ -169,15 +194,26 @@ const ListingsGrid = styled.div`
169
194
  padding: 12px;
170
195
  `;
171
196
 
172
- const CharacterListingCard = styled.div`
197
+ const CharacterListingCard = styled.div<{ $isBeingBought?: boolean }>`
173
198
  display: flex;
174
199
  flex-direction: column;
175
200
  align-items: center;
176
201
  gap: 8px;
177
202
  background: rgba(255, 255, 255, 0.03);
178
- border: 1px solid rgba(255, 255, 255, 0.08);
203
+ border: 1px solid ${({ $isBeingBought }) =>
204
+ $isBeingBought ? 'rgba(239,68,68,0.3)' : 'rgba(255,255,255,0.08)'};
179
205
  border-radius: 8px;
180
206
  padding: 12px;
207
+ position: relative;
208
+ opacity: ${({ $isBeingBought }) => ($isBeingBought ? 0.7 : 1)};
209
+ transition: border-color 0.15s, background 0.15s;
210
+
211
+ &:hover {
212
+ border-color: ${({ $isBeingBought }) =>
213
+ $isBeingBought ? 'rgba(239,68,68,0.5)' : 'rgba(245,158,11,0.3)'};
214
+ background: ${({ $isBeingBought }) =>
215
+ $isBeingBought ? 'rgba(239,68,68,0.04)' : 'rgba(245,158,11,0.04)'};
216
+ }
181
217
  `;
182
218
 
183
219
  const CharacterSprite = styled.div`
@@ -185,6 +221,9 @@ const CharacterSprite = styled.div`
185
221
  align-items: center;
186
222
  justify-content: center;
187
223
  image-rendering: pixelated;
224
+ width: 64px;
225
+ height: 64px;
226
+ flex-shrink: 0;
188
227
  `;
189
228
 
190
229
  const CharacterInfo = styled.div`
@@ -193,19 +232,21 @@ const CharacterInfo = styled.div`
193
232
  align-items: center;
194
233
  gap: 4px;
195
234
  text-align: center;
235
+ width: 100%;
196
236
  `;
197
237
 
198
238
  const CharacterName = styled.span`
199
- font-family: 'Press Start 2P', cursive;
200
- font-size: 0.55rem;
201
- color: #f3f4f6;
239
+ font-family: 'Press Start 2P', cursive !important;
240
+ font-size: 0.55rem !important;
241
+ color: #f3f4f6 !important;
202
242
  text-transform: uppercase;
203
243
  letter-spacing: 0.5px;
204
244
  `;
205
245
 
206
246
  const CharacterMeta = styled.span`
207
- font-size: 0.45rem;
208
- color: #888;
247
+ font-family: 'Press Start 2P', cursive !important;
248
+ font-size: 0.45rem !important;
249
+ color: #888 !important;
209
250
  text-transform: uppercase;
210
251
  letter-spacing: 0.5px;
211
252
  `;
@@ -213,10 +254,12 @@ const CharacterMeta = styled.span`
213
254
  const ListingPrice = styled.div`
214
255
  display: flex;
215
256
  align-items: center;
216
- gap: 6px;
217
- font-family: 'Press Start 2P', cursive;
218
- font-size: 0.5rem;
219
- color: #fef08a;
257
+ justify-content: center;
258
+ gap: 4px;
259
+ font-family: 'Press Start 2P', cursive !important;
260
+ font-size: 0.5rem !important;
261
+ color: #fef08a !important;
262
+ line-height: 1;
220
263
  `;
221
264
 
222
265
  const DCCoinWrapper = styled.span`
@@ -224,48 +267,55 @@ const DCCoinWrapper = styled.span`
224
267
  align-items: center;
225
268
  justify-content: center;
226
269
  flex-shrink: 0;
270
+ line-height: 0;
227
271
  `;
228
272
 
229
- const ListingMeta = styled.span`
230
- font-size: 0.38rem;
231
- color: #52525b;
273
+ const ListingDate = styled.span`
274
+ font-family: 'Press Start 2P', cursive !important;
275
+ font-size: 0.35rem !important;
276
+ color: #52525b !important;
232
277
  text-transform: uppercase;
233
278
  letter-spacing: 0.5px;
234
279
  `;
235
280
 
236
- const ListingStatus = styled.span<{ $status: 'pending' | 'sold' }>`
237
- font-size: 0.38rem;
238
- padding: 2px 6px;
281
+ const DelistBtn = styled(CTAButton)`
282
+ width: 100%;
283
+ padding: 6px 10px;
284
+ height: 28px;
285
+ margin-top: 2px;
286
+
287
+ span { font-size: 0.48rem; }
288
+ svg { font-size: 0.9rem; }
289
+ `;
290
+
291
+ const PendingBadge = styled.span`
292
+ position: absolute;
293
+ top: 6px;
294
+ right: 6px;
295
+ background: rgba(239, 68, 68, 0.2);
296
+ border: 1px solid rgba(239, 68, 68, 0.4);
239
297
  border-radius: 4px;
298
+ padding: 2px 6px;
299
+ font-family: 'Press Start 2P', cursive !important;
300
+ font-size: 0.35rem !important;
301
+ color: #ef4444 !important;
240
302
  text-transform: uppercase;
241
303
  letter-spacing: 0.5px;
242
- ${({ $status }) =>
243
- $status === 'pending'
244
- ? `
245
- background: rgba(239, 68, 68, 0.2);
246
- border: 1px solid rgba(239, 68, 68, 0.4);
247
- color: #ef4444;
248
- `
249
- : `
250
- background: rgba(34, 197, 94, 0.2);
251
- border: 1px solid rgba(34, 197, 94, 0.4);
252
- color: #22c55e;
253
- `}
254
304
  `;
255
305
 
256
- const ActionButton = styled(CTAButton)<{ disabled?: boolean }>`
257
- padding: 6px 12px;
258
- height: 28px;
259
- opacity: ${({ disabled }) => (disabled ? 0.5 : 1)};
260
- pointer-events: ${({ disabled }) => (disabled ? 'none' : 'auto')};
306
+ const Toolbar = styled.div`
307
+ width: 95%;
308
+ margin: 10px auto 0 auto;
309
+ display: flex;
310
+ justify-content: flex-end;
311
+ `;
261
312
 
262
- span {
263
- font-size: 0.5rem;
264
- }
313
+ const ListCharacterBtn = styled(CTAButton)`
314
+ padding: 8px 16px;
315
+ height: 32px;
265
316
 
266
- svg {
267
- font-size: 1rem;
268
- }
317
+ span { font-size: 0.5rem; }
318
+ svg { font-size: 1rem; }
269
319
  `;
270
320
 
271
321
  const PagerFooter = styled.div`
@@ -278,28 +328,6 @@ const PagerFooter = styled.div`
278
328
  margin: 0 auto;
279
329
  `;
280
330
 
281
- const Pagination = styled.div`
282
- display: flex;
283
- gap: 6px;
284
- `;
285
-
286
- const PageButton = styled.button<{ $active: boolean }>`
287
- padding: 6px 10px;
288
- font-family: 'Press Start 2P', cursive;
289
- font-size: 0.5rem;
290
- border-radius: 4px;
291
- border: 1px solid ${({ $active }) => ($active ? '#f59e0b' : 'rgba(255,255,255,0.12)')};
292
- background: ${({ $active }) => ($active ? 'rgba(245,158,11,0.15)' : 'rgba(0,0,0,0.3)')};
293
- color: ${({ $active }) => ($active ? '#f59e0b' : '#777')};
294
- cursor: pointer;
295
- transition: border-color 0.15s, background 0.15s, color 0.15s;
296
-
297
- &:hover {
298
- border-color: ${({ $active }) => ($active ? '#f59e0b' : 'rgba(255,255,255,0.3)')};
299
- color: ${({ $active }) => ($active ? '#f59e0b' : '#bbb')};
300
- }
301
- `;
302
-
303
331
  const EmptyState = styled.div`
304
332
  display: flex;
305
333
  flex-direction: column;
@@ -311,17 +339,19 @@ const EmptyState = styled.div`
311
339
  `;
312
340
 
313
341
  const EmptyText = styled.span`
314
- font-size: 0.55rem;
315
- color: #71717a;
342
+ font-family: 'Press Start 2P', cursive !important;
343
+ font-size: 0.48rem !important;
344
+ color: #71717a !important;
316
345
  text-transform: uppercase;
317
346
  letter-spacing: 1px;
318
347
  `;
319
348
 
320
349
  const EmptySubtext = styled.span`
321
- font-size: 0.45rem;
322
- color: #52525b;
323
- text-transform: none;
324
- letter-spacing: 0.5px;
350
+ font-family: 'Press Start 2P', cursive !important;
351
+ font-size: 0.38rem !important;
352
+ color: #52525b !important;
325
353
  text-align: center;
326
- max-width: 280px;
354
+ max-width: 260px;
355
+ line-height: 1.6;
327
356
  `;
357
+
@@ -1,4 +1,4 @@
1
- import { ICharacterListing } from '@rpg-engine/shared';
1
+ import { ICharacter, ICharacterListing } from '@rpg-engine/shared';
2
2
  import { Meta, Story } from '@storybook/react';
3
3
  import React from 'react';
4
4
  import { RPGUIRoot } from '../../..';
@@ -189,6 +189,25 @@ const mockMyCharacterListings: ICharacterListing[] = [
189
189
  },
190
190
  ];
191
191
 
192
+ const mockAccountCharacters: ICharacter[] = [
193
+ {
194
+ _id: 'char-a1',
195
+ name: 'MyRogue',
196
+ textureKey: 'redhair-girl-1',
197
+ isListedForSale: false,
198
+ tradedAt: undefined,
199
+ skills: { level: 22 } as any,
200
+ } as ICharacter,
201
+ {
202
+ _id: 'char-a2',
203
+ name: 'MyPaladin',
204
+ textureKey: 'dragon-knight',
205
+ isListedForSale: false,
206
+ tradedAt: undefined,
207
+ skills: { level: 38 } as any,
208
+ } as ICharacter,
209
+ ];
210
+
192
211
  export const CharacterBrowse: Story = () => (
193
212
  <RPGUIRoot>
194
213
  <CharacterMarketplacePanel
@@ -288,6 +307,8 @@ export const MyCharacterListings: Story = () => (
288
307
  itemsPerPage={8}
289
308
  onPageChange={() => {}}
290
309
  onCharacterDelist={() => {}}
310
+ accountCharacters={mockAccountCharacters}
311
+ onCharacterList={() => {}}
291
312
  atlasJSON={itemsAtlasJSON}
292
313
  atlasIMG={itemsAtlasIMG}
293
314
  characterAtlasJSON={entitiesAtlasJSON}
@@ -307,6 +328,8 @@ export const MyCharacterListingsEmpty: Story = () => (
307
328
  itemsPerPage={8}
308
329
  onPageChange={() => {}}
309
330
  onCharacterDelist={() => {}}
331
+ accountCharacters={mockAccountCharacters}
332
+ onCharacterList={() => {}}
310
333
  atlasJSON={itemsAtlasJSON}
311
334
  atlasIMG={itemsAtlasIMG}
312
335
  characterAtlasJSON={entitiesAtlasJSON}
@@ -317,6 +340,27 @@ export const MyCharacterListingsEmpty: Story = () => (
317
340
 
318
341
  MyCharacterListingsEmpty.storyName = 'My Listings - Empty State';
319
342
 
343
+ export const MyCharacterListingsNoEligible: Story = () => (
344
+ <RPGUIRoot>
345
+ <MyCharacterListingsPanel
346
+ myCharacterListings={mockMyCharacterListings}
347
+ totalCount={2}
348
+ currentPage={1}
349
+ itemsPerPage={8}
350
+ onPageChange={() => {}}
351
+ onCharacterDelist={() => {}}
352
+ accountCharacters={[]}
353
+ onCharacterList={() => {}}
354
+ atlasJSON={itemsAtlasJSON}
355
+ atlasIMG={itemsAtlasIMG}
356
+ characterAtlasJSON={entitiesAtlasJSON}
357
+ characterAtlasIMG={entitiesAtlasIMG}
358
+ />
359
+ </RPGUIRoot>
360
+ );
361
+
362
+ MyCharacterListingsNoEligible.storyName = 'My Listings - No eligible chars to list';
363
+
320
364
  export const CharacterListingPending: Story = () => (
321
365
  <RPGUIRoot>
322
366
  <CharacterMarketplacePanel