ps99-api 2.4.0 → 2.5.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 (87) 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 +3082 -2759
  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 +6 -9
  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/BoostsComponent.tsx +16 -5
  24. package/example-web/react2/src/components/BoothsComponent.tsx +22 -52
  25. package/example-web/react2/src/components/BoxesComponent.tsx +48 -16
  26. package/example-web/react2/src/components/BuffsComponent.tsx +82 -34
  27. package/example-web/react2/src/components/CharmsComponent.tsx +47 -24
  28. package/example-web/react2/src/components/CollectionConfigIndex.tsx +398 -35
  29. package/example-web/react2/src/components/CollectionsIndex.tsx +132 -23
  30. package/example-web/react2/src/components/CollectionsLayout.tsx +50 -0
  31. package/example-web/react2/src/components/CurrencyComponent.tsx +55 -39
  32. package/example-web/react2/src/components/DynamicCollectionConfigData.tsx +163 -15
  33. package/example-web/react2/src/components/EggsComponent.tsx +50 -12
  34. package/example-web/react2/src/components/EnchantsComponent.tsx +81 -40
  35. package/example-web/react2/src/components/FishingRodsComponent.tsx +36 -22
  36. package/example-web/react2/src/components/Footer.tsx +1 -8
  37. package/example-web/react2/src/components/FruitsComponent.tsx +40 -17
  38. package/example-web/react2/src/components/GenericFetchComponent.tsx +9 -1
  39. package/example-web/react2/src/components/GuildBattlesComponent.tsx +41 -34
  40. package/example-web/react2/src/components/Header.tsx +5 -37
  41. package/example-web/react2/src/components/HomePage.tsx +15 -17
  42. package/example-web/react2/src/components/HoverboardsComponent.tsx +23 -99
  43. package/example-web/react2/src/components/ImageComponent.tsx +255 -45
  44. package/example-web/react2/src/components/ItemCard.tsx +240 -0
  45. package/example-web/react2/src/components/LootboxesComponent.tsx +22 -7
  46. package/example-web/react2/src/components/MasteryComponent.tsx +92 -29
  47. package/example-web/react2/src/components/MerchantsComponent.tsx +41 -16
  48. package/example-web/react2/src/components/MiscItemsComponent.tsx +26 -31
  49. package/example-web/react2/src/components/PetsComponent.tsx +100 -61
  50. package/example-web/react2/src/components/PotionsComponent.tsx +43 -27
  51. package/example-web/react2/src/components/RandomEventsComponent.tsx +32 -23
  52. package/example-web/react2/src/components/RanksComponent.tsx +187 -62
  53. package/example-web/react2/src/components/RarityComponent.tsx +123 -5
  54. package/example-web/react2/src/components/RebirthsComponent.tsx +36 -19
  55. package/example-web/react2/src/components/SecretRoomsComponent.tsx +5 -4
  56. package/example-web/react2/src/components/SeedsComponent.tsx +41 -21
  57. package/example-web/react2/src/components/ShovelsComponent.tsx +21 -9
  58. package/example-web/react2/src/components/Sidebar.tsx +105 -0
  59. package/example-web/react2/src/components/SprinklersComponent.tsx +25 -10
  60. package/example-web/react2/src/components/Tooltip.tsx +36 -0
  61. package/example-web/react2/src/components/UltimatesComponent.tsx +28 -16
  62. package/example-web/react2/src/components/UpgradesComponent.tsx +96 -47
  63. package/example-web/react2/src/components/WateringCansComponent.tsx +20 -14
  64. package/example-web/react2/src/components/WorldsComponent.tsx +21 -11
  65. package/example-web/react2/src/components/XPPotionsComponent.tsx +28 -11
  66. package/example-web/react2/src/components/ZoneFlagsComponent.tsx +25 -14
  67. package/example-web/react2/src/components/ZonesComponent.tsx +43 -60
  68. package/example-web/react2/src/constants/collectionIcons.ts +29 -0
  69. package/example-web/react2/src/context/CollectionDataContext.tsx +62 -0
  70. package/example-web/react2/src/hooks/useExpandableList.ts +38 -0
  71. package/example-web/react2/src/hooks/useItemResolution.ts +351 -0
  72. package/example-web/react2/src/index.css +257 -0
  73. package/example-web/react2/src/index.tsx +2 -1
  74. package/example-web/react2/temp_model.rbxm +0 -0
  75. package/example-web/react2/webpack.config.js +103 -47
  76. package/package.json +11 -11
  77. package/ranks.json +1 -0
  78. package/repro_collection_fetch.ts +33 -0
  79. package/repro_image_fetch.ts +50 -0
  80. package/src/__tests__/__snapshots__/ps99-api-changes.ts.snap +34841 -10439
  81. package/src/__tests__/__snapshots__/ps99-api-live.ts.snap +160667 -67217
  82. package/src/ps99-api.ts +9 -5
  83. package/src/request-client/axios.ts +6 -2
  84. package/src/responses/collection/index.ts +1 -0
  85. package/src/responses/collection/rarity.ts +1 -0
  86. package/tsconfig.json +1 -1
  87. package/example-web/react2/public/service-worker.js +0 -63
