ps99-api 2.3.3 → 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 (99) hide show
  1. package/.github/workflows/release-on-main.yml +1 -2
  2. package/.idea/node-ps99-api.iml +1 -0
  3. package/.idea/runConfigurations/test_changing.xml +1 -1
  4. package/README.md +1 -1
  5. package/debug_currency.json +57 -0
  6. package/debug_goals.json +271 -0
  7. package/dist/ps99-api.d.ts +2 -0
  8. package/dist/ps99-api.js +4 -1
  9. package/dist/ps99-api.js.map +1 -1
  10. package/dist/request-client/axios.js +6 -1
  11. package/dist/request-client/axios.js.map +1 -1
  12. package/dist/responses/collection/achievement.d.ts +2 -0
  13. package/dist/responses/collection/guild-battle.d.ts +2 -4
  14. package/dist/responses/collection/index.d.ts +1 -0
  15. package/dist/responses/collection/index.js +15 -0
  16. package/dist/responses/collection/index.js.map +1 -1
  17. package/dist/responses/collection/rank.d.ts +1 -0
  18. package/dist/responses/collection/rarity.d.ts +1 -0
  19. package/dist/responses/collection/seed.d.ts +1 -0
  20. package/dist/responses/exists.d.ts +2 -0
  21. package/example-web/react/package-lock.json +1504 -1470
  22. package/example-web/react2/package-lock.json +3082 -2759
  23. package/example-web/react2/package.json +6 -1
  24. package/example-web/react2/public/assets/gold_variant_icon.png +0 -0
  25. package/example-web/react2/public/assets/hot_cocoa_egg.png +0 -0
  26. package/example-web/react2/public/index.html +34 -31
  27. package/example-web/react2/src/App.tsx +6 -9
  28. package/example-web/react2/src/assets/guild_placeholder.png +0 -0
  29. package/example-web/react2/src/components/AchievementsComponent.tsx +78 -30
  30. package/example-web/react2/src/components/BoostsComponent.tsx +18 -14
  31. package/example-web/react2/src/components/BoothsComponent.tsx +24 -22
  32. package/example-web/react2/src/components/BoxesComponent.tsx +46 -21
  33. package/example-web/react2/src/components/BuffsComponent.tsx +83 -13
  34. package/example-web/react2/src/components/CharmsComponent.tsx +47 -29
  35. package/example-web/react2/src/components/CollectionConfigIndex.tsx +398 -35
  36. package/example-web/react2/src/components/CollectionsIndex.tsx +132 -23
  37. package/example-web/react2/src/components/CollectionsLayout.tsx +50 -0
  38. package/example-web/react2/src/components/CurrencyComponent.tsx +59 -50
  39. package/example-web/react2/src/components/DynamicCollectionConfigData.tsx +178 -11
  40. package/example-web/react2/src/components/EggsComponent.tsx +77 -44
  41. package/example-web/react2/src/components/EnchantsComponent.tsx +84 -34
  42. package/example-web/react2/src/components/FishingRodsComponent.tsx +38 -31
  43. package/example-web/react2/src/components/Footer.tsx +75 -18
  44. package/example-web/react2/src/components/FruitsComponent.tsx +41 -25
  45. package/example-web/react2/src/components/GenericFetchComponent.tsx +40 -22
  46. package/example-web/react2/src/components/GuildBattlesComponent.tsx +93 -65
  47. package/example-web/react2/src/components/Header.tsx +5 -37
  48. package/example-web/react2/src/components/HomePage.tsx +16 -16
  49. package/example-web/react2/src/components/HoverboardsComponent.tsx +25 -37
  50. package/example-web/react2/src/components/ImageComponent.tsx +255 -45
  51. package/example-web/react2/src/components/ItemCard.tsx +240 -0
  52. package/example-web/react2/src/components/LootboxesComponent.tsx +24 -16
  53. package/example-web/react2/src/components/MasteryComponent.tsx +93 -37
  54. package/example-web/react2/src/components/MerchantsComponent.tsx +46 -28
  55. package/example-web/react2/src/components/MiscItemsComponent.tsx +28 -26
  56. package/example-web/react2/src/components/PetsComponent.tsx +115 -46
  57. package/example-web/react2/src/components/PotionsComponent.tsx +53 -36
  58. package/example-web/react2/src/components/RandomEventsComponent.tsx +39 -35
  59. package/example-web/react2/src/components/RanksComponent.tsx +187 -71
  60. package/example-web/react2/src/components/RarityComponent.tsx +124 -13
  61. package/example-web/react2/src/components/RebirthsComponent.tsx +37 -26
  62. package/example-web/react2/src/components/SecretRoomsComponent.tsx +7 -13
  63. package/example-web/react2/src/components/SeedsComponent.tsx +45 -32
  64. package/example-web/react2/src/components/ShovelsComponent.tsx +23 -18
  65. package/example-web/react2/src/components/Sidebar.tsx +105 -0
  66. package/example-web/react2/src/components/SprinklersComponent.tsx +27 -19
  67. package/example-web/react2/src/components/Tooltip.tsx +36 -0
  68. package/example-web/react2/src/components/UltimatesComponent.tsx +29 -24
  69. package/example-web/react2/src/components/UpgradesComponent.tsx +99 -55
  70. package/example-web/react2/src/components/WateringCansComponent.tsx +23 -21
  71. package/example-web/react2/src/components/WorldsComponent.tsx +27 -24
  72. package/example-web/react2/src/components/XPPotionsComponent.tsx +29 -19
  73. package/example-web/react2/src/components/ZoneFlagsComponent.tsx +27 -23
  74. package/example-web/react2/src/components/ZonesComponent.tsx +56 -73
  75. package/example-web/react2/src/constants/collectionIcons.ts +29 -0
  76. package/example-web/react2/src/context/CollectionDataContext.tsx +62 -0
  77. package/example-web/react2/src/hooks/useExpandableList.ts +38 -0
  78. package/example-web/react2/src/hooks/useItemResolution.ts +351 -0
  79. package/example-web/react2/src/index.css +257 -0
  80. package/example-web/react2/src/index.tsx +2 -1
  81. package/example-web/react2/temp_model.rbxm +0 -0
  82. package/example-web/react2/webpack.config.js +103 -47
  83. package/package.json +11 -11
  84. package/ranks.json +1 -0
  85. package/repro_collection_fetch.ts +33 -0
  86. package/repro_image_fetch.ts +50 -0
  87. package/src/__tests__/__snapshots__/ps99-api-changes.ts.snap +34841 -10439
  88. package/src/__tests__/__snapshots__/ps99-api-live.ts.snap +160667 -67217
  89. package/src/ps99-api.ts +9 -5
  90. package/src/request-client/axios.ts +6 -2
  91. package/src/responses/collection/achievement.ts +2 -0
  92. package/src/responses/collection/guild-battle.ts +2 -4
  93. package/src/responses/collection/index.ts +1 -0
  94. package/src/responses/collection/rank.ts +1 -0
  95. package/src/responses/collection/rarity.ts +1 -0
  96. package/src/responses/collection/seed.ts +1 -0
  97. package/src/responses/exists.ts +2 -0
  98. package/tsconfig.json +1 -1
  99. package/example-web/react2/public/service-worker.js +0 -63
