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,65 +1,74 @@
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 CurrencyComponent: React.FC<{
7
- configData?: CollectionConfigData<"Currency">;
6
+ configData: CollectionConfigData<"Currency">;
8
7
  }> = ({ configData }) => {
9
8
  return (
10
- <GenericFetchComponent<CollectionConfigData<"Currency">>
11
- collectionName="Currency"
12
- configData={configData}
13
- render={(data) => (
9
+
10
+ <div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
11
+
12
+ <div style={{ marginBottom: '20px', textAlign: 'center' }}>
13
+ <p style={{ fontStyle: 'italic', marginBottom: '10px' }}>{configData.Desc}</p>
14
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px', justifyContent: 'center' }}>
15
+ <span className="badge">Max: {configData.MaxAmount}</span>
16
+ {configData.Rarity && <span className="badge" style={{ borderColor: (configData.Rarity.Color as string) }}>Rarity: {configData.Rarity.DisplayName}</span>}
17
+ {configData.Tradable && <span className="badge">Tradable</span>}
18
+ {configData.IsWorldCurrency && <span className="badge">World Currency</span>}
19
+ {configData.PermitAutoLootScaling && <span className="badge">Auto Loot</span>}
20
+ </div>
21
+ </div>
22
+
23
+ <div style={{ marginBottom: '20px' }}>
24
+ <h3 style={{ fontSize: '1.2em', borderBottom: '1px solid #eee', paddingBottom: '5px' }}>Tiers</h3>
25
+ <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(150px, 1fr))", gap: "10px", marginTop: '15px' }}>
26
+ {configData.Tiers.map((tier, index) => (
27
+ <div key={index}>
28
+ <ItemCard
29
+ id={tier.tierName}
30
+ amount={tier.value}
31
+ label={tier.tierName}
32
+ itemData={{
33
+ icon: tier.orbImage,
34
+ rarity: configData.Rarity,
35
+ name: tier.tierName
36
+ }}
37
+ rarityColor={(configData.Rarity?.Color as string) || null}
38
+ />
39
+ <div style={{ textAlign: 'center', fontSize: '0.8em', color: '#666', marginTop: '4px' }}>
40
+ Order: {tier.Order}
41
+ </div>
42
+ </div>
43
+ ))}
44
+ </div>
45
+ </div>
46
+
47
+ {configData.BagTiers && (
14
48
  <div>
15
- <h2>{data.DisplayName}</h2>
16
- <p>Description: {data.Desc}</p>
17
- <p>Max Amount: {data.MaxAmount}</p>
18
- <p>Rarity: {data.Rarity.DisplayName}</p>
19
- <p>Rarity Number: {data.Rarity.RarityNumber}</p>
20
- {data.Tradable && <p>Tradable: Yes</p>}
21
- {data.IsWorldCurrency && <p>World Currency: Yes</p>}
22
- {data.PermitAutoLootScaling && <p>Permit Auto Loot Scaling: Yes</p>}
23
- <h3>Tiers:</h3>
24
- <ul>
25
- {data.Tiers.map((tier, index) => (
26
- <li key={index}>
27
- <ImageComponent src={tier.orbImage} alt={tier.tierName} />
28
- <p>Tier Name: {tier.tierName}</p>
29
- <p>Order: {tier.Order}</p>
30
- <p>Value: {tier.value}</p>
31
- <ImageComponent
32
- src={tier.imageOutline}
33
- alt={`${tier.tierName} outline`}
34
- />
35
- {tier.isBottom && <p>Is Bottom: Yes</p>}
36
- <ImageComponent
37
- src={tier.tinyImage}
38
- alt={`${tier.tierName} tiny`}
49
+ <h3 style={{ fontSize: '1.2em', borderBottom: '1px solid #eee', paddingBottom: '5px' }}>Bag Tiers</h3>
50
+ <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(150px, 1fr))", gap: "10px", marginTop: '15px' }}>
51
+ {configData.BagTiers.map((bagTier, index) => (
52
+ <div key={index}>
53
+ <ItemCard
54
+ id={`bag-tier-${index}`}
55
+ amount={bagTier.value}
56
+ label={`Bag Tier ${index + 1}`}
57
+ itemData={{
58
+ icon: bagTier.image,
59
+ rarity: configData.Rarity,
60
+ name: `Bag Tier ${index + 1}`
61
+ }}
62
+ rarityColor={(configData.Rarity?.Color as string) || null}
39
63
  />
40
- </li>
64
+ </div>
41
65
  ))}
42
- </ul>
43
- {data.BagTiers && (
44
- <div>
45
- <h3>Bag Tiers:</h3>
46
- <ul>
47
- {data.BagTiers.map((bagTier, index) => (
48
- <li key={index}>
49
- <ImageComponent
50
- src={bagTier.image}
51
- alt={`Bag tier ${index + 1}`}
52
- />
53
- <p>Value: {bagTier.value}</p>
54
- </li>
55
- ))}
56
- </ul>
57
- </div>
58
- )}
66
+ </div>
59
67
  </div>
60
68
  )}
61
- />
69
+ </div>
62
70
  );
63
71
  };
64
72
 
73
+
65
74
  export default CurrencyComponent;
@@ -1,23 +1,190 @@
1
- import React, { lazy, Suspense } from "react";
2
- import { useParams } from "react-router-dom";
3
- import { CollectionName } from "ps99-api";
1
+ import React, { lazy, Suspense, useState } from "react";
2
+ import { useParams, useNavigate } from "react-router-dom";
3
+ import { CollectionName, CollectionConfigData } from "ps99-api";
4
+ import { GenericFetchComponent } from "./GenericFetchComponent";
4
5
 
5
- const DynamicCollectionConfigData: React.FC = () => {
6
- const { collectionName, configName } = useParams<{
6
+ interface DynamicCollectionConfigDataProps {
7
+ collectionName?: CollectionName;
8
+ configName?: string;
9
+ render?: (configData: CollectionConfigData<any>) => React.ReactNode; // Add render prop
10
+ additionalProps?: any; // Add additional props to pass to the rendered component
11
+ }
12
+
13
+ const DynamicCollectionConfigData: React.FC<
14
+ DynamicCollectionConfigDataProps
15
+ > = (props) => {
16
+ const params = useParams<{
7
17
  collectionName: CollectionName;
8
18
  configName: string;
9
19
  }>();
10
20
 
11
- if (!collectionName || !configName) {
21
+ const navigate = useNavigate();
22
+ const collectionName = props.collectionName || params.collectionName;
23
+ const configName = props.configName || params.configName;
24
+
25
+ const [viewMode, setViewMode] = useState<'render' | 'schema'>('render');
26
+
27
+ /*
28
+ * We need to use useMemo here to prevent the component from being re-created
29
+ * on every render. This usage of useMemo is safe because the import path
30
+ * depends only on collectionName, and we want to re-create the component
31
+ * only when collectionName changes.
32
+ */
33
+ const Component = React.useMemo(() => {
34
+ if (!collectionName) return null;
35
+ return lazy(() => import(`./${collectionName}Component`));
36
+ }, [collectionName]);
37
+
38
+ if (!collectionName || !configName || !Component) {
12
39
  return <div>Invalid collection or config name</div>;
13
40
  }
14
41
 
15
- const Component = lazy(() => import(`./${collectionName}Component`));
16
-
17
42
  return (
18
- <Suspense fallback={<div>Loading...</div>}>
19
- <Component configName={configName} />
20
- </Suspense>
43
+ <div style={{ display: "flex", flexDirection: "column", height: "100%", width: "100%" }}>
44
+ {/* Header with Close Button */}
45
+ <div style={{
46
+ padding: "15px 20px",
47
+ borderBottom: "4px solid #333",
48
+ display: "flex",
49
+ alignItems: "center",
50
+ justifyContent: "space-between",
51
+ backgroundColor: "#fff",
52
+ flexShrink: 0,
53
+ }}>
54
+ <div
55
+ style={{ display: "flex", alignItems: "center", gap: "10px", cursor: "pointer" }}
56
+ onClick={() => {
57
+ navigator.clipboard.writeText(window.location.href);
58
+ // visual feedback could be added here, e.g. a small alert or changing the icon color briefly
59
+ const icon = document.getElementById('link-icon-path');
60
+ if (icon) icon.style.fill = '#4caf50';
61
+ setTimeout(() => { if (icon) icon.style.fill = '#ccc'; }, 1000);
62
+ }}
63
+ title="Click to copy link to this item"
64
+ onMouseEnter={(e) => {
65
+ const icon = e.currentTarget.querySelector('.link-icon');
66
+ if (icon) (icon as HTMLElement).style.opacity = '1';
67
+ }}
68
+ onMouseLeave={(e) => {
69
+ const icon = e.currentTarget.querySelector('.link-icon');
70
+ if (icon) (icon as HTMLElement).style.opacity = '0';
71
+ }}
72
+ >
73
+ <h2 style={{
74
+ margin: 0,
75
+ fontSize: "2rem",
76
+ fontWeight: "900",
77
+ color: "#333",
78
+ textShadow: "2px 2px 0px #eee",
79
+ fontFamily: "'Fredoka One', cursive, sans-serif",
80
+ textTransform: "capitalize",
81
+ }}>
82
+ {configName}
83
+ </h2>
84
+ <svg
85
+ className="link-icon"
86
+ xmlns="http://www.w3.org/2000/svg"
87
+ width="24"
88
+ height="24"
89
+ viewBox="0 0 24 24"
90
+ fill="none"
91
+ stroke="#ccc"
92
+ strokeWidth="2"
93
+ strokeLinecap="round"
94
+ strokeLinejoin="round"
95
+ style={{ opacity: 0, transition: 'opacity 0.2s' }}
96
+ >
97
+ <path id="link-icon-path" d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path>
98
+ <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path>
99
+ </svg>
100
+ </div>
101
+
102
+ <div style={{ display: 'flex', gap: '10px' }}>
103
+ <button
104
+ onClick={() => setViewMode(prev => prev === 'render' ? 'schema' : 'render')}
105
+ style={{
106
+ width: "48px",
107
+ height: "48px",
108
+ borderRadius: "12px",
109
+ backgroundColor: viewMode === 'render' ? "#2196f3" : "#4caf50",
110
+ color: "white",
111
+ border: `4px solid ${viewMode === 'render' ? "#1565c0" : "#2e7d32"}`,
112
+ fontSize: "20px",
113
+ fontWeight: "900",
114
+ display: "flex",
115
+ alignItems: "center",
116
+ justifyContent: "center",
117
+ cursor: "pointer",
118
+ boxShadow: `inset 0 4px 4px rgba(255,255,255,0.4), 0 4px 0 ${viewMode === 'render' ? "#0d47a1" : "#1b5e20"}`,
119
+ fontFamily: "monospace"
120
+ }}
121
+ title={viewMode === 'render' ? "View Raw Schema" : "View Rendered Item"}
122
+ >
123
+ {viewMode === 'render' ? "{}" : "👁️"}
124
+ </button>
125
+
126
+ <button
127
+ onClick={() => navigate(`/collections/${collectionName}`)}
128
+ style={{
129
+ width: "48px",
130
+ height: "48px",
131
+ borderRadius: "12px",
132
+ backgroundColor: "#ff0055",
133
+ color: "white",
134
+ border: "4px solid #900",
135
+ fontSize: "24px",
136
+ fontWeight: "900",
137
+ display: "flex",
138
+ alignItems: "center",
139
+ justifyContent: "center",
140
+ cursor: "pointer",
141
+ boxShadow: "inset 0 4px 4px rgba(255,255,255,0.4), 0 4px 0 #500",
142
+ }}
143
+ >
144
+ X
145
+ </button>
146
+ </div>
147
+ </div>
148
+
149
+ <div style={{ flex: 1, overflowY: "auto", padding: "20px" }}>
150
+ <Suspense fallback={<div>Loading...</div>}>
151
+ <GenericFetchComponent
152
+ // We use the key prop to force a re-mount of the component when the
153
+ // collectionName or configName changes. This ensures that the internal
154
+ // state of the GenericFetchComponent is reset and the new data is
155
+ // fetched correctly, preventing stale data from being passed to the
156
+ // child component.
157
+ key={`${collectionName}-${configName}`}
158
+ collectionName={collectionName}
159
+ configName={configName}
160
+ render={
161
+ (configData: CollectionConfigData<any>) => {
162
+ if (viewMode === 'schema') {
163
+ return (
164
+ <div style={{
165
+ padding: '20px',
166
+ background: '#1e1e1e',
167
+ color: '#d4d4d4',
168
+ borderRadius: '12px',
169
+ overflow: 'auto',
170
+ height: '100%',
171
+ fontFamily: 'Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace',
172
+ fontSize: '0.9rem',
173
+ textAlign: 'left',
174
+ boxShadow: 'inset 0 0 20px rgba(0,0,0,0.5)',
175
+ border: '2px solid #333'
176
+ }}>
177
+ <pre style={{ margin: 0 }}>{JSON.stringify(configData, null, 2)}</pre>
178
+ </div>
179
+ );
180
+ }
181
+ return props.render ? props.render(configData) : <Component configData={configData} {...props.additionalProps} />;
182
+ }
183
+ } // Use render prop if provided
184
+ />
185
+ </Suspense>
186
+ </div>
187
+ </div>
21
188
  );
22
189
  };
23
190
 
@@ -1,61 +1,94 @@
1
1
  import React from "react";
2
2
  import { CollectionConfigData } from "ps99-api";
3
- import { GenericFetchComponent } from "./GenericFetchComponent";
4
3
  import ImageComponent from "./ImageComponent";
4
+ import ItemCard from "./ItemCard";
5
+ import { useItemResolution } from "../hooks/useItemResolution";
5
6
 
6
7
  const EggsComponent: React.FC<{
7
- configData?: CollectionConfigData<"Eggs">;
8
+ configData: CollectionConfigData<"Eggs">;
8
9
  }> = ({ configData }) => {
10
+ const { getRarityColor, resolveItem } = useItemResolution();
11
+ const rarityColor = configData.rarity ? getRarityColor(configData.rarity) : null;
12
+
9
13
  return (
10
- <GenericFetchComponent<CollectionConfigData<"Eggs">>
11
- collectionName="Eggs"
12
- configData={configData}
13
- render={(data) => (
14
+ <div style={{ width: "100%", height: "100%", boxSizing: "border-box" }}>
15
+ <div style={{ width: '300px', margin: '0 auto 20px 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
+ typeId={(configData as any)._index}
27
+ />
28
+ </div>
29
+ <p>Currency: {configData.currency}</p>
30
+ <p>Override Cost: {configData.overrideCost}</p>
31
+ {configData.isCustomEgg && <p>Custom Egg: Yes</p>}
32
+ {configData.bestEgg && <p>Best Egg: Yes</p>}
33
+ {configData.disableGold && <p>Disable Gold: Yes</p>}
34
+ {configData.disableRainbow && <p>Disable Rainbow: Yes</p>}
35
+ {configData.disableShiny && <p>Disable Shiny: Yes</p>}
36
+ {configData.disableModifiers && <p>Disable Modifiers: Yes</p>}
37
+ {configData.goldChance && <p>Gold Chance: {configData.goldChance}</p>}
38
+ {configData.rainbowChance && (
39
+ <p>Rainbow Chance: {configData.rainbowChance}</p>
40
+ )}
41
+ {configData.shinyChance && <p>Shiny Chance: {configData.shinyChance}</p>}
42
+ {configData.rarity && (
43
+ <div>
44
+ <p>Rarity: {configData.rarity.DisplayName}</p>
45
+ <p>Rarity Number: {configData.rarity.RarityNumber}</p>
46
+ </div>
47
+ )}
48
+ <h3>Pets:</h3>
49
+ <div style={{ marginTop: '15px' }}>
50
+ <h3 style={{ borderBottom: '1px solid #eee', paddingBottom: '5px' }}>Pets</h3>
51
+ <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(120px, 1fr))', gap: '16px' }}>
52
+ {configData.pets.map((pet, index) => {
53
+ const petName = pet[0];
54
+ const chance = pet[1];
55
+ // The 3rd element appears to be the tier (tn), based on existing code usage
56
+ const tn = pet[2];
57
+ const resolvedItem = resolveItem(petName, tn);
58
+ const rarityColor = resolvedItem?.rarity ? getRarityColor(resolvedItem.rarity) : null;
59
+
60
+ return (
61
+ <div key={index} style={{
62
+ // Auto-sizing handled by grid
63
+ }}>
64
+ <ItemCard
65
+ id={petName}
66
+ // Formatting chance as string to include %
67
+ amount={`${chance}%`}
68
+ label={resolvedItem?.name || petName}
69
+ itemData={resolvedItem}
70
+ rarityColor={rarityColor}
71
+ tn={tn}
72
+ // weight or typeId not strictly needed here unless we want debug info
73
+ />
74
+ </div>
75
+ );
76
+ })}
77
+ </div>
78
+ </div>
79
+ {configData.productIds && (
14
80
  <div>
15
- <h2>{data.name}</h2>
16
- <ImageComponent src={data.icon} alt={data.name} />
17
- <p>Currency: {data.currency}</p>
18
- <p>Override Cost: {data.overrideCost}</p>
19
- {data.isCustomEgg && <p>Custom Egg: Yes</p>}
20
- {data.bestEgg && <p>Best Egg: Yes</p>}
21
- {data.disableGold && <p>Disable Gold: Yes</p>}
22
- {data.disableRainbow && <p>Disable Rainbow: Yes</p>}
23
- {data.disableShiny && <p>Disable Shiny: Yes</p>}
24
- {data.disableModifiers && <p>Disable Modifiers: Yes</p>}
25
- {data.goldChance && <p>Gold Chance: {data.goldChance}</p>}
26
- {data.rainbowChance && <p>Rainbow Chance: {data.rainbowChance}</p>}
27
- {data.shinyChance && <p>Shiny Chance: {data.shinyChance}</p>}
28
- {data.rarity && (
29
- <div>
30
- <p>Rarity: {data.rarity.DisplayName}</p>
31
- <p>Rarity Number: {data.rarity.RarityNumber}</p>
32
- </div>
33
- )}
34
- <h3>Pets:</h3>
81
+ <h3>Product IDs:</h3>
35
82
  <ul>
36
- {data.pets.map((pet, index) => (
37
- <li key={index}>
38
- <p>Name: {pet[0]}</p>
39
- <p>Chance: {pet[1]}</p>
40
- {pet[2] && <p>Tier: {pet[2]}</p>}
83
+ {Object.entries(configData.productIds).map(([key, value]) => (
84
+ <li key={key}>
85
+ {key}: {value}
41
86
  </li>
42
87
  ))}
43
88
  </ul>
44
- {data.productIds && (
45
- <div>
46
- <h3>Product IDs:</h3>
47
- <ul>
48
- {Object.entries(data.productIds).map(([key, value]) => (
49
- <li key={key}>
50
- {key}: {value}
51
- </li>
52
- ))}
53
- </ul>
54
- </div>
55
- )}
56
89
  </div>
57
90
  )}
58
- />
91
+ </div>
59
92
  );
60
93
  };
61
94
 
@@ -1,45 +1,95 @@
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";
4
+ import { useItemResolution } from "../hooks/useItemResolution";
5
5
 
6
6
  const EnchantsComponent: React.FC<{
7
- configData?: CollectionConfigData<"Enchants">;
7
+ configData: CollectionConfigData<"Enchants">;
8
8
  }> = ({ configData }) => {
9
+ const { getRarityColor } = useItemResolution();
10
+
9
11
  return (
10
- <GenericFetchComponent<CollectionConfigData<"Enchants">>
11
- collectionName="Enchants"
12
- configData={configData}
13
- render={(data) => (
14
- <div>
15
- <h2>Enchantment</h2>
16
- {data.PageIcon && (
17
- <ImageComponent src={data.PageIcon} alt="Page Icon" />
18
- )}
19
- <p>Base Tier: {data.BaseTier}</p>
20
- <p>Max Tier: {data.MaxTier}</p>
21
- <p>Max Page: {data.MaxPage}</p>
22
- {data.DiminishPowerThreshold && (
23
- <p>Diminish Power Threshold: {data.DiminishPowerThreshold}</p>
24
- )}
25
- {data.EmpoweredBoost && <p>Empowered Boost: {data.EmpoweredBoost}</p>}
26
- {data.ProductId && <p>Product ID: {data.ProductId}</p>}
27
- <h3>Tiers:</h3>
28
- <ul>
29
- {data.Tiers.map((tier, index) => (
30
- <li key={index}>
31
- <ImageComponent src={tier.Icon} alt={tier.DisplayName} />
32
- <p>Display Name: {tier.DisplayName}</p>
33
- <p>Description: {tier.Desc}</p>
12
+
13
+ <div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
14
+ <div style={{ textAlign: 'left', marginBottom: '20px', display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: '10px' }}>
15
+ <p><strong>Base Tier:</strong> {configData.BaseTier}</p>
16
+ <p><strong>Max Tier:</strong> {configData.MaxTier}</p>
17
+ <p><strong>Max Page:</strong> {configData.MaxPage}</p>
18
+ {configData.DiminishPowerThreshold && (
19
+ <p><strong>Diminish Threshold:</strong> {configData.DiminishPowerThreshold}</p>
20
+ )}
21
+ {configData.EmpoweredBoost && (
22
+ <p><strong>Empowered Boost:</strong> {configData.EmpoweredBoost}</p>
23
+ )}
24
+ {configData.ProductId && <p><strong>Product ID:</strong> {configData.ProductId}</p>}
25
+ </div>
26
+ <h3>Tiers:</h3>
27
+ <div style={{ display: "flex", flexWrap: "wrap", gap: "1em" }}>
28
+ {configData.Tiers.map((tier, index) => {
29
+ const rarityColor = tier.Rarity ? getRarityColor(tier.Rarity) : null;
30
+ return (
31
+ <div
32
+ key={index}
33
+ style={{
34
+ flex: "1 1 300px",
35
+ maxWidth: "350px",
36
+ margin: "0 auto"
37
+ }}
38
+ >
39
+ <ItemCard
40
+ id={tier.DisplayName}
41
+ amount={1}
42
+ label={tier.DisplayName}
43
+ tn={index + 1}
44
+ itemData={{
45
+ icon: tier.Icon,
46
+ rarity: tier.Rarity,
47
+ name: tier.DisplayName
48
+ }}
49
+ rarityColor={rarityColor}
50
+ />
51
+ <div style={{ marginTop: '10px', fontSize: '0.9em', color: '#666', textAlign: 'center' }}>
52
+ <p>{tier.Desc}</p>
34
53
  <p>Power: {tier.Power}</p>
35
- <p>Rarity: {tier.Rarity.DisplayName}</p>
36
- <p>Rarity Number: {tier.Rarity.RarityNumber}</p>
37
- </li>
38
- ))}
39
- </ul>
54
+ </div>
55
+ </div>
56
+ );
57
+ })}
58
+ <h3>Tiers</h3>
59
+ <div style={{ display: "grid", gridTemplateColumns: 'repeat(auto-fill, minmax(250px, 1fr))', gap: "15px" }}>
60
+ {configData.Tiers.map((tier, index) => {
61
+ const rarityColor = tier.Rarity ? getRarityColor(tier.Rarity) : null;
62
+ return (
63
+ <div
64
+ key={index}
65
+ style={{
66
+ display: 'flex',
67
+ flexDirection: 'column',
68
+ height: '100%'
69
+ }}
70
+ >
71
+ <ItemCard
72
+ id={tier.DisplayName}
73
+ amount={1}
74
+ label={tier.DisplayName}
75
+ tn={index + 1}
76
+ itemData={{
77
+ icon: tier.Icon,
78
+ rarity: tier.Rarity,
79
+ name: tier.DisplayName
80
+ }}
81
+ rarityColor={rarityColor}
82
+ />
83
+ <div style={{ marginTop: '10px', fontSize: '0.9em', color: '#666', textAlign: 'center' }}>
84
+ <p>{tier.Desc}</p>
85
+ <p style={{ fontWeight: 'bold', color: '#333' }}>Power: {tier.Power}</p>
86
+ </div>
87
+ </div>
88
+ );
89
+ })}
40
90
  </div>
41
- )}
42
- />
91
+ </div>
92
+ </div>
43
93
  );
44
94
  };
45
95