@@ -1,46 +1,109 @@
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 { useExpandableList } from "../hooks/useExpandableList";
4
5
 
5
6
  const MasteryComponent: React.FC<{
6
7
  configData: CollectionConfigData<"Mastery">;
7
8
  }> = ({ configData }) => {
8
- const renderPerks = (perks: any) => {
9
- return Object.entries(perks).map(
10
- ([perkType, perkDetails]: [string, any]) => (
11
- <div key={perkType}>
12
- <h3>{perkType}</h3>
13
- {perkDetails.map((detail: any, index: number) => (
14
- <div key={index}>
15
- <p>Level: {detail.Level}</p>
16
- <p>Title: {detail.Title}</p>
17
- <p>Description: {detail.Text}</p>
18
- {detail.Power && <p>Power: {detail.Power}</p>}
9
+ const perksEntries = React.useMemo(() => configData.Perks ? Object.entries(configData.Perks) : [], [configData.Perks]);
10
+ const { expandedIndices, toggle, expandAll, collapseAll, isExpanded } = useExpandableList(perksEntries.length);
11
+
12
+ const renderPerks = () => {
13
+ return perksEntries.map(
14
+ ([perkType, perkDetails]: [string, any], index: number) => (
15
+ <div key={perkType} style={{ marginBottom: '15px', textAlign: 'left', background: '#f9f9f9', padding: '10px', borderRadius: '8px' }}>
16
+ <h4
17
+ onClick={() => toggle(index)}
18
+ style={{ margin: '0 0 10px 0', color: '#333', cursor: 'pointer', display: 'flex', alignItems: 'center', gap: '8px' }}
19
+ >
20
+ {isExpanded(index) ? '▼' : '▶'} {perkType}
21
+ </h4>
22
+ {isExpanded(index) && (
23
+ <div>
24
+ {perkDetails.map((detail: any, idx: number) => (
25
+ <div key={idx} style={{ marginBottom: '8px', paddingLeft: '10px', borderLeft: '2px solid #ddd' }}>
26
+ <div style={{ fontWeight: 'bold', fontSize: '0.9em' }}>Lvl {detail.Level}: {detail.Title}</div>
27
+ <div style={{ fontSize: '0.9em', color: '#666' }}>{detail.Text}</div>
28
+ {detail.Power && <div style={{ fontSize: '0.8em', color: '#1976d2' }}>Power: {detail.Power}</div>}
29
+ </div>
30
+ ))}
19
31
  </div>
20
- ))}
32
+ )}
21
33
  </div>
22
34
  ),
23
35
  );
24
36
  };
25
37
 
26
38
  return (
27
- <div>
28
- <h2>{configData.Name}</h2>
29
- <ImageComponent src={configData.Icon} alt={configData.Name} />
30
- <p>Description: {configData.Desc}</p>
31
- {configData.ToggleablePerks && (
32
- <div>
33
- <h3>Toggleable Perks</h3>
34
- {Object.entries(configData.ToggleablePerks).map(([perk, value]) => (
35
- <p key={perk}>
36
- {perk}: {value ? "Enabled" : "Disabled"}
37
- </p>
38
- ))}
39
+ <div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
40
+
41
+ <div style={{
42
+ display: 'grid',
43
+ gridTemplateColumns: 'minmax(300px, 1fr) 2fr',
44
+ gap: '40px',
45
+ height: '100%',
46
+ alignItems: 'start'
47
+ }}>
48
+
49
+ {/* Left Column: Icon */}
50
+ <div style={{
51
+ display: 'flex',
52
+ flexDirection: 'column',
53
+ alignItems: 'center',
54
+ gap: '20px',
55
+ background: '#f9f9f9',
56
+ padding: '30px',
57
+ borderRadius: '24px',
58
+ border: '2px solid #eee'
59
+ }}>
60
+ <div style={{ width: '100%', maxWidth: '300px' }}>
61
+ <ItemCard
62
+ id={configData.Name}
63
+ amount={1}
64
+ label={configData.Name}
65
+ itemData={{
66
+ icon: configData.Icon,
67
+ rarity: undefined,
68
+ name: configData.Name
69
+ }}
70
+ rarityColor={null}
71
+ />
72
+ </div>
73
+ <p style={{ fontStyle: 'italic', textAlign: 'center', color: '#666' }}>{configData.Desc}</p>
39
74
  </div>
40
- )}
41
- <div>
42
- <h3>Perks</h3>
43
- {renderPerks(configData.Perks)}
75
+
76
+ {/* Right Column: Perks */}
77
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '20px' }}>
78
+
79
+ {configData.ToggleablePerks && (
80
+ <div style={{ background: '#fff', padding: '20px', borderRadius: '16px', border: '1px solid #eee', boxShadow: '0 2px 4px rgba(0,0,0,0.02)' }}>
81
+ <h3 style={{ fontSize: '1.1em', marginTop: 0, borderBottom: '1px solid #eee', paddingBottom: '10px' }}>Toggleable Perks</h3>
82
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px' }}>
83
+ {Object.entries(configData.ToggleablePerks).map(([perk, value]) => (
84
+ <span key={perk} className="badge" style={{ background: value ? '#e8f5e9' : '#ffebee', color: value ? '#2e7d32' : '#c62828', padding: '8px 12px', fontSize: '0.9rem' }}>
85
+ {perk}: {value ? "Enabled" : "Disabled"}
86
+ </span>
87
+ ))}
88
+ </div>
89
+ </div>
90
+ )}
91
+
92
+ {configData.Perks && (
93
+ <div style={{ background: '#fff' }}>
94
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '15px' }}>
95
+ <h3 style={{ fontSize: '1.4em', margin: 0 }}>Mastery Perks</h3>
96
+ <div style={{ display: 'flex', gap: '10px' }}>
97
+ <button onClick={expandAll} style={{ padding: '6px 12px', borderRadius: '6px', border: '1px solid #ddd', background: '#fff', cursor: 'pointer', fontSize: '0.9rem' }}>Expand All</button>
98
+ <button onClick={collapseAll} style={{ padding: '6px 12px', borderRadius: '6px', border: '1px solid #ddd', background: '#fff', cursor: 'pointer', fontSize: '0.9rem' }}>Collapse All</button>
99
+ </div>
100
+ </div>
101
+ {renderPerks()}
102
+ </div>
103
+ )}
104
+
105
+ </div>
106
+
44
107
  </div>
45
108
  </div>
46
109
  );