@@ -1,45 +1,49 @@
1
1
  import React from "react";
2
2
  import { CollectionConfigData } from "ps99-api";
3
- import { GenericFetchComponent } from "./GenericFetchComponent";
4
- import ImageComponent from "./ImageComponent";
3
+ import ItemCard from "./ItemCard";
5
4
 
6
5
  const RandomEventsComponent: React.FC<{
7
- configData?: CollectionConfigData<"RandomEvents">;
6
+ configData: CollectionConfigData<"RandomEvents">;
8
7
  }> = ({ configData }) => {
9
8
  return (
10
- <GenericFetchComponent<CollectionConfigData<"RandomEvents">>
11
- collectionName="RandomEvents"
12
- configData={configData}
13
- render={(data) => (
14
- <div>
15
- <h2>Random Event: {data.Name}</h2>
16
- <div>
17
- <ImageComponent src={data.Icon} alt={data.Name} />
18
- <p>Color: {data.Color}</p>
19
- <p>Duration: {data.Duration} seconds</p>
20
- <p>Breaking Requirement: {data.BreakingRequirement}</p>
21
- <p>Playtime Requirement: {data.PlaytimeRequirement} minutes</p>
22
- <p>Chance: {data.Chance}</p>
23
- <p>Allow in Zones: {data.AllowInZones ? "Yes" : "No"}</p>
24
- <p>Allow in Instances: {data.AllowInInstances ? "Yes" : "No"}</p>
25
- <p>Allow Multiple: {data.AllowMultiple ? "Yes" : "No"}</p>
26
- {data.MinimumZone && <p>Minimum Zone: {data.MinimumZone}</p>}
27
- </div>
28
- <div>
29
- <h3>Area Whitelist:</h3>
30
- <ul>
31
- {Object.entries(data.AreaWhitelist).map(
32
- ([area, allowed], index) => (
33
- <li key={index}>
34
- {area.replace("_", " ")}: {allowed ? "Yes" : "No"}
35
- </li>
36
- ),
37
- )}
38
- </ul>
39
- </div>
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>}
30
+ </div>
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' }}>
35
+ {Object.entries(configData.AreaWhitelist).map(
36
+ ([area, allowed], index) => (
37
+ allowed && (
38
+ <span key={index} className="badge">
39
+ {area.replace(/_/g, " ")}
40
+ </span>
41
+ )
42
+ ),
43
+ )}
40
44
  </div>
41
- )}
42
- />
45
+ </div>
46
+ </div>
43
47
  );
