@rpg-engine/long-bow 0.5.13 → 0.5.15

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.
@@ -0,0 +1,6 @@
1
+ import { Meta } from '@storybook/react';
2
+ import { IChatRevampProps } from '../components/ChatRevamp/ChatRevamp';
3
+ declare const meta: Meta;
4
+ export default meta;
5
+ export declare const Default: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react").ReactFramework, IChatRevampProps>;
6
+ export declare const PrivateCharacters: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react").ReactFramework, IChatRevampProps>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpg-engine/long-bow",
3
- "version": "0.5.13",
3
+ "version": "0.5.15",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -83,7 +83,7 @@
83
83
  },
84
84
  "dependencies": {
85
85
  "@rollup/plugin-image": "^2.1.1",
86
- "@rpg-engine/shared": "^0.8.74",
86
+ "@rpg-engine/shared": "^0.8.83",
87
87
  "dayjs": "^1.11.2",
88
88
  "font-awesome": "^4.7.0",
89
89
  "fs-extra": "^10.1.0",
@@ -1,4 +1,4 @@
1
- import { IChatMessage } from '@rpg-engine/shared';
1
+ import { IChatMessage, IPrivateChatMessage } from '@rpg-engine/shared';
2
2
  import dayjs from 'dayjs';
3
3
  import React, { useEffect, useState } from 'react';
4
4
  import { ErrorBoundary } from 'react-error-boundary';
@@ -11,7 +11,7 @@ interface IEmitter {
11
11
  name: string;
12
12
  }
13
13
 
14
- interface IStyles {
14
+ export interface IStyles {
15
15
  textColor?: string;
16
16
  buttonColor?: string;
17
17
  buttonBackgroundColor?: string;
@@ -20,7 +20,7 @@ interface IStyles {
20
20
  }
21
21
 
22
22
  export interface IChatProps {
23
- chatMessages: IChatMessage[];
23
+ chatMessages: IChatMessage[] | IPrivateChatMessage[];
24
24
  onSendChatMessage: (message: string) => void;
25
25
  onCloseButton: () => void;
26
26
  onFocus?: () => void;
@@ -80,11 +80,20 @@ export const Chat: React.FC<IChatProps> = ({
80
80
  } ${message}`;
81
81
  };
82
82
 
83
- const onRenderChatMessages = (chatMessages: IChatMessage[]) => {
83
+ const onRenderChatMessages = (
84
+ chatMessages: (IChatMessage | IPrivateChatMessage)[]
85
+ ) => {
84
86
  return chatMessages?.length ? (
85
- chatMessages?.map(({ _id, createdAt, emitter, message }, index) => (
86
- <Message color={styles?.textColor || '#c65102'} key={`${_id}_${index}`}>
87
- {onRenderMessageLines(emitter, createdAt as string, message)}
87
+ chatMessages?.map((chatMessage, index) => (
88
+ <Message
89
+ color={styles?.textColor || '#c65102'}
90
+ key={`${chatMessage._id}_${index}`}
91
+ >
92
+ {onRenderMessageLines(
93
+ chatMessage.emitter,
94
+ chatMessage.createdAt as string,
95
+ chatMessage.message
96
+ )}
88
97
  </Message>
89
98
  ))
90
99
  ) : (
@@ -0,0 +1,121 @@
1
+ import {
2
+ ICharacter,
3
+ IChatMessage,
4
+ IPrivateChatMessage,
5
+ } from '@rpg-engine/shared';
6
+ import React, { useEffect, useState } from 'react';
7
+ import styled from 'styled-components';
8
+ import { uiColors } from '../../constants/uiColors';
9
+ import { Chat, IStyles } from '../Chat/Chat';
10
+ import { SearchCharacter } from './SearchCharacter';
11
+
12
+ export interface ITabStyles {
13
+ width?: string;
14
+ height?: string;
15
+ }
16
+
17
+ export type PrivateChatCharacter = Pick<ICharacter, '_id' | 'name'>;
18
+
19
+ export interface IChatRevampProps {
20
+ chatMessages: IChatMessage[] | IPrivateChatMessage[];
21
+ onSendGlobalChatMessage: (message: string) => void;
22
+ onCloseButton: () => void;
23
+ onFocus?: () => void;
24
+ onBlur?: () => void;
25
+ opacity?: number;
26
+ styles?: IStyles;
27
+ tabs: { label: string; id: string }[];
28
+ activeTab: string;
29
+ onChangeTab: (tabId: string) => void;
30
+ privateChatCharacters?: PrivateChatCharacter[];
31
+ onChangeCharacterName: (characterName: string) => void;
32
+ onCharacterClick?: (character: PrivateChatCharacter) => void;
33
+ onSendPrivateChatMessage: (message: string) => void;
34
+ }
35
+
36
+ export const ChatRevamp = ({
37
+ chatMessages,
38
+ onSendGlobalChatMessage,
39
+ onChangeCharacterName,
40
+ onFocus,
41
+ onBlur,
42
+ onCloseButton,
43
+ styles,
44
+ tabs,
45
+ onChangeTab,
46
+ activeTab,
47
+ privateChatCharacters,
48
+ onCharacterClick,
49
+ onSendPrivateChatMessage,
50
+ }: IChatRevampProps) => {
51
+ const [showSearchCharacter, setShowSearchCharacter] = useState(true);
52
+
53
+ useEffect(() => {
54
+ setShowSearchCharacter(true);
55
+ }, [activeTab]);
56
+
57
+ const isPrivate = activeTab === 'private';
58
+
59
+ return (
60
+ <>
61
+ <TabContainer>
62
+ {tabs.map((tab, index) => (
63
+ <Tab
64
+ key={`${tab.label}_${index}`}
65
+ active={tab.id === activeTab}
66
+ onPointerDown={() => onChangeTab(tab.id)}
67
+ >
68
+ {tab.label}
69
+ </Tab>
70
+ ))}
71
+ </TabContainer>
72
+ {isPrivate && showSearchCharacter ? (
73
+ <SearchCharacter
74
+ onFocus={onFocus}
75
+ onBlur={onBlur}
76
+ onChangeCharacterName={onChangeCharacterName}
77
+ styles={styles}
78
+ recentCharacters={privateChatCharacters}
79
+ setShowSearchCharacter={setShowSearchCharacter}
80
+ onCharacterClick={onCharacterClick}
81
+ />
82
+ ) : (
83
+ <Chat
84
+ chatMessages={chatMessages}
85
+ onSendChatMessage={
86
+ isPrivate ? onSendPrivateChatMessage : onSendGlobalChatMessage
87
+ }
88
+ sendMessage={true}
89
+ onCloseButton={onCloseButton}
90
+ styles={styles}
91
+ onFocus={onFocus}
92
+ onBlur={onBlur}
93
+ />
94
+ )}
95
+ </>
96
+ );
97
+ };
98
+
99
+ const TabContainer = styled.div`
100
+ width: 100%;
101
+ display: flex;
102
+ gap: 10px;
103
+ `;
104
+
105
+ const Tab = styled.button<{ active: boolean }>`
106
+ width: 120px;
107
+ color: white;
108
+ font-size: 0.8rem;
109
+ all: unset;
110
+ padding: 0.6rem;
111
+ font-size: 0.8rem;
112
+
113
+ border-radius: 5px 5px 0 0;
114
+ border-width: 0.25rem 0.25rem 0 0.25rem;
115
+ border-style: solid;
116
+ border-color: ${props => (props.active ? '#c65102' : uiColors.gray)};
117
+
118
+ background-color: ${props =>
119
+ props.active ? uiColors.orange : 'transparent'};
120
+ color: ${props => (props.active ? 'white' : uiColors.raisinBlack)};
121
+ `;
@@ -0,0 +1,163 @@
1
+ import React, { useState } from 'react';
2
+ import { RxMagnifyingGlass } from 'react-icons/rx';
3
+ import styled from 'styled-components';
4
+ import { uiColors } from '../../constants/uiColors';
5
+ import { uiFonts } from '../../constants/uiFonts';
6
+ import { IStyles } from '../Chat/Chat';
7
+ import { Column } from '../shared/Column';
8
+ import type { PrivateChatCharacter } from './ChatRevamp';
9
+
10
+ interface ISearchCharacterProps {
11
+ onFocus?: () => void;
12
+ onBlur?: () => void;
13
+ onChangeCharacterName: (characterName: string) => void;
14
+ styles?: IStyles;
15
+ recentCharacters?: PrivateChatCharacter[];
16
+ setShowSearchCharacter: (show: boolean) => void;
17
+ onCharacterClick?: (character: PrivateChatCharacter) => void;
18
+ }
19
+
20
+ export const SearchCharacter = ({
21
+ onChangeCharacterName,
22
+ onBlur,
23
+ onFocus,
24
+ recentCharacters,
25
+ setShowSearchCharacter,
26
+ onCharacterClick,
27
+ styles = {
28
+ textColor: '#c65102',
29
+ buttonColor: '#005b96',
30
+ buttonBackgroundColor: 'rgba(0,0,0,.2)',
31
+ width: '80%',
32
+ height: 'auto',
33
+ },
34
+ }: ISearchCharacterProps) => {
35
+ const [characterName, setCharacterName] = useState('');
36
+
37
+ const handleSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
38
+ event.preventDefault();
39
+ if (!characterName || characterName.trim() === '') return;
40
+ onChangeCharacterName(characterName);
41
+ };
42
+
43
+ const handleCharacterClick = (
44
+ character: PrivateChatCharacter
45
+ ) => {
46
+ if (!onCharacterClick) return;
47
+ setCharacterName('');
48
+ onCharacterClick(character);
49
+ setShowSearchCharacter(false);
50
+ };
51
+
52
+ return (
53
+ <SearchCharacterContainer
54
+ width={styles?.width || '80%'}
55
+ height={styles?.height || 'auto'}
56
+ >
57
+ <Form onSubmit={handleSubmit}>
58
+ <Column flex={70}>
59
+ <TextField
60
+ value={characterName}
61
+ id="inputCharacterName"
62
+ onChange={e => {
63
+ setCharacterName(e.target.value);
64
+ onChangeCharacterName(e.target.value);
65
+ }}
66
+ height={20}
67
+ type="text"
68
+ autoComplete="off"
69
+ onFocus={onFocus}
70
+ onBlur={onBlur}
71
+ onPointerDown={onFocus}
72
+ autoFocus
73
+ />
74
+ </Column>
75
+ <Column justifyContent="flex-end">
76
+ <SearchButton
77
+ buttonColor={styles?.buttonColor || '#005b96'}
78
+ buttonBackgroundColor={
79
+ styles?.buttonBackgroundColor || 'rgba(0,0,0,.5)'
80
+ }
81
+ id="chat-send-button"
82
+ style={{ borderRadius: '20%' }}
83
+ >
84
+ <RxMagnifyingGlass size={15} />
85
+ </SearchButton>
86
+ </Column>
87
+ </Form>
88
+
89
+ {recentCharacters && recentCharacters.length > 0 && (
90
+ <ListContainer>
91
+ {recentCharacters.map(character => (
92
+ <ListElement onPointerDown={() => handleCharacterClick(character)} key={character._id}>
93
+ {character.name}
94
+ </ListElement>
95
+ ))}
96
+ </ListContainer>
97
+ )}
98
+ </SearchCharacterContainer>
99
+ );
100
+ };
101
+
102
+ interface IContainerProps {
103
+ width: string;
104
+ height: string;
105
+ }
106
+
107
+ interface IButtonProps {
108
+ buttonColor: string;
109
+ buttonBackgroundColor: string;
110
+ }
111
+
112
+ const SearchCharacterContainer = styled.div<IContainerProps>`
113
+ height: ${props => props.height};
114
+ width: ${({ width }) => width};
115
+ padding: 10px;
116
+ background-color: rgba(0, 0, 0, 0.2);
117
+ height: auto;
118
+ `;
119
+
120
+ const Form = styled.form`
121
+ display: flex;
122
+ width: 100%;
123
+ justify-content: center;
124
+ align-items: center;
125
+ `;
126
+
127
+ const TextField = styled.input`
128
+ width: 100%;
129
+ background-color: rgba(0, 0, 0, 0.25) !important;
130
+ border: none !important;
131
+ max-height: 28px !important;
132
+ `;
133
+
134
+ const SearchButton = styled.button<IButtonProps>`
135
+ color: ${({ buttonColor }) => buttonColor};
136
+ background-color: ${({ buttonBackgroundColor }) => buttonBackgroundColor};
137
+ width: 28px;
138
+ height: 28px;
139
+ border: none !important;
140
+ `;
141
+
142
+ const ListContainer = styled.ul`
143
+ border: none;
144
+ overflow-y: scroll;
145
+ list-style: none;
146
+ padding: 0;
147
+ `;
148
+
149
+ const ListElement = styled.li`
150
+ margin: 0.5rem 0 !important;
151
+ font-size: ${uiFonts.size.small};
152
+
153
+ &:hover {
154
+ color: #ff0;
155
+ background-color: ${uiColors.darkGray};
156
+ }
157
+
158
+ button {
159
+ all: unset;
160
+ }
161
+ `;
162
+
163
+
@@ -13,7 +13,6 @@ import { ItemQuantitySelector } from './ItemQuantitySelector';
13
13
 
14
14
  import { IPosition } from '../../../types/eventTypes';
15
15
  import ModalPortal from '../../Abstractions/ModalPortal';
16
- import SelectArrow from '../../Arrow/SelectArrow';
17
16
  import { ShortcutsSetter } from '../../Shortcuts/ShortcutsSetter';
18
17
  import { ItemSlot } from './ItemSlot';
19
18
 
@@ -82,15 +81,12 @@ export const ItemContainer: React.FC<IItemContainerProps> = ({
82
81
  onPositionChangeEnd,
83
82
  onPositionChangeStart,
84
83
  }) => {
85
- const MAX_SLOTS_PER_PAGE = 20;
86
84
  const [quantitySelect, setQuantitySelect] = useState({
87
85
  isOpen: false,
88
86
  maxQuantity: 1,
89
87
  callback: (_quantity: number) => { },
90
88
  });
91
89
  const [settingShortcutIndex, setSettingShortcutIndex] = useState(-1);
92
- const [currentPage, setCurrentPage] = useState(1);
93
- const totalPages = Math.ceil(itemContainer.slotQty / MAX_SLOTS_PER_PAGE);
94
90
 
95
91
  const handleSetShortcut = (item: IItem, index: number) => {
96
92
  if (item.type === ItemType.Consumable || item.type === ItemType.Tool) {
@@ -100,11 +96,8 @@ export const ItemContainer: React.FC<IItemContainerProps> = ({
100
96
 
101
97
  const onRenderSlots = () => {
102
98
  const slots = [];
103
- const start = (currentPage - 1) * MAX_SLOTS_PER_PAGE;
104
- const end = start + MAX_SLOTS_PER_PAGE;
105
99
 
106
- for (let i = start; i < end && i < itemContainer.slotQty; i++) {
107
- console.log(itemContainer)
100
+ for (let i = 0; i < itemContainer.slotQty; i++) {
108
101
  slots.push(
109
102
  <ItemSlot
110
103
  isContextMenuDisabled={disableContextMenu}
@@ -163,14 +156,6 @@ export const ItemContainer: React.FC<IItemContainerProps> = ({
163
156
  return slots;
164
157
  };
165
158
 
166
- const goToNextPage = () => {
167
- setCurrentPage(current => Math.min(current + 1, totalPages));
168
- };
169
-
170
- const goToPreviousPage = () => {
171
- setCurrentPage(current => Math.max(current - 1, 1));
172
- };
173
-
174
159
  return (
175
160
  <>
176
161
  <SlotsContainer
@@ -196,32 +181,6 @@ export const ItemContainer: React.FC<IItemContainerProps> = ({
196
181
  <ItemsContainer className="item-container-body">
197
182
  {onRenderSlots()}
198
183
  </ItemsContainer>
199
- {totalPages > 1 && (
200
- <ArrowContainer>
201
- {currentPage > 1 && (
202
- <SelectArrow
203
- className='arrow .arrow-up'
204
- direction="left"
205
- onPointerDown={() => {
206
- if (currentPage > 1) {
207
- goToPreviousPage();
208
- }
209
- }}
210
- />
211
- )}
212
- {currentPage < totalPages && (
213
- <SelectArrow
214
- className='arrow .arrow-down'
215
- direction="right"
216
- onPointerDown={() => {
217
- if (currentPage < totalPages) {
218
- goToNextPage();
219
- }
220
- }}
221
- />
222
- )}
223
- </ArrowContainer>
224
- )}
225
184
  </SlotsContainer>
226
185
  {quantitySelect.isOpen && (
227
186
  <ModalPortal>
@@ -256,6 +215,8 @@ const ItemsContainer = styled.div`
256
215
  display: flex;
257
216
  justify-content: center;
258
217
  flex-wrap: wrap;
218
+ max-height: 270px; /* Defina a altura máxima desejada */
219
+ overflow-y: auto; /* Adicione uma barra de rolagem vertical quando necessário */
259
220
  `;
260
221
 
261
222
  const QuantitySelectorContainer = styled.div`
@@ -270,17 +231,3 @@ const QuantitySelectorContainer = styled.div`
270
231
  align-items: center;
271
232
  background-color: rgba(0, 0, 0, 0.5);
272
233
  `;
273
-
274
- const ArrowContainer = styled.div`
275
- margin-top: 10px;
276
- margin-bottom: 30px;
277
- /* Aplica a margem ao primeiro span (seta para a esquerda) */
278
- span:first-child {
279
- margin-left: 20px;
280
- }
281
-
282
- /* Aplica a margem ao último span (seta para a direita) */
283
- span:last-child {
284
- margin-right: 20px;
285
- }
286
- `;
@@ -39,6 +39,8 @@ const CustomTab = styled.div<ICustomTab>`
39
39
  border-left: 0.25rem solid rgba(0, 0, 0, 0.25);
40
40
  border-right: 0.25rem solid rgba(0, 0, 0, 0.25);
41
41
  border-top: 0.25rem solid rgba(0, 0, 0, 0.25);
42
+ border-bottom: ${props =>
43
+ props.activeTab ? '' : '0.25rem solid rgba(0, 0, 0, 0.25)'};
42
44
  background: ${props => (props.activeTab ? '#4E4A4E' : '#2b292b')};
43
45
  }
44
46
 
@@ -46,6 +48,7 @@ const CustomTab = styled.div<ICustomTab>`
46
48
  border-left: 0.25rem solid #996d55;
47
49
  border-right: 0.25rem solid #996d55;
48
50
  border-top: 0.25rem solid #996d55;
51
+ border-bottom: ${props => (props.activeTab ? '' : '0.25rem solid #996d55')};
49
52
  background: ${props => (props.activeTab ? '#BF886A' : '#B67051')};
50
53
  }
51
54
 
@@ -59,8 +62,6 @@ const CustomTab = styled.div<ICustomTab>`
59
62
 
60
63
  border-radius: 5px 5px 0 0;
61
64
 
62
- z-index: ${props => (props.activeTab ? 1 : -1)};
63
-
64
65
  position: relative;
65
66
  top: 0.3rem;
66
67
  `;
@@ -1,13 +1,27 @@
1
1
  import React from 'react';
2
2
  import styled from 'styled-components';
3
+ import { IStyles } from '../Chat/Chat';
3
4
 
4
5
  interface IProps {
5
6
  id: string;
6
7
  children: React.ReactNode;
8
+ styles?: IStyles;
7
9
  }
8
10
 
9
- export const TabBody: React.FC<IProps> = ({ id, children }) => {
10
- return <Container data-tab-id={id}>{children}</Container>;
11
+ export const TabBody: React.FC<IProps> = ({ id, children, styles }) => {
12
+ return (
13
+ <Container styles={styles} data-tab-id={id}>
14
+ {children}
15
+ </Container>
16
+ );
11
17
  };
12
18
 
13
- const Container = styled.div``;
19
+ interface IContainerProps {
20
+ styles?: IStyles;
21
+ }
22
+
23
+ const Container = styled.div<IContainerProps>`
24
+ width: 100%;
25
+ height: ${props => props.styles?.height || 'auto'};
26
+ overflow-y: auto;
27
+ `;
@@ -42,9 +42,9 @@ export const TabsContainer: React.FC<ITabsContainer> = ({
42
42
 
43
43
  const onGetContainerType = () => {
44
44
  switch (type) {
45
- case 'brown':
45
+ case MultitabType.Brown:
46
46
  return RPGUIContainerTypes.FramedGold;
47
- case 'gray':
47
+ case MultitabType.Gray:
48
48
  return RPGUIContainerTypes.Framed;
49
49
  default:
50
50
  return RPGUIContainerTypes.Framed;
@@ -22,7 +22,8 @@ export const mockSpells: ISpell[] = [
22
22
  castingAnimationKey: AnimationEffectKeys.SkillLevelUp,
23
23
  targetHitAnimationKey: AnimationEffectKeys.Rooted,
24
24
  projectileAnimationKey: AnimationEffectKeys.Energy,
25
- usableEffect: () => {},
25
+ usableEffect: () => { },
26
+ onlyPremiumAccountType: [],
26
27
  },
27
28
  {
28
29
  key: (SpellsBlueprint.ArrowCreationSpell + '2') as SpellsBlueprint,
@@ -40,7 +41,8 @@ export const mockSpells: ISpell[] = [
40
41
  castingAnimationKey: AnimationEffectKeys.SkillLevelUp,
41
42
  targetHitAnimationKey: AnimationEffectKeys.Rooted,
42
43
  projectileAnimationKey: AnimationEffectKeys.Energy,
43
- usableEffect: () => {},
44
+ usableEffect: () => { },
45
+ onlyPremiumAccountType: [],
44
46
  },
45
47
  {
46
48
  key: (SpellsBlueprint.ArrowCreationSpell + '3') as SpellsBlueprint,
@@ -58,7 +60,8 @@ export const mockSpells: ISpell[] = [
58
60
  castingAnimationKey: AnimationEffectKeys.SkillLevelUp,
59
61
  targetHitAnimationKey: AnimationEffectKeys.Rooted,
60
62
  projectileAnimationKey: AnimationEffectKeys.Energy,
61
- usableEffect: () => {},
63
+ usableEffect: () => { },
64
+ onlyPremiumAccountType: [],
62
65
  },
63
66
  {
64
67
  key: (SpellsBlueprint.ArrowCreationSpell + '4') as SpellsBlueprint,
@@ -76,7 +79,8 @@ export const mockSpells: ISpell[] = [
76
79
  castingAnimationKey: AnimationEffectKeys.SkillLevelUp,
77
80
  targetHitAnimationKey: AnimationEffectKeys.Rooted,
78
81
  projectileAnimationKey: AnimationEffectKeys.Energy,
79
- usableEffect: () => {},
82
+ usableEffect: () => { },
83
+ onlyPremiumAccountType: [],
80
84
  },
81
85
  {
82
86
  key: (SpellsBlueprint.ArrowCreationSpell + '5') as SpellsBlueprint,
@@ -94,6 +98,7 @@ export const mockSpells: ISpell[] = [
94
98
  castingAnimationKey: AnimationEffectKeys.SkillLevelUp,
95
99
  targetHitAnimationKey: AnimationEffectKeys.Rooted,
96
100
  projectileAnimationKey: AnimationEffectKeys.Energy,
97
- usableEffect: () => {},
101
+ usableEffect: () => { },
102
+ onlyPremiumAccountType: [],
98
103
  },
99
104
  ];
@@ -2,6 +2,7 @@ import {
2
2
  getItemTextureKeyPath,
3
3
  IEquipmentSet,
4
4
  ITradeResponseItem,
5
+ UserAccountTypes,
5
6
  } from '@rpg-engine/shared';
6
7
  import capitalize from 'lodash/capitalize';
7
8
  import React from 'react';
@@ -23,6 +24,7 @@ export interface ITradeComponentProps {
23
24
  selectedQty: number;
24
25
  equipmentSet?: IEquipmentSet | null;
25
26
  scale?: number;
27
+ isBuy?: boolean;
26
28
  }
27
29
 
28
30
  const outerQty = 10;
@@ -35,6 +37,7 @@ export const TradingItemRow: React.FC<ITradeComponentProps> = ({
35
37
  selectedQty,
36
38
  equipmentSet,
37
39
  scale,
40
+ isBuy,
38
41
  }) => {
39
42
  const onLeftClick = (qty = 1) => {
40
43
  onQuantityChange(traderItem, Math.max(0, selectedQty - qty));
@@ -47,6 +50,46 @@ export const TradingItemRow: React.FC<ITradeComponentProps> = ({
47
50
  );
48
51
  };
49
52
 
53
+ const renderAccountTypeIndicator = () => {
54
+ if (isBuy && traderItem.canBePurchasedOnlyByPremiumPlans) {
55
+ return traderItem.canBePurchasedOnlyByPremiumPlans.map(accountType => {
56
+ if (accountType !== UserAccountTypes.Free) {
57
+ let backgroundColor;
58
+ let textColor = 'black';
59
+
60
+ switch (accountType) {
61
+ case UserAccountTypes.PremiumBronze:
62
+ backgroundColor = '#CD7F32';
63
+ break;
64
+ case UserAccountTypes.PremiumSilver:
65
+ backgroundColor = '#C0C0C0';
66
+ break;
67
+ case UserAccountTypes.PremiumGold:
68
+ backgroundColor = '#FFD700';
69
+ break;
70
+ case UserAccountTypes.PremiumUltimate:
71
+ backgroundColor = '#002E99';
72
+ break;
73
+ default:
74
+ return null;
75
+ }
76
+
77
+ return (
78
+ <PremiumLabel
79
+ backgroundColor={backgroundColor}
80
+ textColor={textColor}
81
+ key={accountType}
82
+ >
83
+ {capitalize(accountType) + ' PA'}
84
+ </PremiumLabel>
85
+ );
86
+ }
87
+ return null;
88
+ });
89
+ }
90
+ return null;
91
+ };
92
+
50
93
  return (
51
94
  <ItemWrapper>
52
95
  <ItemIconContainer>
@@ -84,6 +127,7 @@ export const TradingItemRow: React.FC<ITradeComponentProps> = ({
84
127
  </Ellipsis>
85
128
  </p>
86
129
  <p>${traderItem.price}</p>
130
+ <p>{renderAccountTypeIndicator()}</p>
87
131
  </NameValue>
88
132
  </ItemNameContainer>
89
133
  <QuantityContainer>
@@ -121,6 +165,20 @@ export const TradingItemRow: React.FC<ITradeComponentProps> = ({
121
165
  );
122
166
  };
123
167
 
168
+ const PremiumLabel = styled.span<{
169
+ backgroundColor: string;
170
+ textColor: string;
171
+ }>`
172
+ background-color: ${({ backgroundColor }) => backgroundColor};
173
+ color: ${({ textColor }) => textColor};
174
+ font-weight: bold;
175
+ padding: 2px 5px;
176
+ border-radius: 5px;
177
+ margin-right: 5px;
178
+ margin-bottom: 5px;
179
+ display: inline-block;
180
+ `;
181
+
124
182
  const StyledArrow = styled(SelectArrow)`
125
183
  margin: 40px;
126
184
  `;
@@ -110,6 +110,7 @@ export const TradingMenu: React.FC<ITradingMenu> = ({
110
110
  selectedQty={qtyMap.get(tradeItem.key) ?? 0}
111
111
  equipmentSet={equipmentSet}
112
112
  scale={scale}
113
+ isBuy={isBuy()}
113
114
  />
114
115
  </ItemWrapper>
115
116
  ))}