@rpg-engine/long-bow 0.8.7 → 0.8.9

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 (70) hide show
  1. package/dist/components/InformationCenter/sections/bestiary/{BestiarySection.d.ts → InformationCenterBestiarySection.d.ts} +1 -1
  2. package/dist/components/InformationCenter/sections/faq/{FaqSection.d.ts → InformationCenterFaqSection.d.ts} +1 -1
  3. package/dist/components/InformationCenter/sections/items/{ItemsSection.d.ts → InformationCenterItemsSection.d.ts} +1 -1
  4. package/dist/components/InformationCenter/sections/tutorials/{TutorialsSection.d.ts → InformationCenterTutorialsSection.d.ts} +2 -1
  5. package/dist/components/Item/Inventory/ItemPropertyColorSelector.d.ts +10 -0
  6. package/dist/components/Item/Inventory/ItemPropertySimpleHandler.d.ts +10 -0
  7. package/dist/components/Store/CartView.d.ts +15 -0
  8. package/dist/components/Store/StoreItemDetails.d.ts +16 -0
  9. package/dist/components/Store/StoreItemRow.d.ts +1 -2
  10. package/dist/components/Store/StoreTypes.d.ts +33 -4
  11. package/dist/components/Store/hooks/useStoreCart.d.ts +14 -0
  12. package/dist/components/Store/sections/StoreItemsSection.d.ts +12 -0
  13. package/dist/components/Store/sections/StorePacksSection.d.ts +9 -0
  14. package/dist/components/shared/CTAButton/CTAButton.d.ts +13 -0
  15. package/dist/components/shared/Card/Card.d.ts +14 -0
  16. package/dist/components/shared/Ellipsis.d.ts +1 -1
  17. package/dist/components/shared/PaginatedContent/PaginatedContent.d.ts +3 -1
  18. package/dist/components/shared/ScrollableContent/ScrollableContent.d.ts +23 -0
  19. package/dist/components/shared/SearchBar/SearchBar.d.ts +2 -3
  20. package/dist/components/shared/SearchHeader/SearchHeader.d.ts +17 -0
  21. package/dist/components/shared/ShoppingCart/CartCard.d.ts +14 -0
  22. package/dist/components/shared/ShoppingCart/CartCardHorizontal.d.ts +13 -0
  23. package/dist/index.d.ts +1 -0
  24. package/dist/long-bow.cjs.development.js +105 -39
  25. package/dist/long-bow.cjs.development.js.map +1 -1
  26. package/dist/long-bow.cjs.production.min.js +1 -1
  27. package/dist/long-bow.cjs.production.min.js.map +1 -1
  28. package/dist/long-bow.esm.js +105 -40
  29. package/dist/long-bow.esm.js.map +1 -1
  30. package/dist/stories/UI/buttonsAndInputs/CTAButton.stories.d.ts +18 -0
  31. package/dist/stories/UI/dropdownsAndSelectors/ItemPropertyColorSelector.stories.d.ts +3 -0
  32. package/package.json +3 -2
  33. package/src/components/InformationCenter/InformationCenter.tsx +8 -8
  34. package/src/components/InformationCenter/InformationCenterTabView.tsx +0 -1
  35. package/src/components/InformationCenter/sections/bestiary/{BestiarySection.tsx → InformationCenterBestiarySection.tsx} +2 -1
  36. package/src/components/InformationCenter/sections/faq/InformationCenterFaqSection.tsx +81 -0
  37. package/src/components/InformationCenter/sections/items/{ItemsSection.tsx → InformationCenterItemsSection.tsx} +2 -10
  38. package/src/components/InformationCenter/sections/tutorials/InformationCenterTutorialsSection.tsx +135 -0
  39. package/src/components/Item/Inventory/ItemPropertyColorSelector.tsx +75 -0
  40. package/src/components/Item/Inventory/ItemPropertySimpleHandler.tsx +26 -0
  41. package/src/components/Item/Inventory/itemContainerHelper.ts +10 -1
  42. package/src/components/Store/CartView.tsx +271 -0
  43. package/src/components/Store/Store.tsx +199 -96
  44. package/src/components/Store/StoreItemDetails.tsx +161 -0
  45. package/src/components/Store/StoreItemRow.tsx +24 -40
  46. package/src/components/Store/StoreTypes.ts +38 -4
  47. package/src/components/Store/hooks/useStoreCart.ts +121 -0
  48. package/src/components/Store/sections/StoreItemsSection.tsx +52 -0
  49. package/src/components/Store/sections/StorePacksSection.tsx +89 -0
  50. package/src/components/Store/sections/images/custom-skin.png +0 -0
  51. package/src/components/shared/CTAButton/CTAButton.tsx +127 -0
  52. package/src/components/shared/Card/Card.tsx +107 -0
  53. package/src/components/shared/Ellipsis.tsx +20 -22
  54. package/src/components/shared/PaginatedContent/PaginatedContent.tsx +48 -79
  55. package/src/components/shared/ScrollableContent/ScrollableContent.tsx +160 -0
  56. package/src/components/shared/SearchBar/SearchBar.tsx +43 -24
  57. package/src/components/shared/SearchHeader/SearchHeader.tsx +80 -0
  58. package/src/components/shared/ShoppingCart/CartCard.tsx +116 -0
  59. package/src/components/shared/ShoppingCart/CartCardHorizontal.tsx +120 -0
  60. package/src/components/shared/SpriteFromAtlas.tsx +2 -0
  61. package/src/index.tsx +1 -0
  62. package/src/stories/Features/store/Store.stories.tsx +54 -4
  63. package/src/stories/UI/buttonsAndInputs/CTAButton.stories.tsx +77 -0
  64. package/src/stories/UI/dropdownsAndSelectors/ItemPropertyColorSelector.stories.tsx +77 -0
  65. package/dist/components/Store/InternalStoreTab.d.ts +0 -15
  66. package/dist/components/Store/StoreTabContent.d.ts +0 -14
  67. package/src/components/InformationCenter/sections/faq/FaqSection.tsx +0 -51
  68. package/src/components/InformationCenter/sections/tutorials/TutorialsSection.tsx +0 -144
  69. package/src/components/Store/InternalStoreTab.tsx +0 -142
  70. package/src/components/Store/StoreTabContent.tsx +0 -46