44
48
  };
45
49
 
@@ -1,87 +1,203 @@
1
1
  import React from "react";
2
2
  import { CollectionConfigData } from "ps99-api";
3
- import { GenericFetchComponent } from "./GenericFetchComponent";
3
+
4
+ import { useItemResolution } from "../hooks/useItemResolution";
5
+ import ItemCard from "./ItemCard";
6
+ import { useExpandableList } from "../hooks/useExpandableList";
4
7
 
5
8
  const RanksComponent: React.FC<{
6
- configData?: CollectionConfigData<"Ranks">;
9
+ configData: CollectionConfigData<"Ranks">;
7
10
  }> = ({ configData }) => {
11
+ const { loading, resolveItem, getRarityColor } = useItemResolution();
12
+ const goalsLength = React.useMemo(() => Array.isArray(configData.Goals) ? configData.Goals.length : 0, [configData.Goals]);
13
+ const { expandedIndices, toggle, expandAll, collapseAll, isExpanded } = useExpandableList(goalsLength);
14
+
15
+ if (loading) {
16
+ return (
17
+ <div style={{
18
+ display: "flex",
19
+ justifyContent: "center",
20
+ alignItems: "center",
21
+ height: "100vh",
22
+ fontSize: "1.5rem",
23
+ color: "#888"
24
+ }}>
25
+ Loading reference data...
26
+ </div>
27
+ );
28
+ }
29
+
30
+ /* renderItemCard logic moved to ItemCard.tsx */
31
+
8
32
  return (
9
- <GenericFetchComponent<CollectionConfigData<"Ranks">>
10
- collectionName="Ranks"
11
- configData={configData}
12
- render={(data) => (
13
- <div>
14
- <h2>Rank: {data.Title}</h2>
15
- <p>Rank Number: {data.RankNumber}</p>
16
- <p>Max Enchants Equipped: {data.MaxEnchantsEquipped}</p>
17
- <p>Maximum Active Goals: {data.MaximumActiveGoals}</p>
18
- <p>Unlockable Egg Slots: {data.UnlockableEggSlots}</p>
19
- <p>Unlockable Pet Slots: {data.UnlockablePetSlots}</p>
20
- {data.RequiredRebirth && (
21
- <p>Required Rebirth: {data.RequiredRebirth}</p>
22
- )}
23
- {data.RequiredZone && <p>Required Zone: {data.RequiredZone}</p>}
33
+ <div style={{ width: "100%", height: "100%", boxSizing: "border-box" }}>
34
+ <div style={{ width: "100%" }}>
24
35
 
25
- <div>
26
- <h3>Goals:</h3>
27
- {data.Goals.map((goalSet, index) => (
28
- <div key={index}>
29
- <h4>Goal Set {index + 1}</h4>
30
- <ul>
31
- {goalSet.map((goal, goalIndex) => (
32
- <li key={goalIndex}>
33
- <p>Type: {goal.Type}</p>
34
- <p>Amount: {goal.Amount}</p>
35
- <p>Weight: {goal.Weight}</p>
36
- {goal.CurrencyID && <p>Currency ID: {goal.CurrencyID}</p>}
37
- {goal.BreakableType && (
38
- <p>Breakable Type: {goal.BreakableType}</p>
39
- )}
40
- {goal.PotionTier && <p>Potion Tier: {goal.PotionTier}</p>}
41
- {goal.EnchantTier && (
42
- <p>Enchant Tier: {goal.EnchantTier}</p>
43
- )}
44
- </li>
45
- ))}
46
- </ul>
47
- </div>
48
- ))}
36
+ {/* Info Grid */}
37
+ <div style={{
38
+ display: "grid",
39
+ gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))",
40
+ gap: "16px",
41
+ marginBottom: "60px",
42
+ background: "#ffffff",
43
+ padding: "24px",
44
+ borderRadius: "20px",
45
+ boxShadow: "0 4px 20px rgba(0,0,0,0.05)"
46
+ }}>
47
+ <InfoItem label="Max Enchants" value={configData.MaxEnchantsEquipped} />
48
+ <InfoItem label="Max Goals" value={configData.MaximumActiveGoals} />
49
+ <InfoItem label="Egg Slots" value={configData.UnlockableEggSlots} />
50
+ <InfoItem label="Pet Slots" value={configData.UnlockablePetSlots} />
51
+ {configData.RequiredRebirth && <InfoItem label="Rebirth Req" value={configData.RequiredRebirth} />}
52
+ {configData.RequiredZone && <InfoItem label="Zone Req" value={configData.RequiredZone} />}
53
+ </div>
54
+
55
+ {/* Rewards Section */}
56
+ {configData.Rewards && configData.Rewards.length > 0 && (
57
+ <div style={{ marginBottom: "60px" }}>
58
+ <SectionTitle title="Rewards" />
59
+ <div style={{
60
+ display: "grid",
61
+ gridTemplateColumns: "repeat(auto-fill, minmax(200px, 1fr))",
62
+ gap: "24px"
63
+ }}>
64
+ {configData.Rewards.map((reward: any, i: number) => {
65
+ // Handle inconsistent reward structure if necessary
66
+ const data = reward.Item?._data || reward.Item || reward._data || reward;
67
+ const id = data.id || data._id || "Reward";
68
+ const amount = data._am || data.Amount || 1;
69
+ const tn = data.tn || data.Tier;
70
+ const itemData = resolveItem(id, tn);
71
+ return (
72
+ <ItemCard
73
+ key={`${id}-${tn}-${i}`}
74
+ id={id}
75
+ amount={amount}
76
+ label={id}
77
+ tn={tn}
78
+ itemData={itemData}
79
+ rarityColor={itemData?.rarity ? getRarityColor(itemData.rarity) : null}
80
+ />
81
+ );
82
+ })}
83
+ </div>
49
84
  </div>
85
+ )}
50
86
 
87
+ {/* Goals Section */}
88
+ {configData.Goals && (
51
89
  <div>
52
- <h3>Rewards:</h3>
53
- <ul>
54
- {data.Rewards.map((reward, rewardIndex) => (
55
- <li key={rewardIndex}>
56
- <p>Stars Required: {reward.StarsRequired}</p>
57
- <p>Reward Item ID: {reward.Item._data.id}</p>
58
- {reward.Item._data._am && (
59
- <p>Amount: {reward.Item._data._am}</p>
60
- )}
61
- {reward.Item._data.tn && <p>TN: {reward.Item._data.tn}</p>}
62
- </li>
63
- ))}
64
- </ul>
65
- </div>
90
+ <SectionTitle title="Goals" />
66
91
 
67
- {data.RankUpRewards && (
68
- <div>
69
- <h3>Rank Up Rewards:</h3>
70
- <ul>
71
- {data.RankUpRewards.map((reward, rewardIndex) => (
72
- <li key={rewardIndex}>
73
- <p>Reward Item ID: {reward._data.id}</p>
74
- {reward._data._am && <p>Amount: {reward._data._am}</p>}
75
- {reward._data.tn && <p>TN: {reward._data.tn}</p>}
76
- </li>
77
- ))}
78
- </ul>
92
+ <div style={{ marginBottom: '20px', display: 'flex', gap: '10px', justifyContent: 'center' }}>
93
+ <button onClick={expandAll} style={{ padding: '8px 16px', borderRadius: '8px', border: '1px solid #ddd', background: '#fff', cursor: 'pointer' }}>Expand All</button>
94
+ <button onClick={collapseAll} style={{ padding: '8px 16px', borderRadius: '8px', border: '1px solid #ddd', background: '#fff', cursor: 'pointer' }}>Collapse All</button>
79
95
  </div>
80
- )}
81
- </div>
82
- )}
83
- />
96
+
97
+ <div style={{ display: "flex", flexDirection: "column", gap: "20px" }}>
98
+ {Array.isArray(configData.Goals) ? configData.Goals.map((goalSet: any[], setIndex: number) => (
99
+ <div key={setIndex} style={{
100
+ background: "#fff",
101
+ borderRadius: "24px",
102
+ padding: "30px",
103
+ boxShadow: "0 2px 10px rgba(0,0,0,0.03)"
104
+ }}>
105
+ <div
106
+ onClick={() => toggle(setIndex)}
107
+ style={{
108
+ display: "flex",
109
+ alignItems: "center",
110
+ gap: "12px",
111
+ cursor: "pointer",
112
+ marginBottom: isExpanded(setIndex) ? "24px" : "0"
113
+ }}
114
+ >
115
+ <div style={{
116
+ background: "#ffcc00",
117
+ width: "8px",
118
+ height: "32px",
119
+ borderRadius: "4px"
120
+ }} />
121
+ <h3 style={{
122
+ fontSize: "1.5rem",
123
+ color: "#444",
124
+ fontWeight: "700",
125
+ margin: 0
126
+ }}>
127
+ {isExpanded(setIndex) ? '▼' : '▶'} Goal Set {setIndex + 1}
128
+ </h3>
129
+ </div>
130
+
131
+ {isExpanded(setIndex) && (
132
+ <div style={{
133
+ display: "grid",
134
+ gridTemplateColumns: "repeat(auto-fill, minmax(180px, 1fr))",
135
+ gap: "20px"
136
+ }}>
137
+ {goalSet.map((goal: any, index: number) => {
138
+ // Determine ID and Label
139
+ // Goals usually have Type (numeric) which we need to resolve
140
+ // Sometimes they might have CurrencyID
141
+ const id = String(goal.CurrencyID || goal.Type);
142
+ const tn = goal.EnchantTier || goal.PotionTier || goal.Tier;
143
+ const itemData = resolveItem(id, tn);
144
+ // Label can be tricky, resolveItem will give us data, but for fallback prompt we use ID
145
+ return (
146
+ <ItemCard
147
+ key={`${id}-${tn}-${index}`}
148
+ id={id}
149
+ amount={goal.Amount}
150
+ label={id}
151
+ tn={tn}
152
+ weight={goal.Weight}
153
+ typeId={goal.Type}
154
+ itemData={itemData}
155
+ rarityColor={itemData?.rarity ? getRarityColor(itemData.rarity) : null}
156
+ />
157
+ );
158
+ })}
159
+ </div>
160
+ )}
161
+ </div>
162
+ )) : (
163
+ <div>No goals structure found</div>
164
+ )}
165
+ </div>
166
+ </div>
167
+ )}
168
+ </div>
169
+ </div>
84
170
  );