@@ -7,28 +7,53 @@ const MerchantsComponent: React.FC<{
7
7
  const renderStockRange = (stockRange: number[][] | undefined) => {
8
8
  if (!stockRange) return null;
9
9
  return stockRange.map((range, index) => (
10
- <div key={index}>
11
- <p>
12
- Level {index + 1}: {range[0]} - {range[1]}
13
- </p>
10
+ <div key={index} style={{ background: '#e1f5fe', padding: '8px', borderRadius: '4px', textAlign: 'center' }}>
11
+ <strong>Lvl {index + 1}</strong>
12
+ <div>{range[0]} - {range[1]} items</div>
14
13
  </div>
15
14
  ));
16
15
  };
17
16
 
18
17
  return (
19
- <div>
20
- <h2>{configData.DisplayName}</h2>
21
- <p>Price Multiplier: {configData.PriceMult}</p>
22
- <p>Machine Name: {configData.MachineName}</p>
23
- <p>Refresh Rate: {configData.RefreshRate} seconds</p>
24
- {configData.HideNotification && <p>Notification: Hidden</p>}
25
- {configData.HideRespect && <p>Respect: Hidden</p>}
26
- {configData.IsStatic && <p>Static Merchant</p>}
27
- {renderStockRange(configData.StockRangeByRespectLevel)}
18
+ <div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
19
+ <div style={{ textAlign: 'left', width: '100%', marginBottom: '15px' }}>
20
+ <p><strong>Machine:</strong> {configData.MachineName}</p>
21
+ <p><strong>Price Mult:</strong> {configData.PriceMult}x</p>
22
+ <p><strong>Refresh:</strong> {configData.RefreshRate}s</p>
23
+ {configData.IsStatic && <p><strong>Type:</strong> Static Merchant</p>}
24
+ </div>
25
+
26
+ {configData.StockRangeByRespectLevel && (
27
+ <div style={{ width: '100%', marginBottom: '15px' }}>
28
+ <h4>Stock Ranges</h4>
29
+ <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(120px, 1fr))', gap: '10px' }}>
30
+ {renderStockRange(configData.StockRangeByRespectLevel)}
31
+ </div>
32
+ </div>
33
+ )}
34
+
28
35
  {configData.SlotRespectLevels && (
29
- <div>
30
- <h3>Slot Respect Levels</h3>
31
- <p>{configData.SlotRespectLevels.join(", ")}</p>
36
+ <div style={{ width: '100%' }}>
37
+ <h4>Slot Respect Levels</h4>
38
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px', justifyContent: 'center' }}>
39
+ {configData.SlotRespectLevels.map((level, idx) => (
40
+ <span key={idx} style={{
41
+ background: '#fff9c4',
42
+ color: '#fbc02d',
43
+ padding: '5px 10px',
44
+ borderRadius: '50%',
45
+ width: '30px',
46
+ height: '30px',
47
+ display: 'flex',
48
+ alignItems: 'center',
49
+ justifyContent: 'center',
50
+ fontWeight: 'bold',
51
+ border: '2px solid #fbc02d'
52
+ }}>
53
+ {level}
54
+ </span>
55
+ ))}
56
+ </div>
32
57
  </div>
33
58
  )}
34
59
  </div>
@@ -1,42 +1,37 @@
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 MiscItemsComponent: React.FC<{
6
7
  configData: CollectionConfigData<"MiscItems">;
7
8
  }> = ({ configData }) => {
9
+ const { getRarityColor } = useItemResolution();
10
+ const rarityColor = configData.Rarity ? getRarityColor(configData.Rarity) : null;
11
+
8
12
  return (
9
- <div
10
- style={{
11
- padding: "1em",
12
- border: "1px solid #ccc",
13
- borderRadius: "8px",
14
- backgroundColor: "#f9f9f9",
15
- }}
16
- >
17
- <h2 style={{ borderBottom: "2px solid #ccc", paddingBottom: "0.5em" }}>
18
- {configData.DisplayName}
19
- </h2>
20
- <p>
21
- <strong>Category:</strong> {configData.Category}
22
- </p>
23
- <ImageComponent src={configData.Icon} alt={configData.DisplayName} />
24
- {configData.AltIcon && (
25
- <ImageComponent
26
- src={configData.AltIcon}
27
- alt={`${configData.DisplayName} (Alternate)`}
13
+ <div style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: '30px' }}>
14
+
15
+ <div style={{ width: '100%', maxWidth: '400px' }}>
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}
28
26
  />
29
- )}
30
- <p>
31
- <strong>Description:</strong> {configData.Desc}
32
- </p>
33
- <p>
34
- <strong>Rarity:</strong> {configData.Rarity.DisplayName} (Rarity Number:{" "}
35
- {configData.Rarity.RarityNumber})
36
- </p>
37
- <p>
38
- <strong>Tradable:</strong> {configData.Tradable ? "Yes" : "No"}
39
- </p>
27
+ </div>
28
+
29
+ <div style={{ fontSize: '1.1em', color: '#555', textAlign: 'center', maxWidth: '600px', lineHeight: '1.6' }}>
30
+ <p style={{ marginBottom: '20px', background: '#fff', padding: '20px', borderRadius: '12px', border: '1px solid #eee' }}>"{configData.Desc}"</p>
31
+ <div style={{ display: 'flex', gap: '10px', justifyContent: 'center' }}>
32
+ {configData.Tradable && <span className="badge" style={{ background: '#9c27b0', color: 'white', padding: '8px 16px', borderRadius: '20px', fontWeight: 'bold' }}>Tradable</span>}
33
+ </div>
34
+ </div>
40
35
  </div>