@@ -0,0 +1,271 @@
1
+ import React, { useState } from 'react';
2
+ import { FaShoppingBag, FaTimes, FaTrash } from 'react-icons/fa';
3
+ import styled from 'styled-components';
4
+ import { CTAButton } from '../shared/CTAButton/CTAButton';
5
+ import { SpriteFromAtlas } from '../shared/SpriteFromAtlas';
6
+ import { IStoreItem } from './StoreTypes';
7
+
8
+ interface ICartViewProps {
9
+ cartItems: { item: IStoreItem; quantity: number }[];
10
+ onRemoveFromCart: (itemKey: string) => void;
11
+ onClose: () => void;
12
+ onPurchase: () => Promise<boolean>;
13
+ atlasJSON: Record<string, any>;
14
+ atlasIMG: string;
15
+ }
16
+
17
+ export const CartView: React.FC<ICartViewProps> = ({
18
+ cartItems,
19
+ onRemoveFromCart,
20
+ onClose,
21
+ onPurchase,
22
+ atlasJSON,
23
+ atlasIMG,
24
+ }) => {
25
+ const [isLoading, setIsLoading] = useState(false);
26
+ const [error, setError] = useState<string | null>(null);
27
+
28
+ const total = cartItems.reduce(
29
+ (sum, cartItem) => sum + cartItem.item.price * cartItem.quantity,
30
+ 0
31
+ );
32
+
33
+ const formatPrice = (price: number) => price.toFixed(2);
34
+
35
+ const handlePurchase = async () => {
36
+ try {
37
+ setIsLoading(true);
38
+ setError(null);
39
+ const success = await onPurchase();
40
+
41
+ if (!success) {
42
+ setError('Purchase failed. Please try again.');
43
+ }
44
+ } catch (err) {
45
+ setError('An error occurred during purchase. Please try again.');
46
+ console.error('Purchase error:', err);
47
+ } finally {
48
+ setIsLoading(false);
49
+ }
50
+ };
51
+
52
+ return (
53
+ <Container>
54
+ <Header>
55
+ <Title>Shopping Cart</Title>
56
+ <CloseButton onPointerDown={onClose}>
57
+ <FaTimes />
58
+ </CloseButton>
59
+ </Header>
60
+
61
+ <CartItems>
62
+ {cartItems.length === 0 ? (
63
+ <EmptyCart>Your cart is empty</EmptyCart>
64
+ ) : (
65
+ cartItems.map(cartItem => (
66
+ <CartItemRow key={cartItem.item.key}>
67
+ <ItemIconContainer>
68
+ <SpriteFromAtlas
69
+ atlasJSON={atlasJSON}
70
+ atlasIMG={atlasIMG}
71
+ spriteKey={cartItem.item.texturePath}
72
+ width={32}
73
+ height={32}
74
+ imgScale={2}
75
+ centered
76
+ />
77
+ </ItemIconContainer>
78
+ <ItemDetails>
79
+ <ItemName>{cartItem.item.name}</ItemName>
80
+ <ItemInfo>
81
+ <span>${formatPrice(cartItem.item.price)}</span>
82
+ <span>×</span>
83
+ <span>{cartItem.quantity}</span>
84
+ <span>=</span>
85
+ <span>
86
+ ${formatPrice(cartItem.item.price * cartItem.quantity)}
87
+ </span>
88
+ </ItemInfo>
89
+ </ItemDetails>
90
+
91
+ <CTAButton
92
+ icon={<FaTrash />}
93
+ onClick={e => {
94
+ e.stopPropagation();
95
+ onRemoveFromCart(cartItem.item.key);
96
+ }}
97
+ />
98
+ </CartItemRow>
99
+ ))
100
+ )}
101
+ </CartItems>
102
+
103
+ <Footer>
104
+ <TotalInfo>
105
+ <TotalRow>
106
+ <span>Total:</span>
107
+ <span>${formatPrice(total)}</span>
108
+ </TotalRow>
109
+ {error && <ErrorMessage>{error}</ErrorMessage>}
110
+ </TotalInfo>
111
+ <CTAButton
112
+ icon={<FaShoppingBag />}
113
+ label={isLoading ? 'Processing...' : 'Complete Purchase'}
114
+ onClick={handlePurchase}
115
+ fullWidth
116
+ disabled={cartItems.length === 0 || isLoading}
117
+ />
118
+ </Footer>
119
+ </Container>
120
+ );
121
+ };
122
+
123
+ const Container = styled.div`
124
+ display: flex;
125
+ flex-direction: column;
126
+ width: 100%;
127
+ height: 100%;
128
+ gap: 1.5rem;
129
+ padding: 1.5rem;
130
+ `;
131
+
132
+ const Header = styled.div`
133
+ display: flex;
134
+ justify-content: space-between;
135
+ align-items: center;
136
+ `;
137
+
138
+ const Title = styled.h2`
139
+ font-family: 'Press Start 2P', cursive;
140
+ font-size: 1rem;
141
+ color: #ffffff;
142
+ margin: 0;
143
+ `;
144
+
145
+ const CloseButton = styled.div`
146
+ padding: 0.5rem;
147
+ min-width: unset;
148
+ width: 42px;
149
+ height: 42px;
150
+ display: flex;
151
+ font-size: 1.5rem;
152
+ align-items: center;
153
+ color: white;
154
+ justify-content: center;
155
+ `;
156
+
157
+ const CartItems = styled.div`
158
+ display: flex;
159
+ flex-direction: column;
160
+ gap: 1rem;
161
+ overflow-y: auto;
162
+ flex: 1;
163
+ min-height: 200px;
164
+ padding-right: 0.5rem;
165
+
166
+ /* Custom scrollbar styling */
167
+ &::-webkit-scrollbar {
168
+ width: 8px;
169
+ background-color: rgba(0, 0, 0, 0.2);
170
+ border-radius: 4px;
171
+ }
172
+
173
+ &::-webkit-scrollbar-thumb {
174
+ background-color: rgba(255, 255, 255, 0.2);
175
+ border-radius: 4px;
176
+
177
+ &:hover {
178
+ background-color: rgba(255, 255, 255, 0.3);
179
+ }
180
+ }
181
+
182
+ /* Firefox scrollbar styling */
183
+ scrollbar-width: thin;
184
+ scrollbar-color: rgba(255, 255, 255, 0.2) rgba(0, 0, 0, 0.2);
185
+ `;
186
+
187
+ const EmptyCart = styled.div`
188
+ display: flex;
189
+ align-items: center;
190
+ justify-content: center;
191
+ height: 100%;
192
+ color: #ffffff;
193
+ font-family: 'Press Start 2P', cursive;
194
+ font-size: 0.875rem;
195
+ opacity: 0.7;
196
+ `;
197
+
198
+ const CartItemRow = styled.div`
199
+ display: flex;
200
+ align-items: center;
201
+ gap: 1rem;
202
+ padding: 1rem;
203
+ background: rgba(0, 0, 0, 0.2);
204
+ border-radius: 4px;
205
+ `;
206
+
207
+ const ItemIconContainer = styled.div`
208
+ width: 32px;
209
+ height: 32px;
210
+ display: flex;
211
+ align-items: center;
212
+ justify-content: center;
213
+ border-radius: 4px;
214
+ padding: 4px;
215
+ `;
216
+
217
+ const ItemDetails = styled.div`
218
+ flex: 1;
219
+ display: flex;
220
+ flex-direction: column;
221
+ gap: 0.5rem;
222
+ `;
223
+
224
+ const ItemName = styled.div`
225
+ font-family: 'Press Start 2P', cursive;
226
+ font-size: 0.875rem;
227
+ color: #ffffff;
228
+ `;
229
+
230
+ const ItemInfo = styled.div`
231
+ display: flex;
232
+ align-items: center;
233
+ gap: 0.5rem;
234
+ font-family: 'Press Start 2P', cursive;
235
+ font-size: 0.75rem;
236
+ color: #fef08a;
237
+ `;
238
+
239
+ const Footer = styled.div`
240
+ display: flex;
241
+ flex-direction: column;
242
+ gap: 1.5rem;
243
+ padding-top: 1.5rem;
244
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
245
+ `;
246
+
247
+ const TotalInfo = styled.div`
248
+ display: flex;
249
+ flex-direction: column;
250
+ gap: 0.75rem;
251
+ `;
252
+
253
+ const TotalRow = styled.div`
254
+ display: flex;
255
+ justify-content: space-between;
256
+ font-family: 'Press Start 2P', cursive;
257
+ font-size: 0.875rem;
258
+ color: #ffffff;
259
+
260
+ span:last-child {
261
+ color: #fef08a;
262
+ }
263
+ `;
264
+
265
+ const ErrorMessage = styled.div`
266
+ color: #ef4444;
267
+ font-family: 'Press Start 2P', cursive;
268
+ font-size: 0.75rem;
269
+ margin-top: 0.5rem;
270
+ text-align: center;
271
+ `;
@@ -1,63 +1,91 @@
1
- import { ItemType } from '@rpg-engine/shared';
1
+ import { ItemRarities, ItemSubType, ItemType } from '@rpg-engine/shared';
2
2
  import React, { useMemo, useState } from 'react';