85
171
  };
86
172
 
173
+ // Helper Components
174
+ const InfoItem = ({ label, value }: { label: string, value: any }) => (
175
+ <div style={{ display: "flex", flexDirection: "column", alignItems: "center" }}>
176
+ <span style={{ fontSize: "12px", color: "#999", textTransform: "uppercase", fontWeight: "700" }}>{label}</span>
177
+ <span style={{ fontSize: "18px", color: "#333", fontWeight: "800" }}>{typeof value === 'number' ? value.toLocaleString() : value}</span>
178
+ </div>
179
+ );
180
+
181
+ const SectionTitle = ({ title }: { title: string }) => (
182
+ <h2 style={{
183
+ fontSize: "2.2rem",
184
+ color: "#444",
185
+ marginBottom: "30px",
186
+ textAlign: "center",
187
+ position: "relative",
188
+ display: "inline-block",
189
+ left: "50%",
190
+ transform: "translateX(-50%)"
191
+ }}>
192
+ {title}
193
+ <div style={{
194
+ width: "60px",
195
+ height: "4px",
196
+ background: "#ffcc00",
197
+ borderRadius: "2px",
198
+ margin: "10px auto 0"
199
+ }} />
200
+ </h2>
201
+ );
202
+
87
203
  export default RanksComponent;
