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,58 +1,74 @@
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 CurrencyComponent: React.FC<{
6
6
  configData: CollectionConfigData<"Currency">;
7
7
  }> = ({ configData }) => {
8
8
  return (
9
- <div>
10
- <h2>{configData.DisplayName}</h2>
11
- <p>Description: {configData.Desc}</p>
12
- <p>Max Amount: {configData.MaxAmount}</p>
13
- <p>Rarity: {configData.Rarity.DisplayName}</p>
14
- <p>Rarity Number: {configData.Rarity.RarityNumber}</p>
15
- {configData.Tradable && <p>Tradable: Yes</p>}
16
- {configData.IsWorldCurrency && <p>World Currency: Yes</p>}
17
- {configData.PermitAutoLootScaling && <p>Permit Auto Loot Scaling: Yes</p>}
18
- <h3>Tiers:</h3>
19
- <ul>
20
- {configData.Tiers.map((tier, index) => (
21
- <li key={index}>
22
- <ImageComponent src={tier.orbImage} alt={tier.tierName} />
23
- <p>Tier Name: {tier.tierName}</p>
24
- <p>Order: {tier.Order}</p>
25
- <p>Value: {tier.value}</p>
26
- <ImageComponent
27
- src={tier.imageOutline}
28
- alt={`${tier.tierName} outline`}
29
- />
30
- {tier.isBottom && <p>Is Bottom: Yes</p>}
31
- <ImageComponent
32
- src={tier.tinyImage}
33
- alt={`${tier.tierName} tiny`}
34
- />
35
- </li>
36
- ))}
37
- </ul>
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
+
38
47
  {configData.BagTiers && (
39
48
  <div>
40
- <h3>Bag Tiers:</h3>
41
- <ul>
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' }}>
42
51
  {configData.BagTiers.map((bagTier, index) => (
43
- <li key={index}>
44
- <ImageComponent
45
- src={bagTier.image}
46
- alt={`Bag tier ${index + 1}`}
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}
47
63
  />
48
- <p>Value: {bagTier.value}</p>
49
- </li>
64
+ </div>
50
65
  ))}
51
- </ul>
66
+ </div>
52
67
  </div>
53
68
  )}
54
69
  </div>
55
70
  );
56
71
  };
57
72
 
73
+
58
74
  export default CurrencyComponent;
@@ -1,5 +1,5 @@
1
- import React, { lazy, Suspense } from "react";
2
- import { useParams } from "react-router-dom";
1
+ import React, { lazy, Suspense, useState } from "react";
2
+ import { useParams, useNavigate } from "react-router-dom";
3
3
  import { CollectionName, CollectionConfigData } from "ps99-api";
4
4
  import { GenericFetchComponent } from "./GenericFetchComponent";
5
5
 
@@ -7,6 +7,7 @@ interface DynamicCollectionConfigDataProps {
7
7
  collectionName?: CollectionName;
8
8
  configName?: string;
9
9
  render?: (configData: CollectionConfigData<any>) => React.ReactNode; // Add render prop
10
+ additionalProps?: any; // Add additional props to pass to the rendered component
10
11
  }
11
12
 
12
13
  const DynamicCollectionConfigData: React.FC<
@@ -17,26 +18,173 @@ const DynamicCollectionConfigData: React.FC<
17
18
  configName: string;
18
19
  }>();
19
20
 
21
+ const navigate = useNavigate();
20
22
  const collectionName = props.collectionName || params.collectionName;
21
23
  const configName = props.configName || params.configName;
22
24
 
23
- if (!collectionName || !configName) {
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) {
24
39
  return <div>Invalid collection or config name</div>;
25
40
  }
26
41
 
27
- const Component = lazy(() => import(`./${collectionName}Component`));
28
-
29
42
  return (
30
- <Suspense fallback={<div>Loading...</div>}>
31
- <GenericFetchComponent
32
- collectionName={collectionName}
33
- configName={configName}
34
- render={
35
- props.render ||
36
- ((configData) => <Component configData={configData} />)
37
- } // Use render prop if provided
38
- />
39
- </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>
40
188
  );
41
189
  };
42
190
 