41
36
  );
42
37
  };
@@ -1,12 +1,17 @@
1
1
  import React from "react";
2
2
  import { CollectionConfigData } from "ps99-api";
3
3
  import ImageComponent from "./ImageComponent";
4
+ import ItemCard from "./ItemCard";
5
+ import { useItemResolution } from "../hooks/useItemResolution";
4
6
 
5
7
  const PetsComponent: React.FC<{
6
8
  configData: CollectionConfigData<"Pets">;
7
9
  displayType?: "all" | "specific";
8
10
  pt?: number;
9
11
  }> = ({ configData, displayType = "all", pt }) => {
12
+ const { getRarityColor } = useItemResolution();
13
+ const rarityColor = (configData as any).rarity ? getRarityColor((configData as any).rarity) : null;
14
+
10
15
  const getVariationName = () => {
11
16
  if (pt === 1) return "(Golden)";
12
17
  if (pt === 2) return "(Rainbow)";
@@ -14,79 +19,113 @@ const PetsComponent: React.FC<{
14
19
  };
15
20
 
16
21
  return (
17
- <div style={{ padding: "1em", border: "1px solid #ccc", borderRadius: "8px", backgroundColor: "#f9f9f9" }}>
18
- <h2 style={{ borderBottom: "2px solid #ccc", paddingBottom: "0.5em" }}>
19
- {configData.name} {getVariationName()}
20
- </h2>
21
- {displayType === "all" && (
22
- <>
23
- <ImageComponent src={configData.thumbnail} alt={configData.name} />
22
+ <div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
23
+ {/* 2-Column Grid Layout */}
24
+ <div style={{
25
+ display: 'grid',
26
+ gridTemplateColumns: 'minmax(300px, 1fr) 2fr', // Left (Image) : Right (Details)
27
+ gap: '40px',
28
+ height: '100%',
29
+ alignItems: 'start'
30
+ }}>
31
+
32
+ {/* Left Column: Images */}
33
+ <div style={{
34
+ display: 'flex',
35
+ flexDirection: 'column',
36
+ alignItems: 'center',
37
+ gap: '20px'
38
+ }}>
39
+ {/* Main Image */}
40
+ <div style={{ width: '250px', height: '250px' }}>
41
+ <ImageComponent src={configData.thumbnail} alt={configData.name} />
42
+ </div>
43
+ {/* Golden Variant if exists */}
24
44
  {configData.goldenThumbnail && (
25
- <ImageComponent
26
- src={configData.goldenThumbnail}
27
- alt={`${configData.name} (Golden)`}
28
- />
29
- )}
30
- </>
31
- )}
32
- {displayType === "specific" && (
33
- <>
34
- {pt === 1 && configData.goldenThumbnail ? (
35
- <div style={{ minWidth: '250px' }}>
45
+ <div style={{ width: '150px', height: '150px', opacity: 0.8 }}>
46
+ <p style={{ textAlign: 'center', margin: '0 0 5px 0', fontSize: '0.9rem', fontWeight: 'bold', color: '#888' }}>Golden Variant</p>
36
47
  <ImageComponent
37
48
  src={configData.goldenThumbnail}
38
49
  alt={`${configData.name} (Golden)`}
39
50
  />
40
51
  </div>
41
- ) : (
42
- <div style={{ minWidth: '250px', outline: pt === 2 ? '4px solid #FFD700' : 'none', borderRadius: '8px' }}>
43
- <ImageComponent
44
- src={configData.thumbnail}
45
- alt={configData.name}
46
- />
47
- </div>
48
52
  )}
49
- </>
50
- )}
51
- <p><strong>From World Number:</strong> {configData.fromWorldNumber}</p>
52
- <p><strong>From Zone Number:</strong> {configData.fromZoneNumber}</p>
53
- {configData.indexObtainable && <p><strong>Index Obtainable:</strong> Yes</p>}
54
- {configData.huge && <p><strong>Huge:</strong> Yes</p>}
55
- {configData.fly && <p><strong>Can Fly:</strong> Yes</p>}
56
- {configData.tradable && <p><strong>Tradable:</strong> Yes</p>}
57
- {configData.secret && <p><strong>Secret:</strong> Yes</p>}
58
- {configData.hidden && <p><strong>Hidden:</strong> Yes</p>}
59
- {configData.cachedPower && (
60
- <div>
61
- <h3>Cached Power:</h3>
62
- <ul>
63
- {configData.cachedPower.map((power, index) => (
64
- <li key={index}>{power}</li>
65
- ))}
66
- </ul>
67
53
  </div>
68
- )}
69
- {configData.animations && (
70
- <div>
71
- <h3>Animations:</h3>
72
- <ul>
73
- {Object.entries(configData.animations).map(
74
- ([key, value], index) => (
75
- <li key={index}>
76
- {key}: {JSON.stringify(value)}
77
- </li>
78
- ),
54
+
55
+ {/* Right Column: details */}
56
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '20px' }}>
57
+
58
+ {/* Stats Grid */}
59
+ <div style={{
60
+ display: 'grid',
61
+ gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))',
62
+ gap: '20px',
63
+ width: '100%'
64
+ }}>
65
+ <div style={{ background: '#f5f5f5', padding: '15px', borderRadius: '12px', border: '1px solid #ddd' }}>
66
+ <strong style={{ display: 'block', fontSize: '0.9rem', color: '#666', marginBottom: '5px' }}>FROM WORLD</strong>
67
+ <span style={{ fontSize: '1.5rem', fontWeight: 'bold', color: '#333' }}>{configData.fromWorldNumber || "?"}</span>
68
+ </div>
69
+ <div style={{ background: '#f5f5f5', padding: '15px', borderRadius: '12px', border: '1px solid #ddd' }}>
70
+ <strong style={{ display: 'block', fontSize: '0.9rem', color: '#666', marginBottom: '5px' }}>FROM ZONE</strong>
71
+ <span style={{ fontSize: '1.5rem', fontWeight: 'bold', color: '#333' }}>{configData.fromZoneNumber || "?"}</span>
72
+ </div>
73
+ {configData.power && (
74
+ <div style={{ background: '#e3f2fd', padding: '15px', borderRadius: '12px', border: '1px solid #90caf9' }}>
75
+ <strong style={{ display: 'block', fontSize: '0.9rem', color: '#1565c0', marginBottom: '5px' }}>POWER</strong>
76
+ <span style={{ fontSize: '1.5rem', fontWeight: 'bold', color: '#1565c0' }}>{configData.power}</span>
77
+ </div>
79
78
  )}
80
- </ul>
79
+ {configData.exclusiveLevel && (
80
+ <div style={{ background: '#f3e5f5', padding: '15px', borderRadius: '12px', border: '1px solid #ce93d8' }}>
81
+ <strong style={{ display: 'block', fontSize: '0.9rem', color: '#7b1fa2', marginBottom: '5px' }}>EXCLUSIVE LEVEL</strong>
82
+ <span style={{ fontSize: '1.5rem', fontWeight: 'bold', color: '#7b1fa2' }}>{configData.exclusiveLevel}</span>
83
+ </div>
84
+ )}
85
+ </div>
86
+
87
+ {/* Badges */}
88
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '10px' }}>
89
+ {configData.indexObtainable && <span className="badge" style={{ background: '#4caf50', color: 'white', padding: '8px 16px', borderRadius: '20px', fontWeight: 'bold' }}>Index Obtainable</span>}
90
+ {configData.huge && <span className="badge" style={{ background: '#ff9800', color: 'white', padding: '8px 16px', borderRadius: '20px', fontWeight: 'bold' }}>HUGE</span>}
91
+ {configData.fly && <span className="badge" style={{ background: '#2196f3', color: 'white', padding: '8px 16px', borderRadius: '20px', fontWeight: 'bold' }}>Fly</span>}
92
+ {configData.tradable && <span className="badge" style={{ background: '#9c27b0', color: 'white', padding: '8px 16px', borderRadius: '20px', fontWeight: 'bold' }}>Tradable</span>}
93
+ {configData.secret && <span className="badge" style={{ background: '#607d8b', color: 'white', padding: '8px 16px', borderRadius: '20px', fontWeight: 'bold' }}>Secret</span>}
94
+ {configData.hidden && <span className="badge" style={{ background: '#333', color: 'white', padding: '8px 16px', borderRadius: '20px', fontWeight: 'bold' }}>Hidden</span>}
95
+ </div>
96
+
97
+ {/* Description / Index Desc */}
98
+ {configData.indexDesc && (
99
+ <div style={{
100
+ marginTop: '10px',
101
+ padding: '20px',
102
+ background: '#fff3e0',
103
+ borderLeft: '5px solid #ff9800',
104
+ borderRadius: '4px',
105
+ fontStyle: 'italic',
106
+ fontSize: '1.1rem',
107
+ lineHeight: '1.6',
108
+ color: '#5d4037'
109
+ }}>
110
+ "{configData.indexDesc}"
111
+ </div>
112
+ )}
113
+
114
+ {/* Cached Power (Debug/Extra info) */}
115
+ {configData.cachedPower && (
116
+ <div style={{ marginTop: 'auto', paddingTop: '20px', borderTop: '1px solid #eee' }}>
117
+ <h4 style={{ margin: '0 0 10px 0', color: '#999', fontSize: '0.8rem', textTransform: 'uppercase' }}>Technical Data</h4>
118
+ <div style={{ fontSize: '0.86rem', color: '#aaa', fontFamily: 'monospace' }}>
119
+ Cached Power: {configData.cachedPower.join(', ')}
120
+ </div>
121
+ </div>
122
+ )}
123
+
81
124
  </div>
82
- )}
83
- {configData.indexDesc && <p><strong>Description:</strong> {configData.indexDesc}</p>}
84
- {configData.exclusiveLevel && (
85
- <p><strong>Exclusive Level:</strong> {configData.exclusiveLevel}</p>
86
- )}
87
- {configData.power && <p><strong>Power:</strong> {configData.power}</p>}
125
+ </div>
88
126
  </div>