@@ -1,24 +1,135 @@
1
1
  import React from "react";
2
2
  import { CollectionConfigData } from "ps99-api";
3
- import { GenericFetchComponent } from "./GenericFetchComponent";
4
3
 
5
4
  const RarityComponent: React.FC<{
6
- configData?: CollectionConfigData<"Rarity">;
5
+ configData: CollectionConfigData<"Rarity">;
7
6
  }> = ({ configData }) => {
7
+ // Use the rarity color for styling
8
+ const rarityColor = configData.Color || "#333";
9
+ const glowColor = rarityColor;
10
+
8
11
  return (
9
- <GenericFetchComponent<CollectionConfigData<"Rarity">>
10
- collectionName="Rarity"
11
- configData={configData}
12
- render={(data) => (
13
- <div>
14
- <h2>Rarity: {data.DisplayName}</h2>
15
- <p>Rarity Number: {data.RarityNumber}</p>
16
- <p>Color: {data.Color}</p>
17
- <p>Announce: {data.Announce ? "Yes" : "No"}</p>
12
+ <div style={{
13
+ padding: "40px",
14
+ fontFamily: "'Nunito', 'Segoe UI', sans-serif",
15
+ backgroundColor: "#f9f9f9",
16
+ minHeight: "100vh",
17
+ display: "flex",
18
+ flexDirection: "column",
19
+ alignItems: "center"
20
+ }}>
21
+
22
+ {/* Rarity Showcase Card */}
23
+ <div style={{
24
+ background: "#fff",
25
+ borderRadius: "24px",
26
+ padding: "40px",
27
+ boxShadow: `0 10px 40px ${glowColor}40`,
28
+ border: `3px solid ${rarityColor}`,
29
+ maxWidth: "600px",
30
+ width: "100%",
31
+ textAlign: "center",
32
+ marginBottom: "40px",
33
+ position: "relative",
34
+ overflow: "hidden",
35
+ transition: "transform 0.3s ease",
36
+ cursor: "default"
37
+ }}
38
+ onMouseEnter={(e) => {
39
+ e.currentTarget.style.transform = "scale(1.02)";
40
+ }}
41
+ onMouseLeave={(e) => {
42
+ e.currentTarget.style.transform = "scale(1)";
43
+ }}
44
+ >
45
+ {/* Background Glow */}
46
+ <div style={{
47
+ position: "absolute",
48
+ top: "-50%",
49
+ left: "50%",
50
+ transform: "translateX(-50%)",
51
+ width: "150%",
52
+ height: "300px",
53
+ background: `radial-gradient(circle, ${rarityColor}33 0%, transparent 70%)`,
54
+ pointerEvents: "none",
55
+ zIndex: 0
56
+ }} />
57
+
58
+ <div style={{ position: "relative", zIndex: 1 }}>
59
+ <h1 style={{
60
+ fontSize: "3.5rem",
61
+ fontWeight: "900",
62
+ color: rarityColor,
63
+ margin: "0 0 10px 0",
64
+ textShadow: `0 2px 10px ${glowColor}60`
65
+ }}>
66
+ {configData.DisplayName}
67
+ </h1>
68
+ <div style={{
69
+ fontSize: "1.2rem",
70
+ color: "#888",
71
+ fontWeight: "700",
72
+ textTransform: "uppercase",
73
+ letterSpacing: "2px"
74
+ }}>
75
+ Rarity #{configData.RarityNumber}
76
+ </div>
18
77
  </div>
19
- )}
20
- />
78
+ </div>
79
+
80
+ {/* Exhaustive Data Grid */}
81
+ <div style={{
82
+ display: "grid",
83
+ gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))",
84
+ gap: "20px",
85
+ maxWidth: "800px",
86
+ width: "100%"
87
+ }}>
88
+ {Object.entries(configData).map(([key, value]) => {
89
+ // Skip rendering complex objects if intended for simple display, or stringify them
90
+ if (typeof value === 'object' && value !== null) {
91
+ return (
92
+ <DataCard key={key} label={key} value={JSON.stringify(value)} color={rarityColor} />
93
+ );
94
+ }
95
+ return (
96
+ <DataCard key={key} label={key} value={String(value)} color={rarityColor} />
97
+ );
98
+ })}
99
+ </div>
100
+
101
+ </div>
21
102
  );
