ps99-api 2.4.0 → 2.6.0

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 (92) hide show
  1. package/.github/workflows/release-on-main.yml +1 -2
  2. package/.idea/runConfigurations/test_changing.xml +1 -1
  3. package/debug_currency.json +57 -0
  4. package/debug_goals.json +271 -0
  5. package/dist/ps99-api.d.ts +2 -0
  6. package/dist/ps99-api.js +4 -1
  7. package/dist/ps99-api.js.map +1 -1
  8. package/dist/request-client/axios.js +6 -1
  9. package/dist/request-client/axios.js.map +1 -1
  10. package/dist/responses/collection/index.d.ts +1 -0
  11. package/dist/responses/collection/index.js +15 -0
  12. package/dist/responses/collection/index.js.map +1 -1
  13. package/dist/responses/collection/rarity.d.ts +1 -0
  14. package/example-web/react/package-lock.json +1504 -1470
  15. package/example-web/react2/package-lock.json +3089 -2766
  16. package/example-web/react2/package.json +6 -1
  17. package/example-web/react2/public/assets/gold_variant_icon.png +0 -0
  18. package/example-web/react2/public/assets/hot_cocoa_egg.png +0 -0
  19. package/example-web/react2/public/index.html +34 -31
  20. package/example-web/react2/src/App.tsx +15 -15
  21. package/example-web/react2/src/assets/guild_placeholder.png +0 -0
  22. package/example-web/react2/src/components/AchievementsComponent.tsx +74 -19
  23. package/example-web/react2/src/components/AutoSizer.tsx +49 -0
  24. package/example-web/react2/src/components/BoostsComponent.tsx +16 -5
  25. package/example-web/react2/src/components/BoothsComponent.tsx +22 -52
  26. package/example-web/react2/src/components/BoxesComponent.tsx +48 -16
  27. package/example-web/react2/src/components/BuffsComponent.tsx +82 -34
  28. package/example-web/react2/src/components/CharmsComponent.tsx +84 -24
  29. package/example-web/react2/src/components/CollectionConfigIndex.tsx +867 -33
  30. package/example-web/react2/src/components/CollectionsIndex.tsx +380 -27
  31. package/example-web/react2/src/components/CollectionsLayout.tsx +60 -0
  32. package/example-web/react2/src/components/CurrencyComponent.tsx +57 -39
  33. package/example-web/react2/src/components/DynamicCollectionConfigData.tsx +172 -15
  34. package/example-web/react2/src/components/EggsComponent.tsx +50 -12
  35. package/example-web/react2/src/components/EnchantsComponent.tsx +88 -42
  36. package/example-web/react2/src/components/FishingRodsComponent.tsx +36 -22
  37. package/example-web/react2/src/components/Footer.tsx +18 -8
  38. package/example-web/react2/src/components/FruitsComponent.tsx +40 -17
  39. package/example-web/react2/src/components/GenericFetchComponent.tsx +9 -1
  40. package/example-web/react2/src/components/GuildBattlesComponent.tsx +41 -34
  41. package/example-web/react2/src/components/Header.tsx +39 -52
  42. package/example-web/react2/src/components/HomePage.tsx +15 -17
  43. package/example-web/react2/src/components/HoverboardsComponent.tsx +23 -99
  44. package/example-web/react2/src/components/ImageComponent.tsx +255 -45
  45. package/example-web/react2/src/components/ItemCard.tsx +240 -0
  46. package/example-web/react2/src/components/LootboxesComponent.tsx +22 -7
  47. package/example-web/react2/src/components/MasteryComponent.tsx +165 -30
  48. package/example-web/react2/src/components/MerchantsComponent.tsx +41 -16
  49. package/example-web/react2/src/components/MiscItemsComponent.tsx +26 -31
  50. package/example-web/react2/src/components/PetsComponent.tsx +100 -61
  51. package/example-web/react2/src/components/PotionsComponent.tsx +121 -27
  52. package/example-web/react2/src/components/RandomEventsComponent.tsx +32 -23
  53. package/example-web/react2/src/components/RanksComponent.tsx +187 -62
  54. package/example-web/react2/src/components/RarityComponent.tsx +123 -5
  55. package/example-web/react2/src/components/ReactWindowMock.tsx +73 -0
  56. package/example-web/react2/src/components/RebirthsComponent.tsx +36 -19
  57. package/example-web/react2/src/components/SecretRoomsComponent.tsx +5 -4
  58. package/example-web/react2/src/components/SeedsComponent.tsx +41 -21
  59. package/example-web/react2/src/components/ShovelsComponent.tsx +21 -9
  60. package/example-web/react2/src/components/Sidebar.tsx +105 -0
  61. package/example-web/react2/src/components/SprinklersComponent.tsx +25 -10
  62. package/example-web/react2/src/components/Tooltip.tsx +36 -0
  63. package/example-web/react2/src/components/UltimatesComponent.tsx +28 -16
  64. package/example-web/react2/src/components/UpgradesComponent.tsx +97 -47
  65. package/example-web/react2/src/components/WateringCansComponent.tsx +20 -14
  66. package/example-web/react2/src/components/WorldsComponent.tsx +21 -11
  67. package/example-web/react2/src/components/XPPotionsComponent.tsx +28 -11
  68. package/example-web/react2/src/components/ZoneFlagsComponent.tsx +25 -14
  69. package/example-web/react2/src/components/ZonesComponent.tsx +43 -60
  70. package/example-web/react2/src/constants/collectionIcons.ts +29 -0
  71. package/example-web/react2/src/context/CollectionDataContext.tsx +62 -0
  72. package/example-web/react2/src/context/ScrollContext.tsx +35 -0
  73. package/example-web/react2/src/hooks/useCollapsibleHeader.ts +69 -0
  74. package/example-web/react2/src/hooks/useExpandableList.ts +38 -0
  75. package/example-web/react2/src/hooks/useItemResolution.ts +351 -0
  76. package/example-web/react2/src/index.css +257 -0
  77. package/example-web/react2/src/index.tsx +2 -1
  78. package/example-web/react2/src/utils/gigantix.ts +40 -0
  79. package/example-web/react2/temp_model.rbxm +0 -0
  80. package/example-web/react2/webpack.config.js +103 -47
  81. package/package.json +11 -11
  82. package/ranks.json +1 -0
  83. package/repro_collection_fetch.ts +33 -0
  84. package/repro_image_fetch.ts +50 -0
  85. package/src/__tests__/__snapshots__/ps99-api-changes.ts.snap +34841 -10439
  86. package/src/__tests__/__snapshots__/ps99-api-live.ts.snap +160667 -67217
  87. package/src/ps99-api.ts +9 -5
  88. package/src/request-client/axios.ts +6 -2
  89. package/src/responses/collection/index.ts +1 -0
  90. package/src/responses/collection/rarity.ts +1 -0
  91. package/tsconfig.json +1 -1
  92. package/example-web/react2/public/service-worker.js +0 -63
