@rpg-engine/long-bow 0.8.136 → 0.8.138

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpg-engine/long-bow",
3
- "version": "0.8.136",
3
+ "version": "0.8.138",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -1,7 +1,6 @@
1
1
  import React from 'react';
2
2
  import styled from 'styled-components';
3
3
  import { uiColors } from '../../constants/uiColors';
4
- import { Dropdown, IOptionsProps } from '../Dropdown';
5
4
  import { Pagination } from '../shared/Pagination/Pagination';
6
5
 
7
6
  export interface IDCTransaction {
@@ -17,24 +16,35 @@ export interface IDCTransaction {
17
16
  const TRANSACTION_TYPE_LABELS: Record<string, string> = {
18
17
  Purchase: 'Purchase',
19
18
  Transfer: 'Transfer',
20
- MarketplaceSale: 'Marketplace Sale',
21
- MarketplacePurchase: 'Marketplace Buy',
22
- StorePurchase: 'Store Purchase',
19
+ MarketplaceSale: 'Mkt Sale',
20
+ MarketplacePurchase: 'Mkt Buy',
21
+ StorePurchase: 'Store',
23
22
  Fee: 'Fee',
24
23
  Refund: 'Refund',
25
- AdminAdjustment: 'Admin Adjustment',
24
+ AdminAdjustment: 'Admin',
26
25
  };
27
26
 
28
- const TRANSACTION_TYPE_OPTIONS: IOptionsProps[] = [
29
- { id: 0, value: '', option: 'All Types' },
30
- { id: 1, value: 'Purchase', option: 'Purchase' },
31
- { id: 2, value: 'Transfer', option: 'Transfer' },
32
- { id: 3, value: 'MarketplaceSale', option: 'Marketplace Sale' },
33
- { id: 4, value: 'MarketplacePurchase', option: 'Marketplace Buy' },
34
- { id: 5, value: 'StorePurchase', option: 'Store Purchase' },
35
- { id: 6, value: 'Fee', option: 'Fee' },
36
- { id: 7, value: 'Refund', option: 'Refund' },
37
- { id: 8, value: 'AdminAdjustment', option: 'Admin Adjustment' },
27
+ const TRANSACTION_TYPE_COLORS: Record<string, string> = {
28
+ Purchase: '#22c55e', // green — DC coming in
29
+ Transfer: '#60a5fa', // blue — P2P transfer
30
+ MarketplaceSale: '#fbbf24', // gold — sold item
31
+ MarketplacePurchase: '#f97316', // orange bought item
32
+ StorePurchase: '#a78bfa', // purple store
33
+ Fee: '#ef4444', // red — cost
34
+ Refund: '#06b6d4', // cyan — money back
35
+ AdminAdjustment: '#9ca3af', // gray — admin
36
+ };
37
+
38
+ const TRANSACTION_TYPE_OPTIONS = [
39
+ { value: '', label: 'All Types' },
40
+ { value: 'Purchase', label: 'Purchase' },
41
+ { value: 'Transfer', label: 'Transfer' },
42
+ { value: 'MarketplaceSale', label: 'Marketplace Sale' },
43
+ { value: 'MarketplacePurchase', label: 'Marketplace Buy' },
44
+ { value: 'StorePurchase', label: 'Store Purchase' },
45
+ { value: 'Fee', label: 'Fee' },
46
+ { value: 'Refund', label: 'Refund' },
47
+ { value: 'AdminAdjustment', label: 'Admin Adjustment' },
38
48
  ];
39
49
 
40
50
  export interface IDCHistoryPanelProps {
@@ -54,7 +64,14 @@ export const DCHistoryPanel: React.FC<IDCHistoryPanelProps> = ({
54
64
  }) => {
55
65
  const [selectedType, setSelectedType] = React.useState<string>('');
56
66
 
57
- const handleTypeChange = (value: string): void => {
67
+ // Auto-load on first mount (when History tab is opened)
68
+ React.useEffect(() => {
69
+ onRequestHistory(1);
70
+ // eslint-disable-next-line react-hooks/exhaustive-deps
71
+ }, []);
72
+
73
+ const handleTypeChange = (e: React.ChangeEvent<HTMLSelectElement>): void => {
74
+ const value = e.target.value;
58
75
  setSelectedType(value);
59
76
  onRequestHistory(1, value || undefined);
60
77
  };
@@ -65,15 +82,18 @@ export const DCHistoryPanel: React.FC<IDCHistoryPanelProps> = ({
65
82
 
66
83
  const formatDate = (dateStr: string): string => {
67
84
  const d = new Date(dateStr);
68
- const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
69
- return `${months[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`;
85
+ return `${d.getMonth() + 1}/${d.getDate()}/${String(d.getFullYear()).slice(-2)}`;
70
86
  };
71
87
 
72
88
  return (
73
89
  <PanelContainer>
74
90
  <FilterRow>
75
91
  <FilterLabel>Filter:</FilterLabel>
76
- <Dropdown options={TRANSACTION_TYPE_OPTIONS} onChange={handleTypeChange} width="200px" />
92
+ <TypeSelect value={selectedType} onChange={handleTypeChange}>
93
+ {TRANSACTION_TYPE_OPTIONS.map((opt) => (
94
+ <option key={opt.value} value={opt.value}>{opt.label}</option>
95
+ ))}
96
+ </TypeSelect>
77
97
  </FilterRow>
78
98
 
79
99
  {loading && (
@@ -92,18 +112,21 @@ export const DCHistoryPanel: React.FC<IDCHistoryPanelProps> = ({
92
112
  {transactions.map((tx) => {
93
113
  const isCredit = tx.amount > 0;
94
114
  const label = TRANSACTION_TYPE_LABELS[tx.type] ?? tx.type;
115
+ const color = TRANSACTION_TYPE_COLORS[tx.type] ?? '#f59e0b';
116
+ const subtitle = tx.note ?? (tx.relatedCharacterName ? tx.relatedCharacterName : '');
95
117
 
96
118
  return (
97
119
  <TransactionRow key={tx._id}>
98
- <TxType>{label}</TxType>
99
- <TxAmount $credit={isCredit}>
100
- {isCredit ? '+' : ''}{tx.amount} DC
101
- </TxAmount>
102
- <TxNote>
103
- {tx.note ?? (tx.relatedCharacterName ? `With: ${tx.relatedCharacterName}` : '')}
104
- </TxNote>
105
- <TxDate>{formatDate(tx.createdAt)}</TxDate>
106
- <TxBalance>Balance: {tx.balanceAfter} DC</TxBalance>
120
+ <TxLeft>
121
+ <TxType $color={color}>{label}</TxType>
122
+ {subtitle ? <TxNote>{subtitle}</TxNote> : null}
123
+ </TxLeft>
124
+ <TxRight>
125
+ <TxAmount $credit={isCredit}>
126
+ {isCredit ? '+' : ''}{tx.amount} DC
127
+ </TxAmount>
128
+ <TxDate>{formatDate(tx.createdAt)}</TxDate>
129
+ </TxRight>
107
130
  </TransactionRow>
108
131
  );
109
132
  })}
@@ -132,76 +155,115 @@ const FilterRow = styled.div`
132
155
  display: flex;
133
156
  align-items: center;
134
157
  gap: 8px;
135
- margin-bottom: 4px;
158
+ margin-bottom: 2px;
136
159
  `;
137
160
 
138
161
  const FilterLabel = styled.span`
139
- font-size: 8px;
162
+ font-size: 7px;
140
163
  color: #f59e0b;
141
164
  font-family: 'Press Start 2P', cursive;
142
165
  white-space: nowrap;
143
166
  `;
144
167
 
168
+ const TypeSelect = styled.select`
169
+ background: rgba(0, 0, 0, 0.7);
170
+ border: 1px solid rgba(245, 158, 11, 0.4);
171
+ border-radius: 3px;
172
+ color: #f59e0b;
173
+ font-size: 7px;
174
+ font-family: 'Press Start 2P', cursive;
175
+ padding: 3px 5px;
176
+ cursor: pointer;
177
+ outline: none;
178
+ flex: 1;
179
+
180
+ option {
181
+ background: #1a1a2e;
182
+ color: #f59e0b;
183
+ }
184
+
185
+ &:hover {
186
+ border-color: #f59e0b;
187
+ }
188
+
189
+ &:focus {
190
+ border-color: #f59e0b;
191
+ box-shadow: 0 0 0 1px rgba(245, 158, 11, 0.3);
192
+ }
193
+ `;
194
+
145
195
  const TransactionList = styled.div`
146
196
  display: flex;
147
197
  flex-direction: column;
148
- gap: 4px;
149
- max-height: 280px;
198
+ gap: 3px;
199
+ max-height: 260px;
150
200
  overflow-y: auto;
201
+
202
+ &::-webkit-scrollbar {
203
+ width: 4px;
204
+ }
205
+ &::-webkit-scrollbar-track {
206
+ background: rgba(0, 0, 0, 0.3);
207
+ }
208
+ &::-webkit-scrollbar-thumb {
209
+ background: rgba(245, 158, 11, 0.4);
210
+ border-radius: 2px;
211
+ }
151
212
  `;
152
213
 
153
214
  const TransactionRow = styled.div`
154
- background: rgba(0, 0, 0, 0.4);
155
- border: 1px solid rgba(255, 215, 0, 0.2);
156
- padding: 8px;
157
- display: grid;
158
- grid-template-columns: 1fr auto;
159
- grid-template-rows: auto auto auto;
215
+ background: rgba(0, 0, 0, 0.35);
216
+ border: 1px solid rgba(245, 158, 11, 0.15);
217
+ border-radius: 2px;
218
+ padding: 5px 7px;
219
+ display: flex;
220
+ align-items: center;
221
+ gap: 8px;
222
+ `;
223
+
224
+ const TxLeft = styled.div`
225
+ flex: 1;
226
+ min-width: 0;
227
+ display: flex;
228
+ flex-direction: column;
160
229
  gap: 2px;
161
230
  `;
162
231
 
163
- const TxType = styled.span`
164
- font-size: 8px;
165
- color: #f59e0b;
232
+ const TxRight = styled.div`
233
+ display: flex;
234
+ flex-direction: column;
235
+ align-items: flex-end;
236
+ gap: 2px;
237
+ flex-shrink: 0;
238
+ `;
239
+
240
+ const TxType = styled.span<{ $color: string }>`
241
+ font-size: 7px;
242
+ color: ${({ $color }) => $color};
166
243
  font-family: 'Press Start 2P', cursive;
167
- grid-column: 1;
168
- grid-row: 1;
169
244
  `;
170
245
 
171
246
  const TxAmount = styled.span<{ $credit: boolean }>`
172
- font-size: 10px;
247
+ font-size: 8px;
173
248
  font-family: 'Press Start 2P', cursive;
174
249
  color: ${({ $credit }) => ($credit ? uiColors.green : uiColors.red)};
175
- font-weight: bold;
176
- grid-column: 2;
177
- grid-row: 1;
178
- text-align: right;
250
+ white-space: nowrap;
179
251
  `;
180
252
 
181
253
  const TxNote = styled.span`
182
- font-size: 7px;
254
+ font-size: 6px;
183
255
  color: ${uiColors.lightGray};
184
256
  font-family: 'Press Start 2P', cursive;
185
- grid-column: 1;
186
- grid-row: 2;
257
+ white-space: nowrap;
258
+ overflow: hidden;
259
+ text-overflow: ellipsis;
187
260
  `;
188
261
 
189
262
  const TxDate = styled.span`
190
- font-size: 7px;
191
- color: ${uiColors.lightGray};
192
- font-family: 'Press Start 2P', cursive;
193
- grid-column: 2;
194
- grid-row: 2;
195
- text-align: right;
196
- `;
197
-
198
- const TxBalance = styled.span`
199
- font-size: 7px;
200
- color: ${uiColors.white};
263
+ font-size: 6px;
264
+ color: rgba(255, 255, 255, 0.4);
201
265
  font-family: 'Press Start 2P', cursive;
202
- grid-column: 1 / 3;
203
- grid-row: 3;
204
- opacity: 0.7;
266
+ white-space: nowrap;
205
267
  `;
206
268
 
207
269
  const LoadingRow = styled.div`
@@ -219,8 +281,8 @@ const Spinner = styled.div`
219
281
  border: 3px solid rgba(255, 255, 255, 0.2);
220
282
  border-radius: 50%;
221
283
  border-top: 3px solid #f59e0b;
222
- width: 20px;
223
- height: 20px;
284
+ width: 16px;
285
+ height: 16px;
224
286
  animation: spin 0.8s linear infinite;
225
287
 
226
288
  @keyframes spin {
@@ -230,7 +292,7 @@ const Spinner = styled.div`
230
292
  `;
231
293
 
232
294
  const EmptyMessage = styled.div`
233
- font-size: 9px;
295
+ font-size: 8px;
234
296
  color: ${uiColors.lightGray};
235
297
  font-family: 'Press Start 2P', cursive;
236
298
  text-align: center;
@@ -1,4 +1,5 @@
1
- import React, { useCallback, useEffect, useRef, useState } from 'react';
1
+ import { debounce } from 'lodash';
2
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
3
  import { FaPaperPlane } from 'react-icons/fa';
3
4
  import styled from 'styled-components';
4
5
  import { uiColors } from '../../constants/uiColors';
@@ -6,6 +7,11 @@ import { ConfirmModal } from '../ConfirmModal';
6
7
  import { Input } from '../Input';
7
8
  import { CTAButton } from '../shared/CTAButton/CTAButton';
8
9
 
10
+ export interface IDCTransferCharacterResult {
11
+ _id: string;
12
+ name: string;
13
+ }
14
+
9
15
  export interface IDCTransferPanelProps {
10
16
  dcBalance: number;
11
17
  transferLoading: boolean;
@@ -15,6 +21,8 @@ export interface IDCTransferPanelProps {
15
21
  characterName?: string;
16
22
  onInputFocus?: () => void;
17
23
  onInputBlur?: () => void;
24
+ onSearchCharacter?: (name: string) => void;
25
+ searchResults?: IDCTransferCharacterResult[];
18
26
  }
19
27
 
20
28
  export const DCTransferPanel: React.FC<IDCTransferPanelProps> = ({
@@ -26,13 +34,33 @@ export const DCTransferPanel: React.FC<IDCTransferPanelProps> = ({
26
34
  characterName,
27
35
  onInputFocus,
28
36
  onInputBlur,
37
+ onSearchCharacter,
38
+ searchResults,
29
39
  }) => {
30
40
  const [recipientName, setRecipientName] = useState<string>('');
31
41
  const [amount, setAmount] = useState<string>('');
32
42
  const [validationError, setValidationError] = useState<string>('');
33
43
  const [showConfirm, setShowConfirm] = useState<boolean>(false);
44
+ const [showDropdown, setShowDropdown] = useState<boolean>(false);
45
+ const dropdownRef = useRef<HTMLDivElement>(null);
34
46
  const clearResultTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
35
47
 
48
+ const debouncedSearch = useMemo(
49
+ () =>
50
+ debounce((name: string) => {
51
+ if (name.trim().length >= 2) {
52
+ onSearchCharacter?.(name.trim());
53
+ }
54
+ }, 300),
55
+ [onSearchCharacter]
56
+ );
57
+
58
+ useEffect(() => {
59
+ return () => {
60
+ debouncedSearch.cancel();
61
+ };
62
+ }, [debouncedSearch]);
63
+
36
64
  useEffect(() => {
37
65
  if (transferResult) {
38
66
  if (clearResultTimerRef.current) {
@@ -93,15 +121,46 @@ export const DCTransferPanel: React.FC<IDCTransferPanelProps> = ({
93
121
  return (
94
122
  <PanelContainer>
95
123
  <FieldLabel>Recipient Character Name</FieldLabel>
96
- <StyledInput
97
- type="text"
98
- value={recipientName}
99
- onChange={(e) => setRecipientName(e.target.value)}
100
- onFocus={onInputFocus}
101
- onBlur={onInputBlur}
102
- placeholder="Enter character name"
103
- disabled={transferLoading}
104
- />
124
+ <AutocompleteWrapper ref={dropdownRef}>
125
+ <StyledInput
126
+ type="text"
127
+ value={recipientName}
128
+ onChange={(e) => {
129
+ setRecipientName(e.target.value);
130
+ debouncedSearch(e.target.value);
131
+ setShowDropdown(true);
132
+ }}
133
+ onFocus={() => {
134
+ onInputFocus?.();
135
+ if (recipientName.trim().length >= 2 && searchResults && searchResults.length > 0) {
136
+ setShowDropdown(true);
137
+ }
138
+ }}
139
+ onBlur={() => {
140
+ // Delay to allow dropdown click to register
141
+ setTimeout(() => setShowDropdown(false), 150);
142
+ onInputBlur?.();
143
+ }}
144
+ placeholder="Enter character name"
145
+ disabled={transferLoading}
146
+ autoComplete="off"
147
+ />
148
+ {showDropdown && searchResults && searchResults.length > 0 && (
149
+ <DropdownList>
150
+ {searchResults.map((char) => (
151
+ <DropdownItem
152
+ key={char._id}
153
+ onPointerDown={() => {
154
+ setRecipientName(char.name);
155
+ setShowDropdown(false);
156
+ }}
157
+ >
158
+ {char.name}
159
+ </DropdownItem>
160
+ ))}
161
+ </DropdownList>
162
+ )}
163
+ </AutocompleteWrapper>
105
164
 
106
165
  <FieldLabel>Amount (DC)</FieldLabel>
107
166
  <StyledInput
@@ -215,3 +274,36 @@ const ErrorMessage = styled.div`
215
274
  color: ${uiColors.red};
216
275
  font-family: 'Press Start 2P', cursive;
217
276
  `;
277
+
278
+ const AutocompleteWrapper = styled.div`
279
+ position: relative;
280
+ width: 100%;
281
+ `;
282
+
283
+ const DropdownList = styled.ul`
284
+ position: absolute;
285
+ top: 100%;
286
+ left: 0;
287
+ right: 0;
288
+ background: rgba(10, 10, 30, 0.95);
289
+ border: 1px solid #f59e0b;
290
+ border-top: none;
291
+ list-style: none;
292
+ padding: 0;
293
+ margin: 0;
294
+ max-height: 120px;
295
+ overflow-y: auto;
296
+ z-index: 10;
297
+ `;
298
+
299
+ const DropdownItem = styled.li`
300
+ padding: 6px 8px;
301
+ font-size: 11px;
302
+ font-family: 'Press Start 2P', cursive;
303
+ color: ${uiColors.white};
304
+ cursor: pointer;
305
+
306
+ &:hover {
307
+ background: rgba(245, 158, 11, 0.3);
308
+ }
309
+ `;
@@ -1,11 +1,11 @@
1
1
  import React, { useCallback, useState } from 'react';
2
- import { FaTimes } from 'react-icons/fa';
2
+ import { FaShoppingCart, FaTimes } from 'react-icons/fa';
3
3
  import styled from 'styled-components';
4
4
  import { uiColors } from '../../constants/uiColors';
5
5
  import ModalPortal from '../Abstractions/ModalPortal';
6
6
  import { InternalTabs } from '../InternalTabs/InternalTabs';
7
7
  import { DCHistoryPanel, IDCTransaction } from './DCHistoryPanel';
8
- import { DCTransferPanel } from './DCTransferPanel';
8
+ import { DCTransferPanel, IDCTransferCharacterResult } from './DCTransferPanel';
9
9
 
10
10
  type WalletTabId = 'balance' | 'transfer' | 'history';
11
11
 
@@ -22,6 +22,9 @@ export interface IDCWalletModalProps {
22
22
  characterName?: string;
23
23
  onInputFocus?: () => void;
24
24
  onInputBlur?: () => void;
25
+ onBuyDC?: () => void;
26
+ onSearchCharacter?: (name: string) => void;
27
+ searchResults?: IDCTransferCharacterResult[];
25
28
  }
26
29
 
27
30
  export const DCWalletModal: React.FC<IDCWalletModalProps> = ({
@@ -37,6 +40,9 @@ export const DCWalletModal: React.FC<IDCWalletModalProps> = ({
37
40
  characterName,
38
41
  onInputFocus,
39
42
  onInputBlur,
43
+ onBuyDC,
44
+ onSearchCharacter,
45
+ searchResults,
40
46
  }) => {
41
47
  const [activeTab, setActiveTab] = useState<WalletTabId>('balance');
42
48
 
@@ -55,7 +61,12 @@ export const DCWalletModal: React.FC<IDCWalletModalProps> = ({
55
61
  <BalanceContent>
56
62
  <BalanceLabel>Your DC Balance</BalanceLabel>
57
63
  <BalanceAmount>{dcBalance.toLocaleString()} DC</BalanceAmount>
58
- <BalanceHint>Buy DC packs from the Store &gt; Packs tab.</BalanceHint>
64
+ {onBuyDC && (
65
+ <BuyButton onPointerDown={onBuyDC} title="Buy Definya Coins">
66
+ <FaShoppingCart />
67
+ <BuyButtonLabel>Buy DC</BuyButtonLabel>
68
+ </BuyButton>
69
+ )}
59
70
  </BalanceContent>
60
71
  ),
61
72
  },
@@ -72,6 +83,8 @@ export const DCWalletModal: React.FC<IDCWalletModalProps> = ({
72
83
  characterName={characterName}
73
84
  onInputFocus={onInputFocus}
74
85
  onInputBlur={onInputBlur}
86
+ onSearchCharacter={onSearchCharacter}
87
+ searchResults={searchResults}
75
88
  />
76
89
  ),
77
90
  },
@@ -110,7 +123,12 @@ export const DCWalletModal: React.FC<IDCWalletModalProps> = ({
110
123
  <InternalTabs
111
124
  tabs={tabs}
112
125
  activeTab={activeTab}
113
- onTabChange={(tabId: string) => setActiveTab(tabId as WalletTabId)}
126
+ onTabChange={(tabId: string) => {
127
+ setActiveTab(tabId as WalletTabId);
128
+ if (tabId === 'history') {
129
+ onRequestHistory(1);
130
+ }
131
+ }}
114
132
  activeTextColor="#000000"
115
133
  activeColor="#fef08a"
116
134
  inactiveColor="#6b7280"
@@ -218,8 +236,29 @@ const BalanceAmount = styled.div`
218
236
  letter-spacing: 2px;
219
237
  `;
220
238
 
221
- const BalanceHint = styled.span`
239
+ const BuyButton = styled.button`
240
+ display: flex;
241
+ align-items: center;
242
+ gap: 8px;
243
+ margin-top: 8px;
244
+ padding: 10px 20px;
245
+ background: #f59e0b;
246
+ border: none;
247
+ border-radius: 6px;
248
+ color: #000;
249
+ cursor: pointer;
250
+ font-size: 1rem;
251
+
252
+ &:hover {
253
+ background: #fbbf24;
254
+ }
255
+
256
+ &:active {
257
+ background: #d97706;
258
+ }
259
+ `;
260
+
261
+ const BuyButtonLabel = styled.span`
222
262
  font-family: 'Press Start 2P', cursive;
223
- font-size: 7px;
224
- color: ${uiColors.lightGray};
263
+ font-size: 8px;
225
264
  `;
@@ -5,6 +5,7 @@ import styled from 'styled-components';
5
5
  import { ConfirmModal } from '../ConfirmModal';
6
6
  import { Dropdown } from '../Dropdown';
7
7
  import { Input } from '../Input';
8
+ import { MarketplaceBuyModal, MarketplacePaymentMethod } from './MarketplaceBuyModal';
8
9
  import { MarketplaceRows } from './MarketplaceRows';
9
10
  import { itemRarityOptions, itemTypeOptions, orderByOptions } from './filters';
10
11
 
@@ -26,11 +27,13 @@ export interface IBuyPanelProps {
26
27
  onChangePriceInput: (value: [number | undefined, number | undefined]) => void;
27
28
  scale?: number;
28
29
  equipmentSet?: IEquipmentSet | null;
29
- onMarketPlaceItemBuy?: (marketPlaceItemId: string) => void;
30
+ onMarketPlaceItemBuy?: (marketPlaceItemId: string, paymentMethod?: MarketplacePaymentMethod) => void;
30
31
  characterId: string;
31
32
  enableHotkeys?: () => void;
32
33
  disableHotkeys?: () => void;
33
34
  currentPage: number;
35
+ dcBalance?: number;
36
+ dcToGoldSwapRate?: number;
34
37
  }
35
38
 
36
39
  export const BuyPanel: React.FC<IBuyPanelProps> = ({
@@ -50,6 +53,8 @@ export const BuyPanel: React.FC<IBuyPanelProps> = ({
50
53
  enableHotkeys,
51
54
  disableHotkeys,
52
55
  currentPage,
56
+ dcBalance = 0,
57
+ dcToGoldSwapRate = 0,
53
58
  }) => {
54
59
  const [name, setName] = useState('');
55
60
  const [mainLevel, setMainLevel] = useState<
@@ -70,9 +75,30 @@ export const BuyPanel: React.FC<IBuyPanelProps> = ({
70
75
  itemsContainer.current?.scrollTo(0, 0);
71
76
  }, [currentPage]);
72
77
 
78
+ const buyingItem = buyingItemId
79
+ ? items.find(i => i._id === buyingItemId)
80
+ : null;
81
+ const hasDCBalance = dcBalance > 0 && dcToGoldSwapRate > 0;
82
+
83
+ const getDCEquivalentPrice = (goldPrice: number): number =>
84
+ dcToGoldSwapRate > 0 ? Math.ceil(goldPrice / dcToGoldSwapRate) : 0;
85
+
73
86
  return (
74
87
  <>
75
- {buyingItemId && (
88
+ {buyingItemId && buyingItem && hasDCBalance && (
89
+ <MarketplaceBuyModal
90
+ goldPrice={buyingItem.price}
91
+ dcEquivalentPrice={getDCEquivalentPrice(buyingItem.price)}
92
+ dcBalance={dcBalance}
93
+ onClose={() => setBuyingItemId(null)}
94
+ onConfirm={(paymentMethod) => {
95
+ onMarketPlaceItemBuy?.(buyingItemId, paymentMethod);
96
+ setBuyingItemId(null);
97
+ enableHotkeys?.();
98
+ }}
99
+ />
100
+ )}
101
+ {buyingItemId && !hasDCBalance && (
76
102
  <ConfirmModal
77
103
  onClose={setBuyingItemId.bind(null, null)}
78
104
  onConfirm={() => {
@@ -216,6 +242,7 @@ export const BuyPanel: React.FC<IBuyPanelProps> = ({
216
242
  atlasJSON={atlasJSON}
217
243
  item={item}
218
244
  itemPrice={price}
245
+ dcEquivalentPrice={dcToGoldSwapRate > 0 ? getDCEquivalentPrice(price) : undefined}
219
246
  equipmentSet={equipmentSet}
220
247
  onMarketPlaceItemBuy={setBuyingItemId.bind(null, _id)}
221
248
  disabled={owner === characterId}
@@ -5,6 +5,7 @@ import { Button, ButtonTypes } from '../Button';
5
5
  import { DraggableContainer } from '../DraggableContainer';
6
6
  import { Pager } from '../Pager';
7
7
  import { RPGUIContainerTypes } from '../RPGUI/RPGUIContainer';
8
+ import { MarketplacePaymentMethod } from './MarketplaceBuyModal';
8
9
  import { BuyPanel } from './BuyPanel';
9
10
  import { ManagmentPanel } from './ManagmentPanel';
10
11
 
@@ -26,7 +27,7 @@ export interface IMarketPlaceProps {
26
27
  onChangePriceInput: (value: [number | undefined, number | undefined]) => void;
27
28
  scale?: number;
28
29
  equipmentSet?: IEquipmentSet | null;
29
- onMarketPlaceItemBuy?: (marketPlaceItemId: string) => void;
30
+ onMarketPlaceItemBuy?: (marketPlaceItemId: string, paymentMethod?: MarketplacePaymentMethod) => void;
30
31
  onMarketPlaceItemRemove?: (marketPlaceItemId: string) => void;
31
32
  availableGold: number;
32
33
  selectedItemToSell: IItem | null;
@@ -41,6 +42,8 @@ export interface IMarketPlaceProps {
41
42
  currentPage: number;
42
43
  itemsPerPage: number;
43
44
  onPageChange: (page: number) => void;
45
+ dcBalance?: number;
46
+ dcToGoldSwapRate?: number;
44
47
  }
45
48
 
46
49
  export const Marketplace: React.FC<IMarketPlaceProps> = props => {