22
103
  };
23
104
 
105
+ const DataCard = ({ label, value, color }: { label: string, value: string, color: string }) => (
106
+ <div style={{
107
+ background: "#fff",
108
+ padding: "20px",
109
+ borderRadius: "16px",
110
+ boxShadow: "0 4px 15px rgba(0,0,0,0.05)",
111
+ borderLeft: `4px solid ${color}`,
112
+ display: "flex",
113
+ flexDirection: "column",
114
+ wordBreak: "break-word"
115
+ }}>
116
+ <span style={{
117
+ fontSize: "12px",
118
+ color: "#aaa",
119
+ fontWeight: "700",
120
+ textTransform: "uppercase",
121
+ marginBottom: "8px"
122
+ }}>
123
+ {label}
124
+ </span>
125
+ <span style={{
126
+ fontSize: "16px",
127
+ color: "#333",
128
+ fontWeight: "600"
129
+ }}>
130
+ {value}
131
+ </span>
132
+ </div>
133
+ );
134
+
24
135
  export default RarityComponent;
@@ -1,36 +1,47 @@
1
+
1
2
  import React from "react";
2
3
  import { CollectionConfigData, RebirthUnlock } from "ps99-api";
3
- import { GenericFetchComponent } from "./GenericFetchComponent";
4
- import ImageComponent from "./ImageComponent";
4
+ import ItemCard from "./ItemCard";
5
5
 
