ps99-api 2.4.0 → 2.6.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.
- package/.github/workflows/release-on-main.yml +1 -2
- package/.idea/runConfigurations/test_changing.xml +1 -1
- package/debug_currency.json +57 -0
- package/debug_goals.json +271 -0
- package/dist/ps99-api.d.ts +2 -0
- package/dist/ps99-api.js +4 -1
- package/dist/ps99-api.js.map +1 -1
- package/dist/request-client/axios.js +6 -1
- package/dist/request-client/axios.js.map +1 -1
- package/dist/responses/collection/index.d.ts +1 -0
- package/dist/responses/collection/index.js +15 -0
- package/dist/responses/collection/index.js.map +1 -1
- package/dist/responses/collection/rarity.d.ts +1 -0
- package/example-web/react/package-lock.json +1504 -1470
- package/example-web/react2/package-lock.json +3089 -2766
- package/example-web/react2/package.json +6 -1
- package/example-web/react2/public/assets/gold_variant_icon.png +0 -0
- package/example-web/react2/public/assets/hot_cocoa_egg.png +0 -0
- package/example-web/react2/public/index.html +34 -31
- package/example-web/react2/src/App.tsx +15 -15
- package/example-web/react2/src/assets/guild_placeholder.png +0 -0
- package/example-web/react2/src/components/AchievementsComponent.tsx +74 -19
- package/example-web/react2/src/components/AutoSizer.tsx +49 -0
- package/example-web/react2/src/components/BoostsComponent.tsx +16 -5
- package/example-web/react2/src/components/BoothsComponent.tsx +22 -52
- package/example-web/react2/src/components/BoxesComponent.tsx +48 -16
- package/example-web/react2/src/components/BuffsComponent.tsx +82 -34
- package/example-web/react2/src/components/CharmsComponent.tsx +84 -24
- package/example-web/react2/src/components/CollectionConfigIndex.tsx +867 -33
- package/example-web/react2/src/components/CollectionsIndex.tsx +380 -27
- package/example-web/react2/src/components/CollectionsLayout.tsx +60 -0
- package/example-web/react2/src/components/CurrencyComponent.tsx +57 -39
- package/example-web/react2/src/components/DynamicCollectionConfigData.tsx +172 -15
- package/example-web/react2/src/components/EggsComponent.tsx +50 -12
- package/example-web/react2/src/components/EnchantsComponent.tsx +88 -42
- package/example-web/react2/src/components/FishingRodsComponent.tsx +36 -22
- package/example-web/react2/src/components/Footer.tsx +18 -8
- package/example-web/react2/src/components/FruitsComponent.tsx +40 -17
- package/example-web/react2/src/components/GenericFetchComponent.tsx +9 -1
- package/example-web/react2/src/components/GuildBattlesComponent.tsx +41 -34
- package/example-web/react2/src/components/Header.tsx +39 -52
- package/example-web/react2/src/components/HomePage.tsx +15 -17
- package/example-web/react2/src/components/HoverboardsComponent.tsx +23 -99
- package/example-web/react2/src/components/ImageComponent.tsx +255 -45
- package/example-web/react2/src/components/ItemCard.tsx +240 -0
- package/example-web/react2/src/components/LootboxesComponent.tsx +22 -7
- package/example-web/react2/src/components/MasteryComponent.tsx +165 -30
- package/example-web/react2/src/components/MerchantsComponent.tsx +41 -16
- package/example-web/react2/src/components/MiscItemsComponent.tsx +26 -31
- package/example-web/react2/src/components/PetsComponent.tsx +100 -61
- package/example-web/react2/src/components/PotionsComponent.tsx +121 -27
- package/example-web/react2/src/components/RandomEventsComponent.tsx +32 -23
- package/example-web/react2/src/components/RanksComponent.tsx +187 -62
- package/example-web/react2/src/components/RarityComponent.tsx +123 -5
- package/example-web/react2/src/components/ReactWindowMock.tsx +73 -0
- package/example-web/react2/src/components/RebirthsComponent.tsx +36 -19
- package/example-web/react2/src/components/SecretRoomsComponent.tsx +5 -4
- package/example-web/react2/src/components/SeedsComponent.tsx +41 -21
- package/example-web/react2/src/components/ShovelsComponent.tsx +21 -9
- package/example-web/react2/src/components/Sidebar.tsx +105 -0
- package/example-web/react2/src/components/SprinklersComponent.tsx +25 -10
- package/example-web/react2/src/components/Tooltip.tsx +36 -0
- package/example-web/react2/src/components/UltimatesComponent.tsx +28 -16
- package/example-web/react2/src/components/UpgradesComponent.tsx +97 -47
- package/example-web/react2/src/components/WateringCansComponent.tsx +20 -14
- package/example-web/react2/src/components/WorldsComponent.tsx +21 -11
- package/example-web/react2/src/components/XPPotionsComponent.tsx +28 -11
- package/example-web/react2/src/components/ZoneFlagsComponent.tsx +25 -14
- package/example-web/react2/src/components/ZonesComponent.tsx +43 -60
- package/example-web/react2/src/constants/collectionIcons.ts +29 -0
- package/example-web/react2/src/context/CollectionDataContext.tsx +62 -0
- package/example-web/react2/src/context/ScrollContext.tsx +35 -0
- package/example-web/react2/src/hooks/useCollapsibleHeader.ts +69 -0
- package/example-web/react2/src/hooks/useExpandableList.ts +38 -0
- package/example-web/react2/src/hooks/useItemResolution.ts +351 -0
- package/example-web/react2/src/index.css +257 -0
- package/example-web/react2/src/index.tsx +2 -1
- package/example-web/react2/src/utils/gigantix.ts +40 -0
- package/example-web/react2/temp_model.rbxm +0 -0
- package/example-web/react2/webpack.config.js +103 -47
- package/package.json +11 -11
- package/ranks.json +1 -0
- package/repro_collection_fetch.ts +33 -0
- package/repro_image_fetch.ts +50 -0
- package/src/__tests__/__snapshots__/ps99-api-changes.ts.snap +34841 -10439
- package/src/__tests__/__snapshots__/ps99-api-live.ts.snap +160667 -67217
- package/src/ps99-api.ts +9 -5
- package/src/request-client/axios.ts +6 -2
- package/src/responses/collection/index.ts +1 -0
- package/src/responses/collection/rarity.ts +1 -0
- package/tsconfig.json +1 -1
- 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",
|
|
Binary file
|
|
Binary file
|
|
@@ -1,33 +1,36 @@
|
|
|
1
1
|
<!doctype html>
|
|
2
2
|
<html lang="en">
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
</
|
|
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,29 +1,29 @@
|
|
|
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";
|
|
10
|
+
import { ScrollProvider } from "./context/ScrollContext";
|
|
9
11
|
|
|
10
12
|
const App: React.FC = () => {
|
|
11
13
|
return (
|
|
12
14
|
<Router>
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
</Routes>
|
|
26
|
-
<Footer />
|
|
15
|
+
<ScrollProvider>
|
|
16
|
+
<Header />
|
|
17
|
+
<Routes>
|
|
18
|
+
<Route path="/" element={<HomePage />} />
|
|
19
|
+
<Route path="/collections" element={<CollectionsIndex />} />
|
|
20
|
+
<Route element={<CollectionsLayout />}>
|
|
21
|
+
<Route path="/collections/:collectionName" element={<CollectionConfigIndex />} />
|
|
22
|
+
<Route path="/collections/:collectionName/:configName" element={<DynamicCollectionConfigData />} />
|
|
23
|
+
</Route>
|
|
24
|
+
</Routes>
|
|
25
|
+
<Footer />
|
|
26
|
+
</ScrollProvider>
|
|
27
27
|
</Router>
|
|
28
28
|
);
|
|
29
29
|
};
|
|
Binary file
|
|
@@ -1,32 +1,87 @@
|
|
|
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";
|
|
6
|
+
import { useExpandableList } from "../hooks/useExpandableList";
|
|
4
7
|
|
|
5
8
|
const AchievementsComponent: React.FC<{
|
|
6
9
|
configData: CollectionConfigData<"Achievements">;
|
|
7
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
|
+
|
|
8
16
|
return (
|
|
9
|
-
<div>
|
|
10
|
-
|
|
11
|
-
<
|
|
12
|
-
|
|
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" }}>
|
|
13
27
|
{configData.Tiers.map((tier, index) => (
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
|
|
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>
|
|
28
83
|
))}
|
|
29
|
-
</
|
|
84
|
+
</div>
|
|
30
85
|
</div>
|
|
31
86
|
);
|
|
32
87
|
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React, { useState, useLayoutEffect, useRef } from "react";
|
|
2
|
+
|
|
3
|
+
export default function AutoSizer({
|
|
4
|
+
children,
|
|
5
|
+
className,
|
|
6
|
+
defaultHeight,
|
|
7
|
+
defaultWidth,
|
|
8
|
+
style,
|
|
9
|
+
renderProp,
|
|
10
|
+
...rest
|
|
11
|
+
}: any) {
|
|
12
|
+
const [size, setSize] = useState({ height: defaultHeight || 0, width: defaultWidth || 0 });
|
|
13
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
14
|
+
|
|
15
|
+
useLayoutEffect(() => {
|
|
16
|
+
const element = ref.current;
|
|
17
|
+
if (!element) return;
|
|
18
|
+
|
|
19
|
+
const ResizeObserver = (window as any).ResizeObserver;
|
|
20
|
+
if (!ResizeObserver) return;
|
|
21
|
+
|
|
22
|
+
const observer = new ResizeObserver((entries: any) => {
|
|
23
|
+
for (const entry of entries) {
|
|
24
|
+
if (entry.contentRect) {
|
|
25
|
+
const { width, height } = entry.contentRect;
|
|
26
|
+
// Rounding for stability
|
|
27
|
+
setSize({ width: Math.floor(width), height: Math.floor(height) });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
observer.observe(element);
|
|
33
|
+
return () => observer.disconnect();
|
|
34
|
+
}, []);
|
|
35
|
+
|
|
36
|
+
const childParams = size;
|
|
37
|
+
|
|
38
|
+
// Render prop takes precedence if provided (based on my usage in other files)
|
|
39
|
+
const content = renderProp
|
|
40
|
+
? renderProp(childParams)
|
|
41
|
+
: (typeof children === 'function' ? children(childParams) : children);
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div className={className} ref={ref} style={{ ...style, width: '100%', height: '100%', overflow: 'hidden' }}>
|
|
45
|
+
{/* Only render content if we have size, or if default provided to avoid jumpiness */}
|
|
46
|
+
{(size.width > 0 && size.height > 0) ? content : null}
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { CollectionConfigData } from "ps99-api";
|
|
3
|
-
import
|
|
3
|
+
import ItemCard from "./ItemCard";
|
|
4
4
|
|
|
5
5
|
const BoostsComponent: React.FC<{
|
|
6
6
|
configData: CollectionConfigData<"Boosts">;
|
|
7
7
|
}> = ({ configData }) => {
|
|
8
8
|
return (
|
|
9
|
-
<div>
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
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>
|
|
13
24
|
</div>
|
|
14
25
|
);
|
|
15
26
|
};
|
|
@@ -1,62 +1,32 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { CollectionConfigData } from "ps99-api";
|
|
3
|
-
import
|
|
3
|
+
import ItemCard from "./ItemCard";
|
|
4
|
+
import { useItemResolution } from "../hooks/useItemResolution";
|
|
4
5
|
|
|
5
6
|
const BoothsComponent: React.FC<{
|
|
6
7
|
configData: CollectionConfigData<"Booths">;
|
|
7
8
|
}> = ({ configData }) => {
|
|
9
|
+
const { getRarityColor } = useItemResolution();
|
|
10
|
+
const rarityColor = configData.Rarity ? getRarityColor(configData.Rarity) : null;
|
|
11
|
+
|
|
8
12
|
return (
|
|
9
|
-
<div
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
<
|
|
22
|
-
<
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
</p>
|
|
27
|
-
<p>
|
|
28
|
-
<strong>Rarity Number:</strong> {configData.Rarity.RarityNumber}
|
|
29
|
-
</p>
|
|
30
|
-
{configData.Hidden && (
|
|
31
|
-
<p>
|
|
32
|
-
<strong>Hidden:</strong> Yes
|
|
33
|
-
</p>
|
|
34
|
-
)}
|
|
35
|
-
{configData.Tradable && (
|
|
36
|
-
<p>
|
|
37
|
-
<strong>Tradable:</strong> Yes
|
|
38
|
-
</p>
|
|
39
|
-
)}
|
|
40
|
-
{configData.OffSale && (
|
|
41
|
-
<p>
|
|
42
|
-
<strong>Off Sale:</strong> Yes
|
|
43
|
-
</p>
|
|
44
|
-
)}
|
|
45
|
-
{configData.ProductId && (
|
|
46
|
-
<p>
|
|
47
|
-
<strong>Product ID:</strong> {configData.ProductId}
|
|
48
|
-
</p>
|
|
49
|
-
)}
|
|
50
|
-
{configData.DiamondPrice && (
|
|
51
|
-
<p>
|
|
52
|
-
<strong>Diamond Price:</strong> {configData.DiamondPrice}
|
|
53
|
-
</p>
|
|
54
|
-
)}
|
|
55
|
-
{configData.Sittable && (
|
|
56
|
-
<p>
|
|
57
|
-
<strong>Sittable:</strong> Yes
|
|
58
|
-
</p>
|
|
59
|
-
)}
|
|
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>
|
|
60
30
|
</div>
|
|
61
31
|
);
|
|
62
32
|
};
|
|
@@ -1,26 +1,58 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { CollectionConfigData } from "ps99-api";
|
|
3
|
-
import
|
|
3
|
+
import ItemCard from "./ItemCard";
|
|
4
|
+
import { useItemResolution } from "../hooks/useItemResolution";
|
|
4
5
|
|
|
5
6
|
const BoxesComponent: React.FC<{
|
|
6
7
|
configData: CollectionConfigData<"Boxes">;
|
|
7
8
|
}> = ({ configData }) => {
|
|
9
|
+
const { getRarityColor } = useItemResolution();
|
|
10
|
+
const rarityColor = configData.Rarity ? getRarityColor(configData.Rarity) : null;
|
|
11
|
+
|
|
8
12
|
return (
|
|
9
|
-
<div>
|
|
10
|
-
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
</
|
|
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 && (
|
|
35
|
+
<div>
|
|
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>
|
|
52
|
+
))}
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
)}
|
|
24
56
|
</div>
|
|
25
57
|
);
|
|
26
58
|
};
|
|
@@ -1,45 +1,93 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { CollectionConfigData } from "ps99-api";
|
|
3
|
-
import
|
|
3
|
+
import { GenericFetchComponent } from "./GenericFetchComponent";
|
|
4
|
+
import ItemCard from "./ItemCard";
|
|
4
5
|
|
|
5
6
|
const BuffsComponent: React.FC<{
|
|
6
7
|
configData: CollectionConfigData<"Buffs">;
|
|
7
8
|
}> = ({ configData }) => {
|
|
8
9
|
return (
|
|
9
|
-
<div
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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? */}
|
|
41
56
|
</div>
|
|
42
|
-
|
|
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>
|
|
43
91
|
</div>
|
|
44
92
|
);
|
|
45
93
|
};
|