@@ -1,14 +1,31 @@
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 EggsComponent: React.FC<{
6
8
  configData: CollectionConfigData<"Eggs">;
7
9
  }> = ({ configData }) => {
10
+ const { getRarityColor, resolveItem } = useItemResolution();
11
+ const rarityColor = configData.rarity ? getRarityColor(configData.rarity) : null;
12
+
8
13
  return (
9
- <div>
10
- <h2>{configData.name}</h2>
11
- <ImageComponent src={configData.icon} alt={configData.name} />
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>
12
29
  <p>Currency: {configData.currency}</p>
13
30
  <p>Override Cost: {configData.overrideCost}</p>
14
31
  {configData.isCustomEgg && <p>Custom Egg: Yes</p>}
@@ -29,15 +46,36 @@ const EggsComponent: React.FC<{
29
46
  </div>
30
47
  )}
31
48
  <h3>Pets:</h3>
32
- <ul>
33
- {configData.pets.map((pet, index) => (
34
- <li key={index}>
35
- <p>Name: {pet[0]}</p>
36
- <p>Chance: {pet[1]}</p>
37
- {pet[2] && <p>Tier: {pet[2]}</p>}
38
- </li>
39
- ))}
40
- </ul>
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>
41
79
  {configData.productIds && (
42
80
  <div>
43
81
  <h3>Product IDs:</h3>
@@ -1,52 +1,93 @@
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 EnchantsComponent: React.FC<{
6
7
  configData: CollectionConfigData<"Enchants">;
7
8
  }> = ({ configData }) => {
9
+ const { getRarityColor } = useItemResolution();
10
+
8
11
  return (
9
- <div>
10
- <h2>Enchantment</h2>
11
- <p>Base Tier: {configData.BaseTier}</p>
12
- <p>Max Tier: {configData.MaxTier}</p>
13
- <p>Max Page: {configData.MaxPage}</p>
14
- {configData.DiminishPowerThreshold && (
15
- <p>Diminish Power Threshold: {configData.DiminishPowerThreshold}</p>
16
- )}
17
- {configData.EmpoweredBoost && (
18
- <p>Empowered Boost: {configData.EmpoweredBoost}</p>
19
- )}
20
- {configData.ProductId && <p>Product ID: {configData.ProductId}</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>
21
26
  <h3>Tiers:</h3>
22
27
  <div style={{ display: "flex", flexWrap: "wrap", gap: "1em" }}>
23
- {configData.Tiers.map((tier, index) => (
24
- <div
25
- key={index}
26
- style={{
27
- border: "1px solid #ccc",
28
- borderRadius: "8px",
29
- padding: "1em",
30
- width: "calc(33% - 1em)",
31
- boxSizing: "border-box",
32
- }}
33
- >
34
- <ImageComponent src={tier.Icon} alt={tier.DisplayName} />
35
- <h4>{tier.DisplayName}</h4>
36
- <p>
37
- <strong>Description:</strong> {tier.Desc}
38
- </p>
39
- <p>
40
- <strong>Power:</strong> {tier.Power}
41
- </p>
42
- <p>
43
- <strong>Rarity:</strong> {tier.Rarity.DisplayName}
44
- </p>
45
- <p>
46
- <strong>Rarity Number:</strong> {tier.Rarity.RarityNumber}
47
- </p>
48
- </div>
49
- ))}
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>
53
+ <p>Power: {tier.Power}</p>
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
+ })}
90
+ </div>
50
91
  </div>
51
92
  </div>
52
93
  );
@@ -1,35 +1,49 @@
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 FishingRodsComponent: React.FC<{
6
7
  configData: CollectionConfigData<"FishingRods">;
7
8
  }> = ({ configData }) => {
8
9
  return (
9
- <div>
10
+ <div className="game-card" style={{ maxWidth: '500px', margin: '0 auto' }}>
10
11
  <h2>{configData.DisplayName}</h2>
11
- <ImageComponent src={configData.Icon} alt={configData.DisplayName} />
12
- <p>Fishing Chance: {configData.FishingChance}</p>
13
- <p>Fishing Currency Multiplier: {configData.FishingCurrencyMultiplier}</p>
14
- <p>Minimum Fishing Time: {configData.MinFishingTime} seconds</p>
15
- <p>
16
- Fishing Game Speed Multiplier: {configData.FishingGameSpeedMultiplier}
17
- </p>
18
- <p>Bar Size: {configData.BarSize}</p>
19
- <p>Associated Item ID: {configData.AssociatedItemID}</p>
20
- {configData.MerchantSalePrice && (
21
- <p>Merchant Sale Price: {configData.MerchantSalePrice}</p>
22
- )}
23
- <h3>Fishing Odds:</h3>
24
- <ul>
25
- {configData.FishingOdds.map((odds, index) => (
26
- <li key={index}>
27
- {odds[0]}: {odds[1]}%
28
- </li>
29
- ))}
30
- </ul>
12
+
13
+ <div style={{ maxWidth: '300px', margin: '0 auto 15px auto' }}>
14
+ <ItemCard
15
+ id={configData.DisplayName}
16
+ amount={1}
17
+ label={configData.DisplayName}
18
+ itemData={{
19
+ icon: configData.Icon,
20
+ rarity: undefined,
21
+ name: configData.DisplayName
22
+ }}
23
+ rarityColor={null}
24
+ />
25
+ </div>
26
+
27
+ <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '10px', marginBottom: '20px', textAlign: 'left' }}>
28
+ <p><strong>Chance:</strong> {configData.FishingChance}%</p>
29
+ <p><strong>Currency:</strong> {configData.FishingCurrencyMultiplier}x</p>
30
+ <p><strong>Speed:</strong> {configData.FishingGameSpeedMultiplier}x</p>
31
+ <p><strong>Bar Size:</strong> {configData.BarSize}</p>
32
+ </div>
33
+
34
+ <div style={{ borderTop: '1px solid #eee', paddingTop: '15px' }}>
35
+ <h3 style={{ fontSize: '1.1em', marginBottom: '10px' }}>Fishing Odds</h3>
36
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px', justifyContent: 'center' }}>
37
+ {configData.FishingOdds.map((odds, index) => (
38
+ <span key={index} className="badge" style={{ background: '#e3f2fd', color: '#1565c0', border: '1px solid #90caf9' }}>
39
+ {odds[0]}: {odds[1]}%
40
+ </span>
41
+ ))}
42
+ </div>
43
+ </div>
31
44
  </div>
32
45
  );
33
46
  };
34
47
 
48
+
35
49
  export default FishingRodsComponent;
@@ -31,14 +31,7 @@ const Footer: React.FC = () => {
31
31
  }, []);
32
32
 
33
33
  return (
34
- <footer
35
- style={{
36
- textAlign: "center",
37
- padding: "1em",
38
- background: "#f8f9fa",
39
- borderTop: "1px solid #ccc",
40
- }}
41
- >
34
+ <footer className="game-footer">
42
35
  <div
43
36
  style={{
44
37
  display: "flex",