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
@@ -6,8 +6,8 @@ import {
6
6
  RawStackKey,
7
7
  LootTableData,
8
8
  } from "ps99-api";
9
- import { GenericFetchComponent } from "./GenericFetchComponent";
10
- import ImageComponent from "./ImageComponent";
9
+ import ItemCard from "./ItemCard";
10
+ import { useItemResolution } from "../hooks/useItemResolution";
11
11
 
12
12
  const parseRawStackKey = (rawStackKey: RawStackKey): LootTableData => {
13
13
  try {
@@ -21,21 +21,25 @@ const parseRawStackKey = (rawStackKey: RawStackKey): LootTableData => {
21
21
  const renderLootTable = (lootTable: LootTableRoot | LootTableRoot[]) => {
22
22
  const lootTableArray = Array.isArray(lootTable) ? lootTable : [lootTable];
23
23
  return lootTableArray.map((table, index) => (
24
- <div key={index}>
24
+ <div key={index} style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))', gap: '10px' }}>
25
25
  {table.entries.map((entry: LootTableEntry, entryIndex: number) => {
26
26
  const parsedStackKey = entry.Value._stackKey
27
27
  ? parseRawStackKey(entry.Value._stackKey)
28
28
  : null;
29
29
  return (
30
- <div key={entryIndex}>
31
- <h4>Loot Entry {entryIndex + 1}</h4>
32
- <p>Weight: {entry.Weight}</p>
30
+ <div key={entryIndex} style={{ padding: '10px', border: '1px solid #eee', borderRadius: '8px', background: '#f9f9f9', textAlign: 'left' }}>
31
+ <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '5px' }}>
32
+ <span style={{ fontWeight: 'bold', fontSize: '0.9em' }}>Entry {entryIndex + 1}</span>
33
+ <span className="badge">Weight: {entry.Weight}</span>
34
+ </div>
33
35
  {parsedStackKey && (
34
- <>
35
- <p>Item ID: {parsedStackKey.id}</p>
36
- {parsedStackKey.tn && <p>Item TN: {parsedStackKey.tn}</p>}
37
- {parsedStackKey._am && <p>Amount: {parsedStackKey._am}</p>}
38
- </>
36
+ <div style={{ fontSize: '0.85em', color: '#555' }}>
37
+ <div style={{ display: 'flex', alignItems: 'center', gap: '5px' }}>
38
+ <span>ID: <strong>{parsedStackKey.id}</strong></span>
39
+ </div>
40
+ {parsedStackKey.tn && <div>Tier/TN: {parsedStackKey.tn}</div>}
41
+ {parsedStackKey._am && <div>Amount: {parsedStackKey._am}</div>}
42
+ </div>
39
43
  )}
40
44
  </div>
41
45
  );
@@ -45,29 +49,38 @@ const renderLootTable = (lootTable: LootTableRoot | LootTableRoot[]) => {
45
49
  };
46
50
 
47
51
  const SeedComponent: React.FC<{
48
- configData?: CollectionConfigData<"Seeds">;
52
+ configData: CollectionConfigData<"Seeds">;
49
53
  }> = ({ configData }) => {
54
+ const { getRarityColor } = useItemResolution();
55
+ const rarityColor = configData.Rarity ? getRarityColor(configData.Rarity) : null;
56
+
50
57
  return (
51
- <GenericFetchComponent<CollectionConfigData<"Seeds">>
52
- collectionName="Seeds"
53
- configData={configData}
54
- render={(data) => (
55
- <div>
56
- <h2>Seed: {data.DisplayName}</h2>
57
- <p>Description: {data.Desc}</p>
58
- <p>Grow Time: {data.GrowTime} seconds</p>
59
- <p>
60
- Rarity: {data.Rarity.DisplayName} (Rarity Number:{" "}
61
- {data.Rarity.RarityNumber})
62
- </p>
63
- <ImageComponent src={data.Icon} alt={data.DisplayName} />
64
- <div>
65
- <h3>Loot Table</h3>
66
- {renderLootTable(data.LootTable)}
67
- </div>
68
- </div>
69
- )}
70
- />
58
+ <div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
59
+
60
+ <div style={{ maxWidth: '300px', margin: '0 auto 15px auto' }}>
61
+ <ItemCard
62
+ id={configData.DisplayName}
63
+ amount={1}
64
+ label={configData.DisplayName}
65
+ itemData={{
66
+ icon: configData.Icon,
67
+ rarity: configData.Rarity,
68
+ name: configData.DisplayName
69
+ }}
70
+ rarityColor={rarityColor}
71
+ />
72
+ </div>
73
+
74
+ <div style={{ marginBottom: '20px', fontSize: '0.9em', color: '#666', textAlign: 'center' }}>
75
+ <p>{configData.Desc}</p>
76
+ <p style={{ fontWeight: 'bold', marginTop: '5px' }}>Grow Time: {configData.GrowTime}s</p>
77
+ </div>
78
+
79
+ <div style={{ borderTop: '1px solid #eee', paddingTop: '15px' }}>
80
+ <h3 style={{ textAlign: 'center', fontSize: '1.2em', marginBottom: '15px' }}>Loot Table</h3>
81
+ {renderLootTable(configData.LootTable)}
82
+ </div>
83
+ </div>
71
84
  );
72
85
  };
73
86
 
@@ -1,27 +1,32 @@
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 ShovelComponent: React.FC<{
7
- configData?: CollectionConfigData<"Shovels">;
6
+ configData: CollectionConfigData<"Shovels">;
8
7
  }> = ({ configData }) => {
9
8
  return (
10
- <GenericFetchComponent<CollectionConfigData<"Shovels">>
11
- collectionName="Shovels"
12
- configData={configData}
13
- render={(data) => (
14
- <div>
15
- <h2>Shovel: {data.DisplayName}</h2>
16
- <p>Description: {data.Desc}</p>
17
- <p>Associated Item ID: {data.AssociatedItemID}</p>
18
- {data.MerchantSalePrice && (
19
- <p>Merchant Sale Price: {data.MerchantSalePrice}</p>
20
- )}
21
- <ImageComponent src={data.Icon} alt={data.DisplayName} />
22
- </div>
23
- )}
24
- />
9
+ <div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
10
+
11
+ <div style={{ maxWidth: '300px', margin: '0 auto 15px auto' }}>
12
+ <ItemCard
13
+ id={configData.DisplayName}
14
+ amount={1}
15
+ label={configData.DisplayName}
16
+ itemData={{
17
+ icon: configData.Icon,
18
+ rarity: undefined,
19
+ name: configData.DisplayName
20
+ }}
21
+ rarityColor={null}
22
+ />
23
+ </div>
24
+
25
+ <div style={{ fontSize: '0.9em', color: '#666', textAlign: 'center' }}>
26
+ <p>{configData.Desc}</p>
27
+ {configData.MerchantSalePrice && <p style={{ marginTop: '10px' }}><strong>Sale Price:</strong> {configData.MerchantSalePrice}</p>}
28
+ </div>
29
+ </div>
25
30
  );
26
31
  };
27
32
 
@@ -0,0 +1,105 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { Link, useParams } from "react-router-dom";
3
+ import { PetSimulator99API, CollectionName } from "ps99-api";
4
+
5
+ import { COLLECTION_ICONS } from "../constants/collectionIcons";
6
+
7
+
8
+ interface SidebarProps {
9
+ currentCollection?: string;
10
+ }
11
+
12
+ const Sidebar: React.FC<SidebarProps> = ({ currentCollection }) => {
13
+ const [collections, setCollections] = useState<CollectionName[]>([]);
14
+
15
+ useEffect(() => {
16
+ const fetchCollections = async () => {
17
+ const api = new PetSimulator99API();
18
+ const response = await api.getCollections();
19
+ if (response.status === "ok") {
20
+ // Filter out collections that might not have icons or just to keep it clean if needed
21
+ setCollections(response.data);
22
+ }
23
+ };
24
+ fetchCollections();
25
+ }, []);
26
+
27
+ return (
28
+ <div style={{
29
+ width: "260px",
30
+ backgroundColor: "#fff",
31
+ borderRight: "4px solid #333",
32
+ display: "flex",
33
+ flexDirection: "column",
34
+ padding: "15px",
35
+ gap: "10px",
36
+ flexShrink: 0,
37
+ overflowY: "auto",
38
+ maxHeight: "100%",
39
+ }}>
40
+ <h3 style={{
41
+ textAlign: "center",
42
+ marginBottom: "15px",
43
+ color: "#333",
44
+ fontSize: "1.5rem",
45
+ fontWeight: "900",
46
+ textShadow: "2px 2px 0px #eee",
47
+ fontFamily: "'Fredoka One', cursive, sans-serif", // Assuming font availability or fallback
48
+ }}>
49
+ Terminal!
50
+ </h3>
51
+
52
+ <div style={{
53
+ display: "grid",
54
+ gridTemplateColumns: "1fr 1fr",
55
+ gap: "10px",
56
+ }}>
57
+ {collections.map((collection) => {
58
+ const isActive = currentCollection === collection;
59
+ const icon = COLLECTION_ICONS[collection] || "📦";
60
+
61
+ return (
62
+ <Link
63
+ key={collection}
64
+ to={`/collections/${collection}`}
65
+ style={{
66
+ display: "flex",
67
+ flexDirection: "column",
68
+ alignItems: "center",
69
+ justifyContent: "center",
70
+ aspectRatio: "1/1",
71
+ backgroundColor: isActive ? "#00b894" : "#dfe6e9",
72
+ borderRadius: "16px",
73
+ textDecoration: "none",
74
+ color: isActive ? "#fff" : "#2d3436",
75
+ border: "3px solid #333",
76
+ boxShadow: isActive ? "inset 0 -4px 0 rgba(0,0,0,0.2)" : "0 4px 0 #b2bec3",
77
+ transition: "all 0.1s ease",
78
+ transform: isActive ? "translateY(2px)" : "translateY(0)",
79
+ }}
80
+ >
81
+ <span style={{
82
+ fontSize: "2rem",
83
+ marginBottom: "4px",
84
+ filter: isActive ? "drop-shadow(0 2px 0 rgba(0,0,0,0.2))" : "none"
85
+ }}>
86
+ {icon}
87
+ </span>
88
+ <span style={{
89
+ fontSize: "0.8rem",
90
+ fontWeight: "800",
91
+ textAlign: "center",
92
+ lineHeight: "1.1",
93
+ textShadow: isActive ? "1px 1px 0 rgba(0,0,0,0.2)" : "none"
94
+ }}>
95
+ {collection}
96
+ </span>
97
+ </Link>
98
+ );
99
+ })}
100
+ </div>
101
+ </div>
102
+ );
103
+ };
104
+
105
+ export default Sidebar;
@@ -1,28 +1,36 @@
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 SprinklerComponent: React.FC<{
7
- configData?: CollectionConfigData<"Sprinklers">;
7
+ configData: CollectionConfigData<"Sprinklers">;
8
8
  }> = ({ configData }) => {
9
+ const { getRarityColor } = useItemResolution();
10
+ const rarityColor = configData.Rarity ? getRarityColor(configData.Rarity) : null;
11
+
9
12
  return (
10
- <GenericFetchComponent<CollectionConfigData<"Sprinklers">>
11
- collectionName="Sprinklers"
12
- configData={configData}
13
- render={(data) => (
14
- <div>
15
- <h2>Sprinkler: {data.Name}</h2>
16
- <p>Description: {data.Desc}</p>
17
- <p>Color: {data.Color}</p>
18
- <p>Duration: {data.Duration} seconds</p>
19
- <h3>Rarity</h3>
20
- <p>Rarity Number: {data.Rarity.RarityNumber}</p>
21
- <p>Display Name: {data.Rarity.DisplayName}</p>
22
- <ImageComponent src={data.Icon} alt={data.Name} />
23
- </div>
24
- )}
25
- />
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.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
+ />
27
+ </div>
28
+
29
+ <div style={{ fontSize: '0.9em', color: '#666', textAlign: 'center' }}>
30
+ <p>{configData.Desc}</p>
31
+ <p style={{ fontWeight: 'bold', marginTop: '5px' }}>Duration: {configData.Duration}s</p>
32
+ </div>
33
+ </div>
26
34
  );
27
35
  };
28
36
 
@@ -0,0 +1,36 @@
1
+ import React, { useState } from "react";
2
+
3
+ interface TooltipProps {
4
+ content: React.ReactNode;
5
+ children: React.ReactNode;
6
+ }
7
+
8
+ const Tooltip: React.FC<TooltipProps> = ({ content, children }) => {
9
+ const [active, setActive] = useState(false);
10
+
11
+ const showTip = () => {
12
+ setActive(true);
13
+ };
14
+
15
+ const hideTip = () => {
16
+ setActive(false);
17
+ };
18
+
19
+ return (
20
+ <div
21
+ className="tooltip-wrapper"
22
+ onMouseEnter={showTip}
23
+ onMouseLeave={hideTip}
24
+ style={{ position: 'relative', display: 'inline-block' }}
25
+ >
26
+ {children}
27
+ {active && (
28
+ <div className="tooltip-popover">
29
+ {content}
30
+ </div>
31
+ )}
32
+ </div>
33
+ );
34
+ };
35
+
36
+ export default Tooltip;
@@ -1,34 +1,39 @@
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 UltimateComponent: React.FC<{
7
- configData?: CollectionConfigData<"Ultimates">;
7
+ configData: CollectionConfigData<"Ultimates">;
8
8
  }> = ({ configData }) => {
9
+ const { getRarityColor } = useItemResolution();
10
+ const rarityColor = configData.Rarity ? getRarityColor(configData.Rarity) : null;
11
+
9
12
  return (
10
- <GenericFetchComponent<CollectionConfigData<"Ultimates">>
11
- collectionName="Ultimates"
12
- configData={configData}
13
- render={(data) => (
14
- <div>
15
- <h2>Ultimate: {data.DisplayName}</h2>
16
- <p>Description: {data.Desc}</p>
17
- <p>Max Tier: {data.MaxTier}</p>
18
- <p>FFlag Name: {data.FFlagName}</p>
19
- {data.Tradable && <p>Tradable</p>}
20
- {data.ProductId && <p>Product ID: {data.ProductId}</p>}
21
- {data.NotAllowedInInstances && <p>Not Allowed in Instances</p>}
22
- <h3>Rarity</h3>
23
- <p>Rarity Number: {data.Rarity.RarityNumber}</p>
24
- <p>Display Name: {data.Rarity.DisplayName}</p>
25
- <ImageComponent src={data.Icon} alt={data.DisplayName} />
26
- <h3>Tiers</h3>
27
- <p>Tier to Level Mapping: {data.TierToLevel.join(", ")}</p>
28
- <p>Level to Tier Mapping: {data.LevelToTier.join(", ")}</p>
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.Icon,
22
+ rarity: configData.Rarity,
23
+ name: configData.DisplayName
24
+ }}
25
+ rarityColor={rarityColor}
26
+ />
27
+ </div>
28
+
29
+ <div style={{ fontSize: '0.9em', color: '#666', textAlign: 'center' }}>
30
+ <p style={{ marginBottom: '10px' }}>{configData.Desc}</p>
31
+ <div style={{ display: 'flex', gap: '10px', justifyContent: 'center' }}>
32
+ <span className="badge">Max Tier: {configData.MaxTier}</span>
33
+ {configData.Tradable && <span className="badge">Tradable</span>}
29
34
  </div>
30
- )}
31
- />
35
+ </div>
36
+ </div>
32
37
  );
33
38
  };
34
39
 
@@ -1,73 +1,117 @@
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 { useExpandableList } from "../hooks/useExpandableList";
5
6
 
6
7
  const UpgradeComponent: React.FC<{
7
- configData?: CollectionConfigData<"Upgrades">;
8
+ configData: CollectionConfigData<"Upgrades">;
8
9
  }> = ({ configData }) => {
10
+ const { expandedIndices, toggle, expandAll, collapseAll, isExpanded } = useExpandableList(configData.TierCurrencies.length);
11
+
9
12
  return (
10
- <GenericFetchComponent<CollectionConfigData<"Upgrades">>
11
- collectionName="Upgrades"
12
- configData={configData}
13
- render={(data) => (
13
+ <div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
14
+ {configData.Icon && (
15
+ <div style={{ maxWidth: '100px', margin: '0 auto 20px' }}>
16
+ <ImageComponent src={configData.Icon} alt="Upgrade Icon" />
17
+ </div>
18
+ )}
19
+
20
+ <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '20px', marginBottom: '30px' }}>
14
21
  <div>
15
- <h2>Upgrade</h2>
16
- {data.Icon && <ImageComponent src={data.Icon} alt="Upgrade Icon" />}
17
- <h3>Tier Powers</h3>
18
- <ul>
19
- {data.TierPowers.map((power, index) => (
20
- <li key={index}>
21
- Tier {index + 1}: {power}
22
+ <h3 style={{ borderBottom: '1px solid #eee', paddingBottom: '5px' }}>Tier Powers</h3>
23
+ <ul style={{ listStyle: 'none', padding: 0, textAlign: 'left' }}>
24
+ {configData.TierPowers.map((power, index) => (
25
+ <li key={index} style={{ padding: '5px 0', borderBottom: '1px dashed #eee' }}>
26
+ <strong>Tier {index + 1}:</strong> {power}
22
27
  </li>
23
28
  ))}
24
29
  </ul>
25
- <h3>Tier Costs</h3>
26
- <ul>
27
- {data.TierCosts.map((cost, index) => (
28
- <li key={index}>
29
- Tier {index + 1}: {cost}
30
+ </div>
31
+ <div>
32
+ <h3 style={{ borderBottom: '1px solid #eee', paddingBottom: '5px' }}>Tier Costs</h3>
33
+ <ul style={{ listStyle: 'none', padding: 0, textAlign: 'left' }}>
34
+ {configData.TierCosts.map((cost, index) => (
35
+ <li key={index} style={{ padding: '5px 0', borderBottom: '1px dashed #eee' }}>
36
+ <strong>Tier {index + 1}:</strong> {cost}
30
37
  </li>
31
38
  ))}
32
39
  </ul>
33
- <h3>Tier Currencies</h3>
34
- {data.TierCurrencies.map((currency, index) => (
35
- <div key={index}>
36
- <h4>{currency.DisplayName}</h4>
37
- <p>Rarity Number: {currency.Rarity.RarityNumber}</p>
38
- <p>Description: {currency.Desc}</p>
39
- {currency.Tradable && <p>Tradable</p>}
40
- <p>Max Amount: {currency.MaxAmount}</p>
41
- <h5>Bag Tiers</h5>
42
- <ul>
43
- {currency.BagTiers.map((bagTier, bagIndex) => (
44
- <li key={bagIndex}>
45
- <p>Value: {bagTier.value}</p>
46
- <ImageComponent
47
- src={bagTier.image}
48
- alt={`Bag Tier ${bagTier.value}`}
49
- />
50
- </li>
51
- ))}
52
- </ul>
53
- <h5>Tiers</h5>
54
- <ul>
55
- {currency.Tiers.map((tier, tierIndex) => (
56
- <li key={tierIndex}>
57
- <p>Tier Name: {tier.tierName}</p>
58
- <p>Order: {tier.Order}</p>
59
- <p>Value: {tier.value}</p>
60
- {tier.isBottom && <p>Is Bottom</p>}
61
- <ImageComponent src={tier.orbImage} alt="Orb" />
62
- <ImageComponent src={tier.imageOutline} alt="Outline" />
63
- </li>
64
- ))}
65
- </ul>
66
- </div>
67
- ))}
68
40
  </div>
69
- )}
70
- />
41
+ </div>
42
+
43
+ <div>
44
+ <h3 style={{ fontSize: '1.2em', marginBottom: '15px' }}>Tier Currencies</h3>
45
+ <div style={{ marginBottom: '20px', display: 'flex', gap: '10px', justifyContent: 'center' }}>
46
+ <button onClick={expandAll} style={{ padding: '8px 16px', borderRadius: '8px', border: '1px solid #ddd', background: '#fff', cursor: 'pointer' }}>Expand All</button>
47
+ <button onClick={collapseAll} style={{ padding: '8px 16px', borderRadius: '8px', border: '1px solid #ddd', background: '#fff', cursor: 'pointer' }}>Collapse All</button>
48
+ </div>
49
+ {configData.TierCurrencies.map((currency, index) => (
50
+ <div key={index} style={{ background: '#f9f9f9', padding: '15px', borderRadius: '12px', marginBottom: '20px' }}>
51
+ <div
52
+ onClick={() => toggle(index)}
53
+ style={{ cursor: 'pointer' }}
54
+ >
55
+ <h4 style={{ marginTop: 0, display: 'flex', alignItems: 'center', gap: '8px' }}>
56
+ {isExpanded(index) ? '▼' : '▶'} {currency.DisplayName}
57
+ </h4>
58
+ <div style={{ display: 'flex', gap: '10px', justifyContent: 'center', marginBottom: '10px' }}>
59
+ <span className="badge" style={{ borderColor: (currency.Rarity?.Color as string) || '#ccc' }}>{currency.Rarity?.DisplayName}</span>
60
+ {currency.Tradable && <span className="badge">Tradable</span>}
61
+ <span className="badge">Max: {currency.MaxAmount}</span>
62
+ </div>
63
+ </div>
64
+
65
+ {isExpanded(index) && (
66
+ <>
67
+ <p style={{ fontStyle: 'italic', fontSize: '0.9em', color: '#666' }}>{currency.Desc}</p>
68
+
69
+ <h5 style={{ marginTop: '15px', borderBottom: '1px solid #e0e0e0' }}>Bag Tiers</h5>
70
+ <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(150px, 1fr))", gap: "10px", marginTop: '10px' }}>
71
+ {currency.BagTiers.map((bagTier, bagIndex) => (
72
+ <div key={bagIndex}>
73
+ <ItemCard
74
+ id={`bag-tier-${bagIndex}`}
75
+ amount={bagTier.value}
76
+ label={`Bag Tier ${bagIndex + 1}`}
77
+ itemData={{
78
+ icon: bagTier.image,
79
+ rarity: currency.Rarity,
80
+ name: `Bag Tier ${bagIndex + 1}`
81
+ }}
82
+ rarityColor={(currency.Rarity?.Color as string) || null}
83
+ />
84
+ </div>
85
+ ))}
86
+ </div>
87
+
88
+ <h5 style={{ marginTop: '15px', borderBottom: '1px solid #e0e0e0' }}>Tiers</h5>
89
+ <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(150px, 1fr))", gap: "10px", marginTop: '10px' }}>
90
+ {currency.Tiers.map((tier, tierIndex) => (
91
+ <div key={tierIndex}>
92
+ <ItemCard
93
+ id={tier.tierName}
94
+ amount={tier.value}
95
+ label={tier.tierName}
96
+ itemData={{
97
+ icon: tier.orbImage,
98
+ rarity: currency.Rarity,
99
+ name: tier.tierName
100
+ }}
101
+ rarityColor={(currency.Rarity?.Color as string) || null}
102
+ />
103
+ <div style={{ marginTop: '5px', fontSize: '0.8em', color: '#666', textAlign: 'center' }}>
104
+ Order: {tier.Order}
105
+ </div>
106
+ </div>
107
+ ))}
108
+ </div>
109
+ </>
110
+ )}
111
+ </div>
112
+ ))}
113
+ </div>
114
+ </div>
71
115
  );
72
116
  };
73
117
 
@@ -1,30 +1,32 @@
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 WateringCanComponent: React.FC<{
7
- configData?: CollectionConfigData<"WateringCans">;
6
+ configData: CollectionConfigData<"WateringCans">;
8
7
  }> = ({ configData }) => {
9
8
  return (
10
- <GenericFetchComponent<CollectionConfigData<"WateringCans">>
11
- collectionName="WateringCans"
12
- configData={configData}
13
- render={(data) => (
14
- <div>
15
- <h2>Watering Can</h2>
16
- <h3>{data.DisplayName}</h3>
17
- {data.Icon && (
18
- <ImageComponent src={data.Icon} alt={`${data.DisplayName} Icon`} />
19
- )}
20
- <p>Associated Item ID: {data.AssociatedItemID}</p>
21
- <p>Plant Time Multiplier: {data.PlantTimeMultiplier}</p>
22
- <p>
23
- Plant Time Multiplier Duration: {data.PlantTimeMultiplierDuration}
24
- </p>
25
- </div>
26
- )}
27
- />
9
+ <div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
10
+
11
+ <div style={{ maxWidth: '300px', margin: '0 auto 15px auto' }}>
12
+ <ItemCard
13
+ id={configData.DisplayName}
14
+ amount={1}
15
+ label={configData.DisplayName}
16
+ itemData={{
17
+ icon: configData.Icon,
18
+ rarity: undefined,
19
+ name: configData.DisplayName
20
+ }}
21
+ rarityColor={null}
22
+ />
23
+ </div>
24
+
25
+ <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '10px', fontSize: '0.9em', color: '#666', textAlign: 'center' }}>
26
+ <p><strong>Time Mult:</strong> {configData.PlantTimeMultiplier}x</p>
27
+ <p><strong>Duration:</strong> {configData.PlantTimeMultiplierDuration}</p>
28
+ </div>
29
+ </div>
28
30
  );
29
31
  };
30
32