@@ -0,0 +1,73 @@
1
+ import React, { useRef, useEffect } from 'react';
2
+
3
+ export const FixedSizeList = ({ children, itemCount, itemSize, height, width, onScroll, initialScrollOffset, itemData }: any) => {
4
+ const items = [];
5
+ for (let i = 0; i < itemCount; i++) {
6
+ items.push(
7
+ <div key={i} style={{ width: "100%" }}>
8
+ {children({ index: i, style: { height: itemSize, width: "100%" }, data: itemData })}
9
+ </div>
10
+ );
11
+ }
12
+
13
+ // Handle initial scroll
14
+ const ref = useRef<HTMLDivElement>(null);
15
+ useEffect(() => {
16
+ if (ref.current && (initialScrollOffset !== undefined && initialScrollOffset !== null)) {
17
+ ref.current.scrollTop = initialScrollOffset;
18
+ }
19
+ }, [initialScrollOffset]);
20
+
21
+ return (
22
+ <div
23
+ ref={ref}
24
+ style={{ height, width, overflow: "auto", position: 'relative' }}
25
+ onScroll={(e) => onScroll && onScroll({ scrollOffset: e.currentTarget.scrollTop })}
26
+ >
27
+ {items}
28
+ </div>
29
+ );
30
+ };
31
+
32
+ export const FixedSizeGrid = ({ children, columnCount, rowCount, columnWidth, rowHeight, height, width, onScroll, initialScrollOffset, itemData, style }: any) => {
33
+ const items = [];
34
+ for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
35
+ for (let columnIndex = 0; columnIndex < columnCount; columnIndex++) {
36
+ items.push(
37
+ <div key={`${rowIndex}-${columnIndex}`} style={{ position: 'absolute' }}>
38
+ {children({
39
+ columnIndex,
40
+ rowIndex,
41
+ style: {
42
+ position: 'absolute',
43
+ left: columnIndex * columnWidth,
44
+ top: rowIndex * rowHeight,
45
+ height: rowHeight,
46
+ width: columnWidth
47
+ },
48
+ data: itemData
49
+ })}
50
+ </div>
51
+ );
52
+ }
53
+ }
54
+ // Handle initial scroll
55
+ const ref = useRef<HTMLDivElement>(null);
56
+ useEffect(() => {
57
+ if (ref.current && initialScrollOffset) {
58
+ ref.current.scrollTop = initialScrollOffset;
59
+ }
60
+ }, [initialScrollOffset]);
61
+
62
+ return (
63
+ <div
64
+ ref={ref}
65
+ style={{ height, width, overflowY: "auto", overflowX: "hidden", position: 'relative', ...style }}
66
+ onScroll={(e) => onScroll && onScroll({ scrollTop: e.currentTarget.scrollTop })}
67
+ >
68
+ <div style={{ height: rowCount * rowHeight, width: columnWidth * columnCount }}>
69
+ {items}
70
+ </div>
71
+ </div>
72
+ );
73
+ };
@@ -1,29 +1,46 @@
1
+
1
2
  import React from "react";
