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
@@ -17,15 +17,20 @@
17
17
  "ps99-api": "file:../..",
18
18
  "react": "^18.3.1",
19
19
  "react-dom": "^18.3.1",
20
- "react-router-dom": "^6.24.1"
20
+ "react-router-dom": "^6.24.1",
21
+ "react-virtualized-auto-sizer": "^2.0.3",
22
+ "react-window": "^2.2.7"
21
23
  },
22
24
  "devDependencies": {
23
25
  "@types/react": "^18.3.3",
24
26
  "@types/react-dom": "^18.3.0",
27
+ "@types/react-virtualized-auto-sizer": "^1.0.4",
28
+ "@types/react-window": "^1.8.8",
25
29
  "clean-webpack-plugin": "^4.0.0",
26
30
  "copy-webpack-plugin": "^12.0.2",
27
31
  "css-loader": "^7.1.2",
28
32
  "html-webpack-plugin": "^5.6.0",
33
+ "style-loader": "^4.0.0",
29
34
  "ts-loader": "^9.5.1",
30
35
  "typescript": "^5.5.3",
31
36
  "webpack": "^5.92.1",
@@ -1,33 +1,36 @@
1
1
  <!doctype html>
2
2
  <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Pet Simulator 99 API</title>
7
- <link rel="manifest" href="/node-ps99-api/manifest.json" />
8
- <link rel="icon" href="/node-ps99-api/icons/icon-192x192.png" />
9
- </head>
10
- <body>
11
- <div id="root"></div>
12
- <script src="/node-ps99-api/bundle.js"></script>
13
- <script>
14
- if ("serviceWorker" in navigator) {
15
- window.addEventListener("load", function () {
16
- navigator.serviceWorker
17
- .register("/node-ps99-api/service-worker.js")
18
- .then(
19
- function (registration) {
20
- console.log(
21
- "ServiceWorker registration successful with scope: ",
22
- registration.scope,
23
- );
24
- },
25
- function (error) {
26
- console.log("ServiceWorker registration failed: ", error);
27
- },
28
- );
29
- });
30
- }
31
- </script>
32
- </body>
33
- </html>
3
+
4
+ <head>
5
+ <meta charset="UTF-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Pet Simulator 99 API</title>
8
+ <link rel="manifest" href="/node-ps99-api/manifest.json" />
9
+ <link rel="icon" href="/node-ps99-api/icons/icon-192x192.png" />
10
+ </head>
11
+
12
+ <body>
13
+ <div id="root"></div>
14
+
15
+ <script>
16
+ // if ("serviceWorker" in navigator) {
17
+ // window.addEventListener("load", function () {
18
+ // navigator.serviceWorker
19
+ // .register("/node-ps99-api/service-worker.js")
20
+ // .then(
21
+ // function (registration) {
22
+ // console.log(
23
+ // "ServiceWorker registration successful with scope: ",
24
+ // registration.scope,
25
+ // );
26
+ // },
27
+ // function (error) {
28
+ // console.log("ServiceWorker registration failed: ", error);
29
+ // },
30
+ // );
31
+ // });
32
+ // }
33
+ </script>
34
+ </body>
35
+
36
+ </html>
@@ -1,10 +1,11 @@
1
1
  import React from "react";
2
- import { HashRouter as Router, Routes, Route } from "react-router-dom";
2
+ import { HashRouter as Router, Routes, Route, Navigate } from "react-router-dom";
3
3
  import HomePage from "./components/HomePage";
4
4
  import Header from "./components/Header";
5
5
  import CollectionsIndex from "./components/CollectionsIndex";
6
6
  import CollectionConfigIndex from "./components/CollectionConfigIndex";
7
7
  import DynamicCollectionConfigData from "./components/DynamicCollectionConfigData";
8
+ import CollectionsLayout from "./components/CollectionsLayout";
8
9
  import Footer from "./components/Footer";
9
10
 
10
11
  const App: React.FC = () => {
@@ -14,14 +15,10 @@ const App: React.FC = () => {
14
15
  <Routes>
15
16
  <Route path="/" element={<HomePage />} />
16
17
  <Route path="/collections" element={<CollectionsIndex />} />
17
- <Route
18
- path="/collections/:collectionName"
19
- element={<CollectionConfigIndex />}
20
- />
21
- <Route
22
- path="/collections/:collectionName/:configName"
23
- element={<DynamicCollectionConfigData />}
24
- />
18
+ <Route element={<CollectionsLayout />}>
19
+ <Route path="/collections/:collectionName" element={<CollectionConfigIndex />} />
20
+ <Route path="/collections/:collectionName/:configName" element={<DynamicCollectionConfigData />} />
21
+ </Route>
25
22
  </Routes>
26
23
  <Footer />
27
24
  </Router>
@@ -1,40 +1,88 @@
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";
6
+ import { useExpandableList } from "../hooks/useExpandableList";
5
7
 
6
8
  const AchievementsComponent: React.FC<{
7
- configData?: CollectionConfigData<"Achievements">;
9
+ configData: CollectionConfigData<"Achievements">;
8
10
  }> = ({ configData }) => {
11
+ const { resolveItem, getRarityColor, loading } = useItemResolution();
12
+ const { expandedIndices, toggle, expandAll, collapseAll, isExpanded } = useExpandableList(configData.Tiers.length);
13
+
14
+ if (loading) return <div>Loading data...</div>;
15
+
9
16
  return (
10
- <GenericFetchComponent<CollectionConfigData<"Achievements">>
11
- collectionName="Achievements"
12
- configData={configData}
13
- render={(data) => (
14
- <div>
15
- <h2>{data.Name}</h2>
16
- <ImageComponent src={data.Icon} alt={data.Name} />
17
- <ul>
18
- {data.Tiers.map((tier, index) => (
19
- <li key={index}>
20
- <h3>{tier.Title}</h3>
21
- <p>{tier.Desc.replace("{amount}", tier.Amount.toString())}</p>
22
- <p>Difficulty: {tier.Difficulty.Name}</p>
23
- <p>Amount: {tier.Amount}</p>
24
- <ul>
25
- {tier.Rewards.map((reward, rewardIndex) => (
26
- <li key={rewardIndex}>
27
- Reward ID: {reward.Reward._data.id} Amount:{" "}
28
- {reward.Reward._data._am}
29
- </li>
30
- ))}
31
- </ul>
32
- </li>
33
- ))}
34
- </ul>
35
- </div>
36
- )}
37
- />
17
+ <div style={{ width: "100%", height: "100%", boxSizing: "border-box" }}>
18
+
19
+ <div style={{ marginBottom: '20px', display: 'flex', gap: '10px', justifyContent: 'center' }}>
20
+ <button onClick={expandAll} style={{ padding: '8px 16px', borderRadius: '8px', border: '1px solid #ddd', background: '#fff', cursor: 'pointer' }}>Expand All</button>
21
+ <button onClick={collapseAll} style={{ padding: '8px 16px', borderRadius: '8px', border: '1px solid #ddd', background: '#fff', cursor: 'pointer' }}>Collapse All</button>
22
+ </div>
23
+ <div style={{ maxWidth: "150px", margin: "0 auto 20px" }}>
24
+ <ImageComponent src={configData.Icon} alt={configData.Name} />
25
+ </div>
26
+ <div style={{ display: "flex", flexDirection: "column", gap: "20px" }}>
27
+ {configData.Tiers.map((tier, index) => (
28
+ <div
29
+ key={index}
30
+ style={{
31
+ border: "1px solid #eee",
32
+ padding: "15px",
33
+ borderRadius: "12px",
34
+ textAlign: 'left'
35
+ }}
36
+ >
37
+ <div
38
+ onClick={() => toggle(index)}
39
+ style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', cursor: 'pointer', paddingBottom: isExpanded(index) ? '10px' : '0' }}
40
+ >
41
+ <h3 style={{ margin: 0, fontSize: '1.2em', display: 'flex', alignItems: 'center', gap: '8px' }}>
42
+ {isExpanded(index) ? '▼' : '▶'} {tier.Title}
43
+ </h3>
44
+ <div style={{ display: "flex", gap: "10px" }}>
45
+ <span className="badge">Difficulty: {tier.Difficulty.Name}</span>
46
+ <span className="badge">Amount: {tier.Amount.toLocaleString()} {(tier.Desc.match(/{amount}\s*([a-zA-Z]+)/)?.[1] || '')}</span>
47
+ </div>
48
+ </div>
49
+
50
+ {isExpanded(index) && (
51
+ <div style={{ borderTop: '1px solid #eee', paddingTop: '15px' }}>
52
+
53
+ <p style={{ marginBottom: '15px', color: '#555' }}>{tier.Desc.replace("{amount}", tier.Amount.toLocaleString())}</p>
54
+
55
+ <h4 style={{ fontSize: '1em', marginBottom: '10px' }}>Rewards</h4>
56
+ <div
57
+ style={{
58
+ display: "grid",
59
+ gridTemplateColumns: "repeat(auto-fill, minmax(140px, 1fr))",
60
+ gap: "10px",
61
+ }}
62
+ >
63
+ {tier.Rewards.map((reward, rewardIndex) => {
64
+ const id = reward.Reward._data.id;
65
+ const amount = reward.Reward._data._am;
66
+ const itemData = resolveItem(id);
67
+ return (
68
+ <div key={rewardIndex}>
69
+ <ItemCard
70
+ id={id}
71
+ amount={amount}
72
+ label={id}
73
+ itemData={itemData}
74
+ rarityColor={itemData?.rarity ? getRarityColor(itemData.rarity) : null}
75
+ />
76
+ </div>
77
+ );
78
+ })}
79
+ </div>
80
+ </div>
81
+ )}
82
+ </div>
83
+ ))}
84
+ </div>
85
+ </div>
38
86
  );
39
87
  };
40
88
 
@@ -1,23 +1,27 @@
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 BoostsComponent: React.FC<{
7
- configData?: CollectionConfigData<"Boosts">;
6
+ configData: CollectionConfigData<"Boosts">;
8
7
  }> = ({ configData }) => {
9
8
  return (
10
- <GenericFetchComponent<CollectionConfigData<"Boosts">>
11
- collectionName="Boosts"
12
- configData={configData}
13
- render={(data) => (
14
- <div>
15
- <h2>{data.DisplayName}</h2>
16
- <ImageComponent src={data.Icon} alt={data.DisplayName} />
17
- <p>Maximum Percent: {data.MaximumPercent}%</p>
18
- </div>
19
- )}
20
- />
9
+ <div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
10
+ <ItemCard
11
+ id={configData.DisplayName}
12
+ amount={1}
13
+ label={configData.DisplayName}
14
+ itemData={{
15
+ icon: configData.Icon,
16
+ rarity: undefined,
17
+ name: configData.DisplayName
18
+ }}
19
+ rarityColor={null}
20
+ />
21
+ <div style={{ marginTop: '10px', fontSize: '0.9em', color: '#666', textAlign: 'center' }}>
22
+ <p>Max Percent: {configData.MaximumPercent}%</p>
23
+ </div>
24
+ </div>
21
25
  );
22
26
  };
23
27
 
@@ -1,31 +1,33 @@
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 BoothsComponent: React.FC<{
7
- configData?: CollectionConfigData<"Booths">;
7
+ configData: CollectionConfigData<"Booths">;
8
8
  }> = ({ configData }) => {
9
+ const { getRarityColor } = useItemResolution();
10
+ const rarityColor = configData.Rarity ? getRarityColor(configData.Rarity) : null;
11
+
9
12
  return (
10
- <GenericFetchComponent<CollectionConfigData<"Booths">>
11
- collectionName="Booths"
12
- configData={configData}
13
- render={(data) => (
14
- <div>
15
- <h2>{data.DisplayName}</h2>
16
- <ImageComponent src={data.Icon} alt={data.DisplayName} />
17
- <p>Description: {data.Desc}</p>
18
- <p>Rarity: {data.Rarity.DisplayName}</p>
19
- <p>Rarity Number: {data.Rarity.RarityNumber}</p>
20
- {data.Hidden && <p>Hidden: Yes</p>}
21
- {data.Tradable && <p>Tradable: Yes</p>}
22
- {data.OffSale && <p>Off Sale: Yes</p>}
23
- {data.ProductId && <p>Product ID: {data.ProductId}</p>}
24
- {data.DiamondPrice && <p>Diamond Price: {data.DiamondPrice}</p>}
25
- {data.Sittable && <p>Sittable: Yes</p>}
26
- </div>
27
- )}
28
- />
13
+ <div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
14
+ <ItemCard
15
+ id={configData.DisplayName}
16
+ amount={1}
17
+ label={configData.DisplayName}
18
+ itemData={{
19
+ icon: configData.Icon,
20
+ rarity: configData.Rarity,
21
+ name: configData.DisplayName
22
+ }}
23
+ rarityColor={rarityColor}
24
+ />
25
+ <div style={{ marginTop: '10px', fontSize: '0.9em', color: '#666', textAlign: 'center' }}>
26
+ <p>{configData.Desc}</p>
27
+ {configData.ProductId && <p>ID: {configData.ProductId}</p>}
28
+ {configData.DiamondPrice && <p>💎 {configData.DiamondPrice}</p>}
29
+ </div>
30
+ </div>
29
31
  );
30
32
  };
31
33
 
@@ -1,34 +1,59 @@
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 BoxesComponent: React.FC<{
7
- configData?: CollectionConfigData<"Boxes">;
7
+ configData: CollectionConfigData<"Boxes">;
8
8
  }> = ({ configData }) => {
9
+ const { getRarityColor } = useItemResolution();
10
+ const rarityColor = configData.Rarity ? getRarityColor(configData.Rarity) : null;
11
+
9
12
  return (
10
- <GenericFetchComponent<CollectionConfigData<"Boxes">>
11
- collectionName="Boxes"
12
- configData={configData}
13
- render={(data) => (
13
+ <div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
14
+
15
+ <div style={{ maxWidth: '300px', margin: '0 auto 15px auto' }}>
16
+ <ItemCard
17
+ id={configData.DisplayName}
18
+ amount={1}
19
+ label={configData.DisplayName}
20
+ itemData={{
21
+ icon: configData.Icons && configData.Icons.length > 0 ? configData.Icons[0].Icon : undefined,
22
+ rarity: configData.Rarity,
23
+ name: configData.DisplayName
24
+ }}
25
+ rarityColor={rarityColor}
26
+ />
27
+ </div>
28
+
29
+ <div style={{ marginBottom: '20px', fontSize: '0.9em', color: '#666', textAlign: 'center' }}>
30
+ <p>{configData.Desc}</p>
31
+ <p style={{ fontWeight: 'bold' }}>Capacity: {configData.Capacity}</p>
32
+ </div>
33
+
34
+ {configData.Icons && configData.Icons.length > 0 && (
14
35
  <div>
15
- <h2>{data.DisplayName}</h2>
16
- <p>Description: {data.Desc}</p>
17
- <p>Capacity: {data.Capacity}</p>
18
- <p>Rarity: {data.Rarity.DisplayName}</p>
19
- <p>Rarity Number: {data.Rarity.RarityNumber}</p>
20
- <h3>Icons:</h3>
21
- <ul>
22
- {data.Icons.map((icon, index) => (
23
- <li key={index}>
24
- <ImageComponent src={icon.Icon} alt={icon.Name} />
25
- <span>{icon.Name}</span>
26
- </li>
36
+ <h3 style={{ fontSize: '1.2em', borderBottom: '1px solid #eee', paddingBottom: '5px' }}>Variants</h3>
37
+ <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(120px, 1fr))", gap: "10px", marginTop: '15px' }}>
38
+ {configData.Icons.map((icon, index) => (
39
+ <div key={index} style={{ textAlign: 'center' }}>
40
+ <ItemCard
41
+ id={icon.Name}
42
+ amount={1}
43
+ label={icon.Name}
44
+ itemData={{
45
+ icon: icon.Icon,
46
+ rarity: configData.Rarity,
47
+ name: icon.Name
48
+ }}
49
+ rarityColor={rarityColor}
50
+ />
51
+ </div>
27
52
  ))}
28
- </ul>
53
+ </div>
29
54
  </div>
30
55
  )}
31
- />
56
+ </div>
32
57
  );
33
58
  };
34
59
 
@@ -1,24 +1,94 @@
1
1
  import React from "react";
2
2
  import { CollectionConfigData } from "ps99-api";
3
3
  import { GenericFetchComponent } from "./GenericFetchComponent";
4
+ import ItemCard from "./ItemCard";
4
5
 
5
6
  const BuffsComponent: React.FC<{
6
- configData?: CollectionConfigData<"Buffs">;
7
+ configData: CollectionConfigData<"Buffs">;
7
8
  }> = ({ configData }) => {
8
9
  return (
9
- <GenericFetchComponent<CollectionConfigData<"Buffs">>
10
- collectionName="Buffs"
11
- configData={configData}
12
- render={(data) => (
13
- <div>
14
- <h2>{data.DisplayName}</h2>
15
- <p>Associated Item ID: {data.AssociatedItemID}</p>
16
- <p>Associated Item Class: {data.AssociatedItemClass}</p>
17
- <p>Length: {data.Length} seconds</p>
18
- {data.IgnoreInstancePause && <p>Ignore Instance Pause: Yes</p>}
10
+ <div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
11
+
12
+ {/* Grid Layout */}
13
+ <div style={{
14
+ display: 'grid',
15
+ gridTemplateColumns: '1fr 1fr',
16
+ gap: '40px',
17
+ alignItems: 'start'
18
+ }}>
19
+
20
+ {/* Left Column: Associated Item (Visuals) */}
21
+ <div style={{
22
+ background: '#f9f9f9',
23
+ padding: '30px',
24
+ borderRadius: '24px',
25
+ border: '2px solid #eee',
26
+ display: 'flex',
27
+ flexDirection: 'column',
28
+ alignItems: 'center',
29
+ gap: '20px'
30
+ }}>
31
+ {configData.AssociatedItemClass === "Misc" && (
32
+ <>
33
+ <h3 style={{ margin: 0, color: '#888', textTransform: 'uppercase', fontSize: '0.9rem' }}>Associated Item</h3>
34
+ <div style={{ width: '100%', maxWidth: '300px' }}>
35
+ <GenericFetchComponent
36
+ collectionName="MiscItems"
37
+ configName={configData.AssociatedItemID}
38
+ render={(data: any) => (
39
+ <ItemCard
40
+ id={data.DisplayName}
41
+ amount={1}
42
+ label={data.DisplayName}
43
+ itemData={{
44
+ icon: data.Icon,
45
+ rarity: data.Rarity,
46
+ name: data.DisplayName
47
+ }}
48
+ // Simple non-interactive card style
49
+ />
50
+ )}
51
+ />
52
+ </div>
53
+ </>
54
+ )}
55
+ {/* If no associated item, maybe show a generic buff icon? */}
19
56
  </div>
20
- )}
21
- />
57
+
58
+ {/* Right Column: Stats & Data */}
59
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '20px' }}>
60
+
61
+ {/* Stats Grid */}
62
+ <div style={{
63
+ display: 'grid',
64
+ gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))',
65
+ gap: '20px',
66
+ width: '100%'
67
+ }}>
68
+ <div style={{ background: '#e0f7fa', padding: '15px', borderRadius: '12px', border: '1px solid #b2ebf2' }}>
69
+ <strong style={{ display: 'block', fontSize: '0.9rem', color: '#006064', marginBottom: '5px' }}>CLASS</strong>
70
+ <span style={{ fontSize: '1.2rem', fontWeight: 'bold', color: '#00838f' }}>{configData.AssociatedItemClass}</span>
71
+ </div>
72
+ <div style={{ background: '#fff3e0', padding: '15px', borderRadius: '12px', border: '1px solid #ffe0b2' }}>
73
+ <strong style={{ display: 'block', fontSize: '0.9rem', color: '#e65100', marginBottom: '5px' }}>DURATION</strong>
74
+ <span style={{ fontSize: '1.2rem', fontWeight: 'bold', color: '#ef6c00' }}>{configData.Length}s</span>
75
+ </div>
76
+ {configData.IgnoreInstancePause && (
77
+ <div style={{ background: '#fce4ec', padding: '15px', borderRadius: '12px', border: '1px solid #f8bbd0' }}>
78
+ <strong style={{ display: 'block', fontSize: '0.9rem', color: '#880e4f', marginBottom: '5px' }}>SPECIAL</strong>
79
+ <span style={{ fontSize: '1.2rem', fontWeight: 'bold', color: '#ad1457' }}>Ignores Pause</span>
80
+ </div>
81
+ )}
82
+ <div style={{ background: '#f5f5f5', padding: '15px', borderRadius: '12px', border: '1px solid #ddd' }}>
83
+ <strong style={{ display: 'block', fontSize: '0.9rem', color: '#666', marginBottom: '5px' }}>ITEM ID</strong>
84
+ <span style={{ fontSize: '0.9rem', fontFamily: 'monospace', color: '#333' }}>{configData.AssociatedItemID}</span>
85
+ </div>
86
+ </div>
87
+
88
+ </div>
89
+
90
+ </div>
91
+ </div>
22
92
  );
23
93
  };
24
94
 
@@ -1,40 +1,58 @@
1
1
  import React from "react";
2
2
  import { CollectionConfigData } from "ps99-api";
3
- import { GenericFetchComponent } from "./GenericFetchComponent";
3
+ import ItemCard from "./ItemCard";
4
4
  import ImageComponent from "./ImageComponent";
5
+ import { useItemResolution } from "../hooks/useItemResolution";
5
6
 
6
7
  const CharmsComponent: React.FC<{
7
- configData?: CollectionConfigData<"Charms">;
8
+ configData: CollectionConfigData<"Charms">;
8
9
  }> = ({ configData }) => {
10
+ const { getRarityColor } = useItemResolution();
11
+
9
12
  return (
10
- <GenericFetchComponent<CollectionConfigData<"Charms">>
11
- collectionName="Charms"
12
- configData={configData}
13
- render={(data) => (
14
- <div>
15
- <h2>{data.Tiers[0].DisplayName}</h2>
16
- <ImageComponent src={data.Icon} alt={data.Tiers[0].DisplayName} />
17
- <p>Base Tier: {data.BaseTier}</p>
18
- <p>Max Tier: {data.MaxTier}</p>
19
- {data.DiminishPowerThreshold && (
20
- <p>Diminish Power Threshold: {data.DiminishPowerThreshold}</p>
21
- )}
22
- {data.Unique && <p>Unique: Yes</p>}
23
- <h3>Tiers:</h3>
24
- <ul>
25
- {data.Tiers.map((tier, index) => (
26
- <li key={index}>
27
- <p>{tier.DisplayName}</p>
28
- <p>Description: {tier.Desc}</p>
29
- <p>Power: {tier.Power}</p>
30
- <p>Rarity: {tier.Rarity.DisplayName}</p>
31
- <p>Rarity Number: {tier.Rarity.RarityNumber}</p>
32
- </li>
33
- ))}
34
- </ul>
13
+ <div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
14
+
15
+ <div style={{ display: 'flex', gap: '20px', justifyContent: 'center', flexWrap: 'wrap', marginBottom: '20px' }}>
16
+ <div style={{ width: '150px' }}>
17
+ <ImageComponent src={configData.Icon} alt={configData.Tiers[0].DisplayName} />
18
+ </div>
19
+ <div style={{ textAlign: 'left', minWidth: '200px' }}>
20
+ <p><strong>Base Tier:</strong> {configData.BaseTier}</p>
21
+ <p><strong>Max Tier:</strong> {configData.MaxTier}</p>
22
+ {configData.DiminishPowerThreshold && <p><strong>Diminish:</strong> {configData.DiminishPowerThreshold}</p>}
23
+ {configData.Unique && <span className="badge">Unique</span>}
24
+ </div>
25
+ </div>
26
+
27
+ <div style={{ marginTop: '20px' }}>
28
+ <h3 style={{ fontSize: '1.2em', borderBottom: '1px solid #eee', paddingBottom: '5px' }}>Tiers</h3>
29
+ <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(220px, 1fr))", gap: "15px", marginTop: '15px' }}>
30
+ {configData.Tiers.map((tier, index) => {
31
+ const rarityColor = tier.Rarity ? getRarityColor(tier.Rarity) : null;
32
+ return (
33
+ <div key={index}>
34
+ <ItemCard
35
+ id={tier.DisplayName}
36
+ amount={1}
37
+ label={tier.DisplayName}
38
+ tn={index + 1}
39
+ itemData={{
40
+ icon: configData.Icon,
41
+ rarity: tier.Rarity,
42
+ name: tier.DisplayName
43
+ }}
44
+ rarityColor={rarityColor}
45
+ />
46
+ <div style={{ marginTop: '10px', fontSize: '0.9em', color: '#666', textAlign: 'center' }}>
47
+ <p>{tier.Desc}</p>
48
+ <p style={{ fontWeight: 'bold' }}>Power: {tier.Power}</p>
49
+ </div>
50
+ </div>
51
+ );
52
+ })}
35
53
  </div>
36
- )}
37
- />
54
+ </div>
55
+ </div>
38
56
  );
39
57
  };
40
58