89
127
  );
90
128
  };
91
129
 
130
+
92
131
  export default PetsComponent;
@@ -1,46 +1,62 @@
1
1
  import React from "react";
2
2
  import { CollectionConfigData } from "ps99-api";
3
+ import ItemCard from "./ItemCard";
3
4
  import ImageComponent from "./ImageComponent";
5
+ import { useItemResolution } from "../hooks/useItemResolution";
4
6
 
5
7
  const PotionsComponent: React.FC<{
6
8
  configData: CollectionConfigData<"Potions">;
7
9
  }> = ({ configData }) => {
10
+ const { getRarityColor } = useItemResolution();
11
+
8
12
  return (
9
- <div style={{ padding: "1em", border: "1px solid #ccc", borderRadius: "8px", backgroundColor: "#f9f9f9" }}>
10
- <h2 style={{ borderBottom: "2px solid #ccc", paddingBottom: "0.5em" }}>
11
- Potion: {configData.Tiers[0].DisplayName}
12
- </h2>
13
- <div style={{ display: 'flex', gap: '1em' }}>
14
- <div style={{ minWidth: '250px' }}>
13
+
14
+ <div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
15
+
16
+ <div style={{ display: 'flex', gap: '20px', alignItems: 'center', marginBottom: '20px', flexWrap: 'wrap', justifyContent: 'center' }}>
17
+ <div style={{ width: '150px', height: '150px' }}>
15
18
  <ImageComponent
16
19
  src={configData.Tiers[0].Icon}
17
20
  alt={configData.Tiers[0].DisplayName}
18
21
  />
19
22
  </div>
20
- <div>
21
- <p><strong>Description:</strong> {configData.Tiers[0].Desc}</p>
22
- <p><strong>Primary Color:</strong> {configData.PrimaryColor}</p>
23
- <p><strong>Secondary Color:</strong> {configData.SecondaryColor}</p>
24
- <p><strong>Base Tier:</strong> {configData.BaseTier}</p>
25
- <p><strong>Max Tier:</strong> {configData.MaxTier}</p>
23
+ <div style={{ textAlign: 'left', flex: 1, minWidth: '200px' }}>
24
+ <p style={{ marginBottom: '8px' }}><strong>Description:</strong> {configData.Tiers[0].Desc}</p>
25
+ <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '8px' }}>
26
+ <p><strong>Primary:</strong> <span style={{ color: configData.PrimaryColor }}>{configData.PrimaryColor}</span></p>
27
+ <p><strong>Secondary:</strong> <span style={{ color: configData.SecondaryColor }}>{configData.SecondaryColor}</span></p>
28
+ <p><strong>Base Tier:</strong> {configData.BaseTier}</p>
29
+ <p><strong>Max Tier:</strong> {configData.MaxTier}</p>
30
+ </div>
26
31
  </div>