3
+ import { FaShoppingCart } from 'react-icons/fa';
3
4
  import styled from 'styled-components';
4
5
  import { uiColors } from '../../constants/uiColors';
5
6
  import { DraggableContainer } from '../DraggableContainer';
6
7
  import { InternalTabs } from '../InternalTabs/InternalTabs';
7
8
  import { RPGUIContainerTypes } from '../RPGUI/RPGUIContainer';
8
- import { SearchBar } from '../shared/SearchBar/SearchBar';
9
-
10
- import { StoreTabContent } from './StoreTabContent';
11
- import { IStoreItem, IStoreProps } from './StoreTypes';
9
+ import { CTAButton } from '../shared/CTAButton/CTAButton';
10
+ import { CartView } from './CartView';
11
+ import { useStoreCart } from './hooks/useStoreCart';
12
+ import { StoreItemsSection } from './sections/StoreItemsSection';
13
+ import { StorePacksSection } from './sections/StorePacksSection';
14
+ import { StoreItemDetails } from './StoreItemDetails';
15
+ import { IItemPack, IStoreItem, IStoreProps } from './StoreTypes';
12
16
 
13
17
  export const Store: React.FC<IStoreProps> = ({
14
18
  items,
19
+ packs = [],
15
20
  atlasJSON,
16
21
  atlasIMG,
17
22
  onPurchase,
18
- userGold,
19
23
  userAccountType,
20
24
  loading = false,
21
25
  error,
22
- initialSearchQuery = '',
23
26
  onClose,
24
27
  }) => {
25
- const [activeTab, setActiveTab] = useState('all');
26
- const [searchQuery, setSearchQuery] = useState(initialSearchQuery);
28
+ const [selectedPack, setSelectedPack] = useState<IItemPack | null>(null);
29
+ const [activeTab, setActiveTab] = useState('premium');
30
+ const {
31
+ cartItems,
32
+ handleAddToCart,
33
+ handleRemoveFromCart,
34
+ handlePurchase: handleCartPurchase,
35
+ openCart,
36
+ closeCart,
37
+ getTotalItems,
38
+ getTotalPrice,
39
+ isCartOpen,
40
+ } = useStoreCart();
41
+
42
+ const handleAddPackToCart = (pack: IItemPack) => {
43
+ const packItem: IStoreItem = {
44
+ _id: pack.key,
45
+ key: pack.key,
46
+ name: pack.title,
47
+ price: pack.priceUSD,
48
+ texturePath: pack.image.default || pack.image.src,
49
+ textureKey: pack.image.default || pack.image.src,
50
+ type: ItemType.Consumable,
51
+ subType: ItemSubType.Other,
52
+ description: pack.description || '',
53
+ fullDescription: pack.description || '',
54
+ textureAtlas: 'items',
55
+ weight: 0,
56
+ rarity: ItemRarities.Common,
57
+ allowedEquipSlotType: [],
58
+ isEquipable: false,
59
+ isStackable: false,
60
+ isTwoHanded: false,
61
+ hasUseWith: false,
62
+ maxStackSize: 1,
63
+ isUsable: false,
64
+ isStorable: true,
65
+ isSolid: false,
66
+ isItemContainer: false,
67
+ };
68
+ handleAddToCart(packItem, 1);
69
+ };
27
70
 
28
71
  const filterItems = (
29
72
  itemsToFilter: IStoreItem[],
30
- type: ItemType | 'premium' | 'all'
73
+ type: 'items' | 'premium'
31
74
  ): IStoreItem[] => {
32
75
  return itemsToFilter.filter(item => {
33
- const matchesSearch = item.name
34
- .toLowerCase()
35
- .includes(searchQuery.toLowerCase());
36
-
37
- if (!matchesSearch) return false;
38
-
39
- switch (type) {
40
- case ItemType.Weapon:
41
- return item.type === ItemType.Weapon;
42
- case ItemType.Consumable:
43
- return item.type === ItemType.Consumable;
44
- case 'premium':
45
- return item.requiredAccountType?.length ?? 0 > 0;
46
- default:
47
- return true;
76
+ if (type === 'premium') {
77
+ return item.requiredAccountType?.length ?? 0 > 0;
48
78
  }
79
+ return true;
49
80
  });
50
81
  };
51
82
 
52
- // Memoize filtered items for each tab to prevent unnecessary recalculations
53
83
  const filteredItems = useMemo(
54
84
  () => ({
55
- all: filterItems(items, 'all'),
56
- weapons: filterItems(items, ItemType.Weapon),
57
- consumables: filterItems(items, ItemType.Consumable),
85
+ items: filterItems(items, 'items'),
58
86
  premium: filterItems(items, 'premium'),
59
87
  }),
60
- [items, searchQuery]
88
+ [items]
61
89
  );
62
90
 
63
91
  if (loading) {
@@ -70,62 +98,37 @@ export const Store: React.FC<IStoreProps> = ({
70
98
 
71
99
  const tabs = [
72
100
  {
73
- id: 'all',
74
- title: 'All',
101
+ id: 'premium',
102
+ title: 'Premium',
75
103
  content: (
76
- <StoreTabContent
77
- items={filteredItems.all}
78
- atlasJSON={atlasJSON}
79
- atlasIMG={atlasIMG}
80
- onPurchase={onPurchase}
81
- userGold={userGold}
82
- userAccountType={userAccountType}
83
- tabId="all"
104
+ <StorePacksSection
105
+ packs={packs.filter(pack => pack.priceUSD >= 9.99)}
106
+ onAddToCart={handleAddPackToCart}
107
+ onSelectPack={setSelectedPack}
84
108
  />
85
109
  ),
86
110
  },
87
111
  {
88
- id: 'weapons',
89
- title: 'Weapons',
112
+ id: 'packs',
113
+ title: 'Packs',
90
114
  content: (
91
- <StoreTabContent
92
- items={filteredItems.weapons}
93
- atlasJSON={atlasJSON}
94
- atlasIMG={atlasIMG}
95
- onPurchase={onPurchase}
96
- userGold={userGold}
97
- userAccountType={userAccountType}
98
- tabId="weapons"
115
+ <StorePacksSection
116
+ packs={packs.filter(pack => pack.priceUSD < 9.99)}
117
+ onAddToCart={handleAddPackToCart}
118
+ onSelectPack={setSelectedPack}
99
119
  />
100
120
  ),
101
121
  },
102
122
  {
103
- id: 'consumables',
104
- title: 'Consumables',
123
+ id: 'items',
124
+ title: 'Items',
105
125
  content: (
106
- <StoreTabContent
107
- items={filteredItems.consumables}
126
+ <StoreItemsSection
127
+ items={filteredItems.items}
128
+ onAddToCart={handleAddToCart}
108
129
  atlasJSON={atlasJSON}
109
130
  atlasIMG={atlasIMG}
110
- onPurchase={onPurchase}
111
- userGold={userGold}
112
131
  userAccountType={userAccountType}
113
- tabId="consumables"
114
- />
115
- ),
116
- },
117
- {
118
- id: 'premium',
119
- title: 'Premium',
120
- content: (
121
- <StoreTabContent
122
- items={filteredItems.premium}
123
- atlasJSON={atlasJSON}
124
- atlasIMG={atlasIMG}
125
- onPurchase={onPurchase}
126
- userGold={userGold}
127
- userAccountType={userAccountType}
128
- tabId="premium"
129
132
  />
130
133
  ),
131
134
  },
@@ -135,29 +138,82 @@ export const Store: React.FC<IStoreProps> = ({
135
138
  <DraggableContainer
136
139
  title="Store"
137
140
  onCloseButton={onClose}
138
- width="800px"
141
+ width="850px"
139
142
  minWidth="600px"
143
+ height="auto"
140
144
  type={RPGUIContainerTypes.Framed}
145
+ cancelDrag="[class*='Store__Container'], [class*='CartView'], [class*='StoreItemDetails']"
141
146
  >
142
- <Container>
143
- <SearchContainer>
144
- <StyledSearchBar
145
- value={searchQuery}
146
- onChange={setSearchQuery}
147
- placeholder="Search items..."
148
- />
149
- </SearchContainer>
150
- <InternalTabs
151
- tabs={tabs}
152
- activeTextColor="#000000"
153
- activeColor="#fef08a"
154
- inactiveColor="#6b7280"
155
- borderColor="#f59e0b"
156
- hoverColor="#fef3c7"
157
- activeTab={activeTab}
158
- onTabChange={setActiveTab}
147
+ {isCartOpen ? (
148
+ <CartView
149
+ cartItems={cartItems}
150
+ onRemoveFromCart={handleRemoveFromCart}
151
+ onClose={closeCart}
152
+ onPurchase={async () => {
153
+ await handleCartPurchase(onPurchase);
154
+ return true;
155
+ }}
156
+ atlasJSON={atlasJSON}
157
+ atlasIMG={atlasIMG}
158
+ />
159
+ ) : selectedPack ? (
160
+ <StoreItemDetails
161
+ item={{
162
+ ...selectedPack,
163
+ name: selectedPack.title,
164
+ texturePath: selectedPack.image.default || selectedPack.image.src,
165
+ }}
166
+ imageUrl={selectedPack.image}
167
+ onBack={() => setSelectedPack(null)}
168
+ onAddToCart={() => handleAddPackToCart(selectedPack)}
159
169
  />
160
- </Container>
170
+ ) : (
171
+ <Container>
172
+ <TopBar>
173
+ <CartButton>
174
+ <CTAButton
175
+ icon={<FaShoppingCart />}
176
+ label={`${getTotalItems()} items ($${getTotalPrice().toFixed(
177
+ 2
178
+ )})`}
179
+ onClick={openCart}
180
+ />
181
+ </CartButton>
182
+ </TopBar>
183
+ <MainContent>
184
+ <InternalTabs
185
+ tabs={tabs}
186
+ activeTextColor="#000000"
187
+ activeColor="#fef08a"
188
+ inactiveColor="#6b7280"
189
+ borderColor="#f59e0b"
190
+ hoverColor="#fef3c7"
191
+ activeTab={activeTab}
192
+ onTabChange={setActiveTab}
193
+ />
194
+ </MainContent>
195
+ {cartItems.length > 0 && (
196
+ <Footer>
197
+ <CartSummary>
198
+ <CartInfo>
199
+ <span>Items in cart:</span>
200
+ <span>{getTotalItems()}</span>
201
+ </CartInfo>
202
+ <CartInfo>
203
+ <span>Total:</span>
204
+ <span>${getTotalPrice().toFixed(2)}</span>
205
+ </CartInfo>
206
+ </CartSummary>
207
+ <CTAButton
208
+ icon={<FaShoppingCart />}
209
+ label={`Proceed to Checkout ($${getTotalPrice().toFixed(2)})`}
210
+ onClick={openCart}
211
+ fullWidth
212
+ />
213
+ </Footer>
214
+ )}
215
+ </Container>
216
+ )}
161
217
  </DraggableContainer>
162
218
  );
163
219
  };
@@ -166,17 +222,64 @@ const Container = styled.div`
166
222
  display: flex;
167
223
  flex-direction: column;
168
224
  width: 100%;
169
- min-height: 400px;
225
+ height: 100%;
170
226
  gap: 1rem;
171
- padding: 1rem;
172
227
  `;
173
228
 
174
- const SearchContainer = styled.div`
229
+ const TopBar = styled.div`
230
+ display: flex;
231
+ align-items: center;
232
+ justify-content: flex-end;
233
+ gap: 1rem;
175
234
  padding: 0 1rem;
235
+ flex-shrink: 0;
176
236
  `;
177
237
 
178
- const StyledSearchBar = styled(SearchBar)`
179
- width: 100%;
238
+ const CartButton = styled.div`
239
+ min-width: fit-content;
240
+ `;
241
+
242
+ const MainContent = styled.div`
243
+ flex: 1;
244
+ display: flex;
245
+ flex-direction: column;
246
+ min-height: 0;
247
+ overflow: hidden;
248
+
249
+ .rpgui-tabs-content {
250
+ flex: 1;
251
+ overflow-y: auto;
252
+ padding-right: 0.5rem;
253
+ }
254
+ `;
255
+
256
+ const Footer = styled.div`
257
+ display: flex;
258
+ flex-direction: column;
259
+ gap: 1rem;
260
+ padding: 1rem;
261
+ border-top: 2px solid #f59e0b;
262
+ background: rgba(0, 0, 0, 0.2);
263
+ flex-shrink: 0;
264
+ `;
265
+
266
+ const CartSummary = styled.div`
267
+ display: flex;
268
+ flex-direction: column;
269
+ gap: 0.5rem;
270
+ `;
271
+
272
+ const CartInfo = styled.div`
273
+ display: flex;
274
+ align-items: center;
275
+ gap: 0.75rem;
276
+ font-family: 'Press Start 2P', cursive;
277
+ font-size: 0.75rem;
278
+ color: #ffffff;
279
+
280
+ span:last-child {
281
+ color: #fef08a;
282
+ }
180
283
  `;
181
284
 
182
285
  const LoadingMessage = styled.div`