2
3
  import { CollectionConfigData, RebirthUnlock } from "ps99-api";
3
- import ImageComponent from "./ImageComponent";
4
+ import ItemCard from "./ItemCard";
4
5
 
5
6
  const RebirthComponent: React.FC<{
6
7
  configData: CollectionConfigData<"Rebirths">;
7
8
  }> = ({ configData }) => {
8
9
  return (
9
- <div>
10
- <h2>Rebirth: {configData.DisplayName}</h2>
11
- <p>Rebirth Number: {configData.RebirthNumber}</p>
12
- <p>Zone Number Required: {configData.ZoneNumberRequired}</p>
13
- <p>Strength Power Boost: {configData.StrengthPowerBoost}%</p>
14
- <p>Boost Description: {configData.BoostDesc}</p>
15
- {configData.ResetZone && <p>Reset Zone: {configData.ResetZone}</p>}
16
- <h3>Unlocks:</h3>
17
- <ul>
18
- {configData.RebirthUnlocks.map(
19
- (unlock: RebirthUnlock, index: number) => (
20
- <li key={index}>
21
- <ImageComponent src={unlock.Icon} alt={unlock.Title} />
22
- <strong>{unlock.GuiTitle || unlock.Title}</strong>: {unlock.Desc}
23
- </li>
24
- ),
25
- )}
26
- </ul>
10
+ <div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
11
+
12
+ <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: '15px', marginBottom: '20px', textAlign: 'left' }}>
13
+ <p><strong>Zone Required:</strong> {configData.ZoneNumberRequired}</p>
14
+ <p><strong>Strength Boost:</strong> {configData.StrengthPowerBoost}%</p>
15
+ {configData.ResetZone && <p><strong>Reset Zone:</strong> {configData.ResetZone}</p>}
16
+ {configData.BoostDesc && <p style={{ gridColumn: '1 / -1' }}><strong>Boost:</strong> {configData.BoostDesc}</p>}
17
+ </div>
18
+
19
+ <div style={{ borderTop: '1px solid #eee', paddingTop: '15px' }}>
20
+ <h3 style={{ fontSize: '1.2em', marginBottom: '15px' }}>Unlocks</h3>
21
+ <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(200px, 1fr))", gap: "15px" }}>
22
+ {configData.RebirthUnlocks.map(
23
+ (unlock: RebirthUnlock, index: number) => (
24
+ <div key={index}>
25
+ <ItemCard
26
+ id={unlock.Title}
27
+ amount={1}
28
+ label={unlock.GuiTitle || unlock.Title}
29
+ itemData={{
30
+ icon: unlock.Icon,
31
+ rarity: undefined,
32
+ name: unlock.GuiTitle || unlock.Title
33
+ }}
34
+ rarityColor={null}
35
+ />
36
+ <div style={{ textAlign: 'center', fontSize: '0.8em', color: '#666', marginTop: '5px' }}>
37
+ {unlock.Desc}
38
+ </div>
39
+ </div>
40
+ ),
41
+ )}
42
+ </div>
43
+ </div>
27
44
  </div>