6
6
  const RebirthComponent: React.FC<{
7
- configData?: CollectionConfigData<"Rebirths">;
7
+ configData: CollectionConfigData<"Rebirths">;
8
8
  }> = ({ configData }) => {
9
9
  return (
10
- <GenericFetchComponent<CollectionConfigData<"Rebirths">>
11
- collectionName="Rebirths"
12
- configData={configData}
13
- render={(data) => (
14
- <div>
15
- <h2>Rebirth: {data.DisplayName}</h2>
16
- <p>Rebirth Number: {data.RebirthNumber}</p>
17
- <p>Zone Number Required: {data.ZoneNumberRequired}</p>
18
- <p>Strength Power Boost: {data.StrengthPowerBoost}%</p>
19
- <p>Boost Description: {data.BoostDesc}</p>
20
- {data.ResetZone && <p>Reset Zone: {data.ResetZone}</p>}
21
- <h3>Unlocks:</h3>
22
- <ul>
23
- {data.RebirthUnlocks.map((unlock: RebirthUnlock, index: number) => (
24
- <li key={index}>
25
- <ImageComponent src={unlock.Icon} alt={unlock.Title} />
26
- <strong>{unlock.GuiTitle || unlock.Title}</strong>:{" "}
27
- {unlock.Desc}
28
- </li>
29
- ))}
30
- </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
+ )}
31
42
  </div>
32
- )}
33
- />
43
+ </div>
44
+ </div>
34
45
  );
35
46
  };
36
47
 
@@ -1,22 +1,16 @@
1
1
  import React from "react";
2
2
  import { CollectionConfigData } from "ps99-api";
3
- import { GenericFetchComponent } from "./GenericFetchComponent";
4
3
 
5
4
  const SecretRoomComponent: React.FC<{
6
- configData?: CollectionConfigData<"SecretRooms">;
5
+ configData: CollectionConfigData<"SecretRooms">;
7
6
  }> = ({ configData }) => {
8
7
  return (
9
- <GenericFetchComponent<CollectionConfigData<"SecretRooms">>
10
- collectionName="SecretRooms"
11
- configData={configData}
12
- render={(data) => (
13
- <div>
14
- <h2>Secret Room: {data.DisplayName}</h2>
15
- <p>Instance ID: {data.InstanceId}</p>
16
- <p>Required Zone: {data.RequiredZone}</p>
17
- </div>
18
- )}
19
- />
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>
13
+ </div>
20
14
  );
21
15
  };
22
16