27
32
  </div>
28
- <div style={{ marginTop: '1em' }}>
29
- <h3>Tiers:</h3>
30
- <div style={{ display: 'flex', flexWrap: 'wrap', gap: '1em' }}>
31
- {configData.Tiers.map((tier, index) => (
32
- <div key={index} style={{ border: '1px solid #ccc', borderRadius: '8px', padding: '1em', flex: '1 1 calc(33% - 1em)', boxSizing: 'border-box' }}>
33
- <p><strong>Tier {index + 1}:</strong></p>
34
- <p><strong>Display Name:</strong> {tier.DisplayName}</p>
35
- <p><strong>Description:</strong> {tier.Desc}</p>
36
- <div style={{ minWidth: '250px' }}>
37
- <ImageComponent src={tier.Icon} alt={tier.DisplayName} />
33
+
34
+ <div style={{ marginTop: '20px' }}>
35
+ <h3>Tiers</h3>
36
+ <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(220px, 1fr))', gap: '15px' }}>
37
+ {configData.Tiers.map((tier, index) => {
38
+ const rarityColor = tier.Rarity ? getRarityColor(tier.Rarity) : null;
39
+ return (
40
+ <div key={index} style={{ marginBottom: '10px' }}>
41
+ <ItemCard
42
+ id={tier.DisplayName}
43
+ amount={1}
44
+ label={tier.DisplayName}
45
+ tn={index + 1}
46
+ itemData={{
47
+ icon: tier.Icon,
48
+ rarity: tier.Rarity,
49
+ name: tier.DisplayName
50
+ }}
51
+ rarityColor={rarityColor}
52
+ />
53
+ <div style={{ marginTop: '10px', fontSize: '0.9em', color: '#666', textAlign: 'center' }}>
54
+ <p style={{ fontWeight: 'bold' }}>Power: {tier.Power}</p>
55
+ <p>Time: {tier.Time}s</p>
56
+ </div>
38
57
  </div>
39
- <p><strong>Power:</strong> {tier.Power}</p>
40
- <p><strong>Time:</strong> {tier.Time}</p>
41
- <p><strong>Rarity:</strong> {tier.Rarity.DisplayName}</p>
42
- </div>
43
- ))}
58
+ );
59
+ })}
44
60
  </div>