28
45
  );
29
46
  };
@@ -5,10 +5,11 @@ const SecretRoomComponent: React.FC<{
5
5
  configData: CollectionConfigData<"SecretRooms">;
6
6
  }> = ({ configData }) => {
7
7
  return (
8
- <div>
9
- <h2>Secret Room: {configData.DisplayName}</h2>
10
- <p>Instance ID: {configData.InstanceId}</p>
11
- <p>Required Zone: {configData.RequiredZone}</p>
8
+ <div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
9
+ <div style={{ textAlign: 'left', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '10px' }}>
10
+ <p><strong>Instance ID:</strong> {configData.InstanceId}</p>
11
+ <p><strong>Required Zone:</strong> {configData.RequiredZone}</p>
12
+ </div>
12
13
  </div>
13
14
  );
14
15
  };
@@ -6,7 +6,8 @@ import {
6
6
  RawStackKey,
7
7
  LootTableData,
8
8
  } from "ps99-api";
9
- import ImageComponent from "./ImageComponent";
9
+ import ItemCard from "./ItemCard";
10
+ import { useItemResolution } from "../hooks/useItemResolution";
10
11
 
11
12
  const parseRawStackKey = (rawStackKey: RawStackKey): LootTableData => {
12
13
  try {
@@ -20,21 +21,25 @@ const parseRawStackKey = (rawStackKey: RawStackKey): LootTableData => {
20
21
  const renderLootTable = (lootTable: LootTableRoot | LootTableRoot[]) => {
21
22
  const lootTableArray = Array.isArray(lootTable) ? lootTable : [lootTable];
22
23
  return lootTableArray.map((table, index) => (
23
- <div key={index}>
24
+ <div key={index} style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))', gap: '10px' }}>
24
25
  {table.entries.map((entry: LootTableEntry, entryIndex: number) => {
25
26
  const parsedStackKey = entry.Value._stackKey
26
27
  ? parseRawStackKey(entry.Value._stackKey)
27
28
  : null;
28
29
  return (
29
- <div key={entryIndex}>
30
- <h4>Loot Entry {entryIndex + 1}</h4>
31
- <p>Weight: {entry.Weight}</p>
30
+ <div key={entryIndex} style={{ padding: '10px', border: '1px solid #eee', borderRadius: '8px', background: '#f9f9f9', textAlign: 'left' }}>
31
+ <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '5px' }}>
32
+ <span style={{ fontWeight: 'bold', fontSize: '0.9em' }}>Entry {entryIndex + 1}</span>
33
+ <span className="badge">Weight: {entry.Weight}</span>
34
+ </div>
32
35
  {parsedStackKey && (
33
- <>
34
- <p>Item ID: {parsedStackKey.id}</p>
35
- {parsedStackKey.tn && <p>Item TN: {parsedStackKey.tn}</p>}
36
- {parsedStackKey._am && <p>Amount: {parsedStackKey._am}</p>}
37
- </>
36
+ <div style={{ fontSize: '0.85em', color: '#555' }}>
37
+ <div style={{ display: 'flex', alignItems: 'center', gap: '5px' }}>
38
+ <span>ID: <strong>{parsedStackKey.id}</strong></span>
39
+ </div>
40
+ {parsedStackKey.tn && <div>Tier/TN: {parsedStackKey.tn}</div>}
41
+ {parsedStackKey._am && <div>Amount: {parsedStackKey._am}</div>}
42
+ </div>
38
43
  )}
39
44
  </div>
40
45
  );
@@ -46,18 +51,33 @@ const renderLootTable = (lootTable: LootTableRoot | LootTableRoot[]) => {
46
51
  const SeedComponent: React.FC<{
47
52
  configData: CollectionConfigData<"Seeds">;
48
53
  }> = ({ configData }) => {
54
+ const { getRarityColor } = useItemResolution();
55
+ const rarityColor = configData.Rarity ? getRarityColor(configData.Rarity) : null;
56
+
49
57
  return (
50
- <div>
51
- <h2>Seed: {configData.DisplayName}</h2>
52
- <p>Description: {configData.Desc}</p>
53
- <p>Grow Time: {configData.GrowTime} seconds</p>
54
- <p>
55
- Rarity: {configData.Rarity.DisplayName} (Rarity Number:{" "}
56
- {configData.Rarity.RarityNumber})
57
- </p>
58
- <ImageComponent src={configData.Icon} alt={configData.DisplayName} />
59
- <div>
60
- <h3>Loot Table</h3>
58
+ <div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
59
+
60
+ <div style={{ maxWidth: '300px', margin: '0 auto 15px auto' }}>
61
+ <ItemCard
62
+ id={configData.DisplayName}
63
+ amount={1}
64
+ label={configData.DisplayName}
65
+ itemData={{
66
+ icon: configData.Icon,
67
+ rarity: configData.Rarity,
68
+ name: configData.DisplayName
69
+ }}
70
+ rarityColor={rarityColor}
71
+ />
72
+ </div>
73
+
74
+ <div style={{ marginBottom: '20px', fontSize: '0.9em', color: '#666', textAlign: 'center' }}>
75
+ <p>{configData.Desc}</p>
76
+ <p style={{ fontWeight: 'bold', marginTop: '5px' }}>Grow Time: {configData.GrowTime}s</p>
77
+ </div>
78
+
79
+ <div style={{ borderTop: '1px solid #eee', paddingTop: '15px' }}>
80
+ <h3 style={{ textAlign: 'center', fontSize: '1.2em', marginBottom: '15px' }}>Loot Table</h3>
61
81
  {renderLootTable(configData.LootTable)}
62
82
  </div>
63
83
  </div>
@@ -1,19 +1,31 @@
1
1
  import React from "react";
2
2
  import { CollectionConfigData } from "ps99-api";
3
- import ImageComponent from "./ImageComponent";
3
+ import ItemCard from "./ItemCard";
4
4
 
5
5
  const ShovelComponent: React.FC<{
6
6
  configData: CollectionConfigData<"Shovels">;
7
7
  }> = ({ configData }) => {
8
8
  return (
9
- <div>
10
- <h2>Shovel: {configData.DisplayName}</h2>
11
- <p>Description: {configData.Desc}</p>
12
- <p>Associated Item ID: {configData.AssociatedItemID}</p>
13
- {configData.MerchantSalePrice && (
14
- <p>Merchant Sale Price: {configData.MerchantSalePrice}</p>
15
- )}
16
- <ImageComponent src={configData.Icon} alt={configData.DisplayName} />
9
+ <div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
10
+
11
+ <div style={{ maxWidth: '300px', margin: '0 auto 15px auto' }}>
12
+ <ItemCard
13
+ id={configData.DisplayName}
14
+ amount={1}
15
+ label={configData.DisplayName}
16
+ itemData={{
17
+ icon: configData.Icon,
18
+ rarity: undefined,
19
+ name: configData.DisplayName
20
+ }}
21
+ rarityColor={null}
22
+ />
23
+ </div>
24
+
25
+ <div style={{ fontSize: '0.9em', color: '#666', textAlign: 'center' }}>
26
+ <p>{configData.Desc}</p>
27
+ {configData.MerchantSalePrice && <p style={{ marginTop: '10px' }}><strong>Sale Price:</strong> {configData.MerchantSalePrice}</p>}
28
+ </div>
17
29
  </div>
18
30
  );
19
31
  };
@@ -0,0 +1,105 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { Link, useParams } from "react-router-dom";
3
+ import { PetSimulator99API, CollectionName } from "ps99-api";
4
+
5
+ import { COLLECTION_ICONS } from "../constants/collectionIcons";
6
+
7
+
8
+ interface SidebarProps {
9
+ currentCollection?: string;
10
+ }
11
+
12
+ const Sidebar: React.FC<SidebarProps> = ({ currentCollection }) => {
13
+ const [collections, setCollections] = useState<CollectionName[]>([]);
14
+
15
+ useEffect(() => {
16
+ const fetchCollections = async () => {
17
+ const api = new PetSimulator99API();
18
+ const response = await api.getCollections();
19
+ if (response.status === "ok") {
20
+ // Filter out collections that might not have icons or just to keep it clean if needed
21
+ setCollections(response.data);
22
+ }
23
+ };
24
+ fetchCollections();
25
+ }, []);
26
+
27
+ return (
28
+ <div style={{
29
+ width: "260px",
30
+ backgroundColor: "#fff",
31
+ borderRight: "4px solid #333",
32
+ display: "flex",
33
+ flexDirection: "column",
34
+ padding: "15px",
35
+ gap: "10px",
36
+ flexShrink: 0,
37
+ overflowY: "auto",
38
+ maxHeight: "100%",
39
+ }}>
40
+ <h3 style={{
41
+ textAlign: "center",
42
+ marginBottom: "15px",
43
+ color: "#333",
44
+ fontSize: "1.5rem",
45
+ fontWeight: "900",
46
+ textShadow: "2px 2px 0px #eee",
47
+ fontFamily: "'Fredoka One', cursive, sans-serif", // Assuming font availability or fallback
48
+ }}>
49
+ Terminal!
50
+ </h3>
51
+
52
+ <div style={{
53
+ display: "grid",
54
+ gridTemplateColumns: "1fr 1fr",
55
+ gap: "10px",
56
+ }}>
57
+ {collections.map((collection) => {
58
+ const isActive = currentCollection === collection;
59
+ const icon = COLLECTION_ICONS[collection] || "📦";
60
+
61
+ return (
62
+ <Link
63
+ key={collection}
64
+ to={`/collections/${collection}`}
65
+ style={{
66
+ display: "flex",
67
+ flexDirection: "column",
68
+ alignItems: "center",
69
+ justifyContent: "center",
70
+ aspectRatio: "1/1",
71
+ backgroundColor: isActive ? "#00b894" : "#dfe6e9",
72
+ borderRadius: "16px",
73
+ textDecoration: "none",
74
+ color: isActive ? "#fff" : "#2d3436",
75
+ border: "3px solid #333",
76
+ boxShadow: isActive ? "inset 0 -4px 0 rgba(0,0,0,0.2)" : "0 4px 0 #b2bec3",
77
+ transition: "all 0.1s ease",
78
+ transform: isActive ? "translateY(2px)" : "translateY(0)",
79
+ }}
80
+ >
81
+ <span style={{
82
+ fontSize: "2rem",
83
+ marginBottom: "4px",
84
+ filter: isActive ? "drop-shadow(0 2px 0 rgba(0,0,0,0.2))" : "none"
85
+ }}>
86
+ {icon}
87
+ </span>
88
+ <span style={{
89
+ fontSize: "0.8rem",
90
+ fontWeight: "800",
91
+ textAlign: "center",
92
+ lineHeight: "1.1",
93
+ textShadow: isActive ? "1px 1px 0 rgba(0,0,0,0.2)" : "none"
94
+ }}>
95
+ {collection}
96
+ </span>
97
+ </Link>
98
+ );
99
+ })}
100
+ </div>
101
+ </div>
102
+ );
103
+ };
104
+
105
+ export default Sidebar;
@@ -1,20 +1,35 @@
1
1
  import React from "react";
2
2
  import { CollectionConfigData } from "ps99-api";
3
- import ImageComponent from "./ImageComponent";
3
+ import ItemCard from "./ItemCard";
4
+ import { useItemResolution } from "../hooks/useItemResolution";
4
5
 
5
6
  const SprinklerComponent: React.FC<{
6
7
  configData: CollectionConfigData<"Sprinklers">;
7
8
  }> = ({ configData }) => {
9
+ const { getRarityColor } = useItemResolution();
10
+ const rarityColor = configData.Rarity ? getRarityColor(configData.Rarity) : null;
11
+
8
12
  return (
9
- <div>
10
- <h2>Sprinkler: {configData.Name}</h2>
11
- <p>Description: {configData.Desc}</p>
12
- <p>Color: {configData.Color}</p>
13
- <p>Duration: {configData.Duration} seconds</p>
14
- <h3>Rarity</h3>
15
- <p>Rarity Number: {configData.Rarity.RarityNumber}</p>
16
- <p>Display Name: {configData.Rarity.DisplayName}</p>
17
- <ImageComponent src={configData.Icon} alt={configData.Name} />
13
+ <div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
14
+
15
+ <div style={{ maxWidth: '300px', margin: '0 auto 15px auto' }}>
16
+ <ItemCard
17
+ id={configData.Name}
18
+ amount={1}
19
+ label={configData.Name}
20
+ itemData={{
21
+ icon: configData.Icon,
22
+ rarity: configData.Rarity,
23
+ name: configData.Name
24
+ }}
25
+ rarityColor={rarityColor}
26
+ />
27
+ </div>
28
+
29
+ <div style={{ fontSize: '0.9em', color: '#666', textAlign: 'center' }}>
30
+ <p>{configData.Desc}</p>
31
+ <p style={{ fontWeight: 'bold', marginTop: '5px' }}>Duration: {configData.Duration}s</p>
32
+ </div>
18
33
  </div>
19
34
  );
20
35
  };
@@ -0,0 +1,36 @@
1
+ import React, { useState } from "react";
2
+
3
+ interface TooltipProps {
4
+ content: React.ReactNode;
5
+ children: React.ReactNode;
6
+ }
7
+
8
+ const Tooltip: React.FC<TooltipProps> = ({ content, children }) => {
9
+ const [active, setActive] = useState(false);
10
+
11
+ const showTip = () => {
12
+ setActive(true);
13
+ };
14
+
15
+ const hideTip = () => {
16
+ setActive(false);
17
+ };
18
+
19
+ return (
20
+ <div
21
+ className="tooltip-wrapper"
22
+ onMouseEnter={showTip}
23
+ onMouseLeave={hideTip}
24
+ style={{ position: 'relative', display: 'inline-block' }}
25
+ >
26
+ {children}
27
+ {active && (
28
+ <div className="tooltip-popover">
29
+ {content}
30
+ </div>
31
+ )}
32
+ </div>
33
+ );
34
+ };
35
+
36
+ export default Tooltip;
@@ -1,26 +1,38 @@
1
1
  import React from "react";
2
2
  import { CollectionConfigData } from "ps99-api";
3
- import ImageComponent from "./ImageComponent";
3
+ import ItemCard from "./ItemCard";
4
+ import { useItemResolution } from "../hooks/useItemResolution";
4
5
 
5
6
  const UltimateComponent: React.FC<{
6
7
  configData: CollectionConfigData<"Ultimates">;
7
8
  }> = ({ configData }) => {
9
+ const { getRarityColor } = useItemResolution();
10
+ const rarityColor = configData.Rarity ? getRarityColor(configData.Rarity) : null;
11
+
8
12
  return (
9
- <div>
10
- <h2>Ultimate: {configData.DisplayName}</h2>
11
- <p>Description: {configData.Desc}</p>
12
- <p>Max Tier: {configData.MaxTier}</p>
13
- <p>FFlag Name: {configData.FFlagName}</p>
14
- {configData.Tradable && <p>Tradable</p>}
15
- {configData.ProductId && <p>Product ID: {configData.ProductId}</p>}
16
- {configData.NotAllowedInInstances && <p>Not Allowed in Instances</p>}
17
- <h3>Rarity</h3>
18
- <p>Rarity Number: {configData.Rarity.RarityNumber}</p>
19
- <p>Display Name: {configData.Rarity.DisplayName}</p>
20
- <ImageComponent src={configData.Icon} alt={configData.DisplayName} />
21
- <h3>Tiers</h3>
22
- <p>Tier to Level Mapping: {configData.TierToLevel.join(", ")}</p>
23
- <p>Level to Tier Mapping: {configData.LevelToTier.join(", ")}</p>
13
+ <div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
14
+
15
+ <div style={{ maxWidth: '300px', margin: '0 auto 15px auto' }}>
16
+ <ItemCard
17
+ id={configData.DisplayName}
18
+ amount={1}
19
+ label={configData.DisplayName}
20
+ itemData={{
21
+ icon: configData.Icon,
22
+ rarity: configData.Rarity,
23
+ name: configData.DisplayName
24
+ }}
25
+ rarityColor={rarityColor}
26
+ />
27
+ </div>
28
+
29
+ <div style={{ fontSize: '0.9em', color: '#666', textAlign: 'center' }}>
30
+ <p style={{ marginBottom: '10px' }}>{configData.Desc}</p>
31
+ <div style={{ display: 'flex', gap: '10px', justifyContent: 'center' }}>
32
+ <span className="badge">Max Tier: {configData.MaxTier}</span>
33
+ {configData.Tradable && <span className="badge">Tradable</span>}
34
+ </div>
35
+ </div>
24
36
  </div>
25
37
  );
26
38
  };