45
61
  </div>
46
62
  </div>
@@ -1,38 +1,47 @@
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 RandomEventsComponent: React.FC<{
6
6
  configData: CollectionConfigData<"RandomEvents">;
7
7
  }> = ({ configData }) => {
8
8
  return (
9
- <div>
10
- <h2>Random Event: {configData.Name}</h2>
11
- <div>
12
- <ImageComponent src={configData.Icon} alt={configData.Name} />
13
- <p>Color: {configData.Color}</p>
14
- <p>Duration: {configData.Duration} seconds</p>
15
- <p>Breaking Requirement: {configData.BreakingRequirement}</p>
16
- <p>Playtime Requirement: {configData.PlaytimeRequirement} minutes</p>
17
- <p>Chance: {configData.Chance}</p>
18
- <p>Allow in Zones: {configData.AllowInZones ? "Yes" : "No"}</p>
19
- <p>Allow in Instances: {configData.AllowInInstances ? "Yes" : "No"}</p>
20
- <p>Allow Multiple: {configData.AllowMultiple ? "Yes" : "No"}</p>
21
- {configData.MinimumZone && (
22
- <p>Minimum Zone: {configData.MinimumZone}</p>
23
- )}
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.Name}
14
+ amount={1}
15
+ label={configData.Name}
16
+ itemData={{
17
+ icon: configData.Icon,
18
+ rarity: undefined,
19
+ name: configData.Name
20
+ }}
21
+ rarityColor={configData.Color}
22
+ />
23
+ </div>
24
+
25
+ <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '10px', marginBottom: '20px', textAlign: 'left' }}>
26
+ <p><strong>Color:</strong> <span style={{ color: configData.Color }}>{configData.Color}</span></p>
27
+ <p><strong>Duration:</strong> {configData.Duration}s</p>
28
+ <p><strong>Chance:</strong> {configData.Chance}</p>
29
+ {configData.BreakingRequirement && <p><strong>Break Req:</strong> {configData.BreakingRequirement}</p>}
24
30
  </div>
25
- <div>
26
- <h3>Area Whitelist:</h3>
27
- <ul>
31
+
32
+ <div style={{ borderTop: '1px solid #eee', paddingTop: '15px' }}>
33
+ <h3 style={{ fontSize: '1.1em', marginBottom: '10px' }}>Area Whitelist</h3>
34
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '5px', justifyContent: 'center' }}>
28
35
  {Object.entries(configData.AreaWhitelist).map(
29
36
  ([area, allowed], index) => (
30
- <li key={index}>
31
- {area.replace("_", " ")}: {allowed ? "Yes" : "No"}
32
- </li>
37
+ allowed && (
38
+ <span key={index} className="badge">
39
+ {area.replace(/_/g, " ")}
40
+ </span>
41
+ )
33
42
  ),
34
43
  )}
35
- </ul>
44
+ </div>
36
45
  </div>
37
46
  </div>
38
47
  );