ps99-api 2.3.3 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/.github/workflows/release-on-main.yml +1 -2
  2. package/.idea/node-ps99-api.iml +1 -0
  3. package/.idea/runConfigurations/test_changing.xml +1 -1
  4. package/README.md +1 -1
  5. package/debug_currency.json +57 -0
  6. package/debug_goals.json +271 -0
  7. package/dist/ps99-api.d.ts +2 -0
  8. package/dist/ps99-api.js +4 -1
  9. package/dist/ps99-api.js.map +1 -1
  10. package/dist/request-client/axios.js +6 -1
  11. package/dist/request-client/axios.js.map +1 -1
  12. package/dist/responses/collection/achievement.d.ts +2 -0
  13. package/dist/responses/collection/guild-battle.d.ts +2 -4
  14. package/dist/responses/collection/index.d.ts +1 -0
  15. package/dist/responses/collection/index.js +15 -0
  16. package/dist/responses/collection/index.js.map +1 -1
  17. package/dist/responses/collection/rank.d.ts +1 -0
  18. package/dist/responses/collection/rarity.d.ts +1 -0
  19. package/dist/responses/collection/seed.d.ts +1 -0
  20. package/dist/responses/exists.d.ts +2 -0
  21. package/example-web/react/package-lock.json +1504 -1470
  22. package/example-web/react2/package-lock.json +3082 -2759
  23. package/example-web/react2/package.json +6 -1
  24. package/example-web/react2/public/assets/gold_variant_icon.png +0 -0
  25. package/example-web/react2/public/assets/hot_cocoa_egg.png +0 -0
  26. package/example-web/react2/public/index.html +34 -31
  27. package/example-web/react2/src/App.tsx +6 -9
  28. package/example-web/react2/src/assets/guild_placeholder.png +0 -0
  29. package/example-web/react2/src/components/AchievementsComponent.tsx +78 -30
  30. package/example-web/react2/src/components/BoostsComponent.tsx +18 -14
  31. package/example-web/react2/src/components/BoothsComponent.tsx +24 -22
  32. package/example-web/react2/src/components/BoxesComponent.tsx +46 -21
  33. package/example-web/react2/src/components/BuffsComponent.tsx +83 -13
  34. package/example-web/react2/src/components/CharmsComponent.tsx +47 -29
  35. package/example-web/react2/src/components/CollectionConfigIndex.tsx +398 -35
  36. package/example-web/react2/src/components/CollectionsIndex.tsx +132 -23
  37. package/example-web/react2/src/components/CollectionsLayout.tsx +50 -0
  38. package/example-web/react2/src/components/CurrencyComponent.tsx +59 -50
  39. package/example-web/react2/src/components/DynamicCollectionConfigData.tsx +178 -11
  40. package/example-web/react2/src/components/EggsComponent.tsx +77 -44
  41. package/example-web/react2/src/components/EnchantsComponent.tsx +84 -34
  42. package/example-web/react2/src/components/FishingRodsComponent.tsx +38 -31
  43. package/example-web/react2/src/components/Footer.tsx +75 -18
  44. package/example-web/react2/src/components/FruitsComponent.tsx +41 -25
  45. package/example-web/react2/src/components/GenericFetchComponent.tsx +40 -22
  46. package/example-web/react2/src/components/GuildBattlesComponent.tsx +93 -65
  47. package/example-web/react2/src/components/Header.tsx +5 -37
  48. package/example-web/react2/src/components/HomePage.tsx +16 -16
  49. package/example-web/react2/src/components/HoverboardsComponent.tsx +25 -37
  50. package/example-web/react2/src/components/ImageComponent.tsx +255 -45
  51. package/example-web/react2/src/components/ItemCard.tsx +240 -0
  52. package/example-web/react2/src/components/LootboxesComponent.tsx +24 -16
  53. package/example-web/react2/src/components/MasteryComponent.tsx +93 -37
  54. package/example-web/react2/src/components/MerchantsComponent.tsx +46 -28
  55. package/example-web/react2/src/components/MiscItemsComponent.tsx +28 -26
  56. package/example-web/react2/src/components/PetsComponent.tsx +115 -46
  57. package/example-web/react2/src/components/PotionsComponent.tsx +53 -36
  58. package/example-web/react2/src/components/RandomEventsComponent.tsx +39 -35
  59. package/example-web/react2/src/components/RanksComponent.tsx +187 -71
  60. package/example-web/react2/src/components/RarityComponent.tsx +124 -13
  61. package/example-web/react2/src/components/RebirthsComponent.tsx +37 -26
  62. package/example-web/react2/src/components/SecretRoomsComponent.tsx +7 -13
  63. package/example-web/react2/src/components/SeedsComponent.tsx +45 -32
  64. package/example-web/react2/src/components/ShovelsComponent.tsx +23 -18
  65. package/example-web/react2/src/components/Sidebar.tsx +105 -0
  66. package/example-web/react2/src/components/SprinklersComponent.tsx +27 -19
  67. package/example-web/react2/src/components/Tooltip.tsx +36 -0
  68. package/example-web/react2/src/components/UltimatesComponent.tsx +29 -24
  69. package/example-web/react2/src/components/UpgradesComponent.tsx +99 -55
  70. package/example-web/react2/src/components/WateringCansComponent.tsx +23 -21
  71. package/example-web/react2/src/components/WorldsComponent.tsx +27 -24
  72. package/example-web/react2/src/components/XPPotionsComponent.tsx +29 -19
  73. package/example-web/react2/src/components/ZoneFlagsComponent.tsx +27 -23
  74. package/example-web/react2/src/components/ZonesComponent.tsx +56 -73
  75. package/example-web/react2/src/constants/collectionIcons.ts +29 -0
  76. package/example-web/react2/src/context/CollectionDataContext.tsx +62 -0
  77. package/example-web/react2/src/hooks/useExpandableList.ts +38 -0
  78. package/example-web/react2/src/hooks/useItemResolution.ts +351 -0
  79. package/example-web/react2/src/index.css +257 -0
  80. package/example-web/react2/src/index.tsx +2 -1
  81. package/example-web/react2/temp_model.rbxm +0 -0
  82. package/example-web/react2/webpack.config.js +103 -47
  83. package/package.json +11 -11
  84. package/ranks.json +1 -0
  85. package/repro_collection_fetch.ts +33 -0
  86. package/repro_image_fetch.ts +50 -0
  87. package/src/__tests__/__snapshots__/ps99-api-changes.ts.snap +34841 -10439
  88. package/src/__tests__/__snapshots__/ps99-api-live.ts.snap +160667 -67217
  89. package/src/ps99-api.ts +9 -5
  90. package/src/request-client/axios.ts +6 -2
  91. package/src/responses/collection/achievement.ts +2 -0
  92. package/src/responses/collection/guild-battle.ts +2 -4
  93. package/src/responses/collection/index.ts +1 -0
  94. package/src/responses/collection/rank.ts +1 -0
  95. package/src/responses/collection/rarity.ts +1 -0
  96. package/src/responses/collection/seed.ts +1 -0
  97. package/src/responses/exists.ts +2 -0
  98. package/tsconfig.json +1 -1
  99. package/example-web/react2/public/service-worker.js +0 -63
@@ -1,55 +1,111 @@
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 { useExpandableList } from "../hooks/useExpandableList";
5
5
 
6
6
  const MasteryComponent: React.FC<{
7
- configData?: CollectionConfigData<"Mastery">;
7
+ configData: CollectionConfigData<"Mastery">;
8
8
  }> = ({ configData }) => {
9
- const renderPerks = (perks: any) => {
10
- return Object.entries(perks).map(
11
- ([perkType, perkDetails]: [string, any]) => (
12
- <div key={perkType}>
13
- <h3>{perkType}</h3>
14
- {perkDetails.map((detail: any, index: number) => (
15
- <div key={index}>
16
- <p>Level: {detail.Level}</p>
17
- <p>Title: {detail.Title}</p>
18
- <p>Description: {detail.Text}</p>
19
- {detail.Power && <p>Power: {detail.Power}</p>}
9
+ const perksEntries = React.useMemo(() => configData.Perks ? Object.entries(configData.Perks) : [], [configData.Perks]);
10
+ const { expandedIndices, toggle, expandAll, collapseAll, isExpanded } = useExpandableList(perksEntries.length);
11
+
12
+ const renderPerks = () => {
13
+ return perksEntries.map(
14
+ ([perkType, perkDetails]: [string, any], index: number) => (
15
+ <div key={perkType} style={{ marginBottom: '15px', textAlign: 'left', background: '#f9f9f9', padding: '10px', borderRadius: '8px' }}>
16
+ <h4
17
+ onClick={() => toggle(index)}
18
+ style={{ margin: '0 0 10px 0', color: '#333', cursor: 'pointer', display: 'flex', alignItems: 'center', gap: '8px' }}
19
+ >
20
+ {isExpanded(index) ? '▼' : '▶'} {perkType}
21
+ </h4>
22
+ {isExpanded(index) && (
23
+ <div>
24
+ {perkDetails.map((detail: any, idx: number) => (
25
+ <div key={idx} style={{ marginBottom: '8px', paddingLeft: '10px', borderLeft: '2px solid #ddd' }}>
26
+ <div style={{ fontWeight: 'bold', fontSize: '0.9em' }}>Lvl {detail.Level}: {detail.Title}</div>
27
+ <div style={{ fontSize: '0.9em', color: '#666' }}>{detail.Text}</div>
28
+ {detail.Power && <div style={{ fontSize: '0.8em', color: '#1976d2' }}>Power: {detail.Power}</div>}
29
+ </div>
30
+ ))}
20
31
  </div>
21
- ))}
32
+ )}
22
33
  </div>
23
34
  ),
24
35
  );
25
36
  };
26
37
 
27
38
  return (
28
- <GenericFetchComponent<CollectionConfigData<"Mastery">>
29
- collectionName="Mastery"
30
- configData={configData}
31
- render={(data) => (
32
- <div>
33
- <h2>{data.Name}</h2>
34
- <ImageComponent src={data.Icon} alt={data.Name} />
35
- <p>Description: {data.Desc}</p>
36
- {data.ToggleablePerks && (
37
- <div>
38
- <h3>Toggleable Perks</h3>
39
- {Object.entries(data.ToggleablePerks).map(([perk, value]) => (
40
- <p key={perk}>
41
- {perk}: {value ? "Enabled" : "Disabled"}
42
- </p>
43
- ))}
39
+ <div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
40
+
41
+ <div style={{
42
+ display: 'grid',
43
+ gridTemplateColumns: 'minmax(300px, 1fr) 2fr',
44
+ gap: '40px',
45
+ height: '100%',
46
+ alignItems: 'start'
47
+ }}>
48
+
49
+ {/* Left Column: Icon */}
50
+ <div style={{
51
+ display: 'flex',
52
+ flexDirection: 'column',
53
+ alignItems: 'center',
54
+ gap: '20px',
55
+ background: '#f9f9f9',
56
+ padding: '30px',
57
+ borderRadius: '24px',
58
+ border: '2px solid #eee'
59
+ }}>
60
+ <div style={{ width: '100%', maxWidth: '300px' }}>
61
+ <ItemCard
62
+ id={configData.Name}
63
+ amount={1}
64
+ label={configData.Name}
65
+ itemData={{
66
+ icon: configData.Icon,
67
+ rarity: undefined,
68
+ name: configData.Name
69
+ }}
70
+ rarityColor={null}
71
+ />
72
+ </div>
73
+ <p style={{ fontStyle: 'italic', textAlign: 'center', color: '#666' }}>{configData.Desc}</p>
74
+ </div>
75
+
76
+ {/* Right Column: Perks */}
77
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '20px' }}>
78
+
79
+ {configData.ToggleablePerks && (
80
+ <div style={{ background: '#fff', padding: '20px', borderRadius: '16px', border: '1px solid #eee', boxShadow: '0 2px 4px rgba(0,0,0,0.02)' }}>
81
+ <h3 style={{ fontSize: '1.1em', marginTop: 0, borderBottom: '1px solid #eee', paddingBottom: '10px' }}>Toggleable Perks</h3>
82
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px' }}>
83
+ {Object.entries(configData.ToggleablePerks).map(([perk, value]) => (
84
+ <span key={perk} className="badge" style={{ background: value ? '#e8f5e9' : '#ffebee', color: value ? '#2e7d32' : '#c62828', padding: '8px 12px', fontSize: '0.9rem' }}>
85
+ {perk}: {value ? "Enabled" : "Disabled"}
86
+ </span>
87
+ ))}
88
+ </div>
44
89
  </div>
45
90
  )}
46
- <div>
47
- <h3>Perks</h3>
48
- {renderPerks(data.Perks)}
49
- </div>
91
+
92
+ {configData.Perks && (
93
+ <div style={{ background: '#fff' }}>
94
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '15px' }}>
95
+ <h3 style={{ fontSize: '1.4em', margin: 0 }}>Mastery Perks</h3>
96
+ <div style={{ display: 'flex', gap: '10px' }}>
97
+ <button onClick={expandAll} style={{ padding: '6px 12px', borderRadius: '6px', border: '1px solid #ddd', background: '#fff', cursor: 'pointer', fontSize: '0.9rem' }}>Expand All</button>
98
+ <button onClick={collapseAll} style={{ padding: '6px 12px', borderRadius: '6px', border: '1px solid #ddd', background: '#fff', cursor: 'pointer', fontSize: '0.9rem' }}>Collapse All</button>
99
+ </div>
100
+ </div>
101
+ {renderPerks()}
102
+ </div>
103
+ )}
104
+
50
105
  </div>
51
- )}
52
- />
106
+
107
+ </div>
108
+ </div>
53
109
  );
54
110
  };
55
111
 
@@ -1,45 +1,63 @@
1
1
  import React from "react";
2
2
  import { CollectionConfigData } from "ps99-api";
3
- import { GenericFetchComponent } from "./GenericFetchComponent";
4
3
 
5
- const MerchantComponent: React.FC<{
6
- configData?: CollectionConfigData<"Merchants">;
4
+ const MerchantsComponent: React.FC<{
5
+ configData: CollectionConfigData<"Merchants">;
7
6
  }> = ({ configData }) => {
8
7
  const renderStockRange = (stockRange: number[][] | undefined) => {
9
8
  if (!stockRange) return null;
10
9
  return stockRange.map((range, index) => (
11
- <div key={index}>
12
- <p>
13
- Level {index + 1}: {range[0]} - {range[1]}
14
- </p>
10
+ <div key={index} style={{ background: '#e1f5fe', padding: '8px', borderRadius: '4px', textAlign: 'center' }}>
11
+ <strong>Lvl {index + 1}</strong>
12
+ <div>{range[0]} - {range[1]} items</div>
15
13
  </div>
16
14
  ));
17
15
  };
18
16
 
19
17
  return (
20
- <GenericFetchComponent<CollectionConfigData<"Merchants">>
21
- collectionName="Merchants"
22
- configData={configData}
23
- render={(data) => (
24
- <div>
25
- <h2>{data.DisplayName}</h2>
26
- <p>Price Multiplier: {data.PriceMult}</p>
27
- <p>Machine Name: {data.MachineName}</p>
28
- <p>Refresh Rate: {data.RefreshRate} seconds</p>
29
- {data.HideNotification && <p>Notification: Hidden</p>}
30
- {data.HideRespect && <p>Respect: Hidden</p>}
31
- {data.IsStatic && <p>Static Merchant</p>}
32
- {renderStockRange(data.StockRangeByRespectLevel)}
33
- {data.SlotRespectLevels && (
34
- <div>
35
- <h3>Slot Respect Levels</h3>
36
- <p>{data.SlotRespectLevels.join(", ")}</p>
37
- </div>
38
- )}
18
+ <div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
19
+ <div style={{ textAlign: 'left', width: '100%', marginBottom: '15px' }}>
20
+ <p><strong>Machine:</strong> {configData.MachineName}</p>
21
+ <p><strong>Price Mult:</strong> {configData.PriceMult}x</p>
22
+ <p><strong>Refresh:</strong> {configData.RefreshRate}s</p>
23
+ {configData.IsStatic && <p><strong>Type:</strong> Static Merchant</p>}
24
+ </div>
25
+
26
+ {configData.StockRangeByRespectLevel && (
27
+ <div style={{ width: '100%', marginBottom: '15px' }}>
28
+ <h4>Stock Ranges</h4>
29
+ <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(120px, 1fr))', gap: '10px' }}>
30
+ {renderStockRange(configData.StockRangeByRespectLevel)}
31
+ </div>
32
+ </div>
33
+ )}
34
+
35
+ {configData.SlotRespectLevels && (
36
+ <div style={{ width: '100%' }}>
37
+ <h4>Slot Respect Levels</h4>
38
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px', justifyContent: 'center' }}>
39
+ {configData.SlotRespectLevels.map((level, idx) => (
40
+ <span key={idx} style={{
41
+ background: '#fff9c4',
42
+ color: '#fbc02d',
43
+ padding: '5px 10px',
44
+ borderRadius: '50%',
45
+ width: '30px',
46
+ height: '30px',
47
+ display: 'flex',
48
+ alignItems: 'center',
49
+ justifyContent: 'center',
50
+ fontWeight: 'bold',
51
+ border: '2px solid #fbc02d'
52
+ }}>
53
+ {level}
54
+ </span>
55
+ ))}
56
+ </div>
39
57
  </div>
40
58
  )}
41
- />
59
+ </div>
42
60
  );
43
61
  };
44
62
 
45
- export default MerchantComponent;
63
+ export default MerchantsComponent;
@@ -1,36 +1,38 @@
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 MiscItemsComponent: React.FC<{
7
- configData?: CollectionConfigData<"MiscItems">;
7
+ configData: CollectionConfigData<"MiscItems">;
8
8
  }> = ({ configData }) => {
9
+ const { getRarityColor } = useItemResolution();
10
+ const rarityColor = configData.Rarity ? getRarityColor(configData.Rarity) : null;
11
+
9
12
  return (
10
- <GenericFetchComponent<CollectionConfigData<"MiscItems">>
11
- collectionName="MiscItems"
12
- configData={configData}
13
- render={(data) => (
14
- <div>
15
- <h2>{data.DisplayName}</h2>
16
- <p>Category: {data.Category}</p>
17
- <ImageComponent src={data.Icon} alt={data.DisplayName} />
18
- {data.AltIcon && (
19
- <ImageComponent
20
- src={data.AltIcon}
21
- alt={`${data.DisplayName} (Alternate)`}
22
- />
23
- )}
24
- <p>Description: {data.Desc}</p>
25
- <p>
26
- Rarity: {data.Rarity.DisplayName} (Rarity Number:{" "}
27
- {data.Rarity.RarityNumber})
28
- </p>
29
- {data.Tradable && <p>Tradable: Yes</p>}
30
- {!data.Tradable && <p>Tradable: No</p>}
13
+ <div style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: '30px' }}>
14
+
15
+ <div style={{ width: '100%', maxWidth: '400px' }}>
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: '1.1em', color: '#555', textAlign: 'center', maxWidth: '600px', lineHeight: '1.6' }}>
30
+ <p style={{ marginBottom: '20px', background: '#fff', padding: '20px', borderRadius: '12px', border: '1px solid #eee' }}>"{configData.Desc}"</p>
31
+ <div style={{ display: 'flex', gap: '10px', justifyContent: 'center' }}>
32
+ {configData.Tradable && <span className="badge" style={{ background: '#9c27b0', color: 'white', padding: '8px 16px', borderRadius: '20px', fontWeight: 'bold' }}>Tradable</span>}
31
33
  </div>
32
- )}
33
- />
34
+ </div>
35
+ </div>
34
36
  );
35
37
  };
36
38
 
@@ -1,62 +1,131 @@
1
1
  import React from "react";
2
2
  import { CollectionConfigData } from "ps99-api";
3
- import { GenericFetchComponent } from "./GenericFetchComponent";
4
3
  import ImageComponent from "./ImageComponent";
4
+ import ItemCard from "./ItemCard";
5
+ import { useItemResolution } from "../hooks/useItemResolution";
5
6
 
6
7
  const PetsComponent: React.FC<{
7
- configData?: CollectionConfigData<"Pets">;
8
- }> = ({ configData }) => {
8
+ configData: CollectionConfigData<"Pets">;
9
+ displayType?: "all" | "specific";
10
+ pt?: number;
11
+ }> = ({ configData, displayType = "all", pt }) => {
12
+ const { getRarityColor } = useItemResolution();
13
+ const rarityColor = (configData as any).rarity ? getRarityColor((configData as any).rarity) : null;
14
+
15
+ const getVariationName = () => {
16
+ if (pt === 1) return "(Golden)";
17
+ if (pt === 2) return "(Rainbow)";
18
+ return "";
19
+ };
20
+
9
21
  return (
10
- <GenericFetchComponent<CollectionConfigData<"Pets">>
11
- collectionName="Pets"
12
- configData={configData}
13
- render={(data) => (
14
- <div>
15
- <h2>{data.name}</h2>
16
- <ImageComponent src={data.thumbnail} alt={data.name} />
17
- {data.goldenThumbnail && (
18
- <ImageComponent
19
- src={data.goldenThumbnail}
20
- alt={`${data.name} (Golden)`}
21
- />
22
+ <div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
23
+ {/* 2-Column Grid Layout */}
24
+ <div style={{
25
+ display: 'grid',
26
+ gridTemplateColumns: 'minmax(300px, 1fr) 2fr', // Left (Image) : Right (Details)
27
+ gap: '40px',
28
+ height: '100%',
29
+ alignItems: 'start'
30
+ }}>
31
+
32
+ {/* Left Column: Images */}
33
+ <div style={{
34
+ display: 'flex',
35
+ flexDirection: 'column',
36
+ alignItems: 'center',
37
+ gap: '20px'
38
+ }}>
39
+ {/* Main Image */}
40
+ <div style={{ width: '250px', height: '250px' }}>
41
+ <ImageComponent src={configData.thumbnail} alt={configData.name} />
42
+ </div>
43
+ {/* Golden Variant if exists */}
44
+ {configData.goldenThumbnail && (
45
+ <div style={{ width: '150px', height: '150px', opacity: 0.8 }}>
46
+ <p style={{ textAlign: 'center', margin: '0 0 5px 0', fontSize: '0.9rem', fontWeight: 'bold', color: '#888' }}>Golden Variant</p>
47
+ <ImageComponent
48
+ src={configData.goldenThumbnail}
49
+ alt={`${configData.name} (Golden)`}
50
+ />
51
+ </div>
22
52
  )}
23
- <p>From World Number: {data.fromWorldNumber}</p>
24
- <p>From Zone Number: {data.fromZoneNumber}</p>
25
- {data.indexObtainable && <p>Index Obtainable: Yes</p>}
26
- {data.huge && <p>Huge: Yes</p>}
27
- {data.fly && <p>Can Fly: Yes</p>}
28
- {data.tradable && <p>Tradable: Yes</p>}
29
- {data.secret && <p>Secret: Yes</p>}
30
- {data.hidden && <p>Hidden: Yes</p>}
31
- {data.cachedPower && (
32
- <div>
33
- <h3>Cached Power:</h3>
34
- <ul>
35
- {data.cachedPower.map((power, index) => (
36
- <li key={index}>{power}</li>
37
- ))}
38
- </ul>
53
+ </div>
54
+
55
+ {/* Right Column: details */}
56
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '20px' }}>
57
+
58
+ {/* Stats Grid */}
59
+ <div style={{
60
+ display: 'grid',
61
+ gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))',
62
+ gap: '20px',
63
+ width: '100%'
64
+ }}>
65
+ <div style={{ background: '#f5f5f5', padding: '15px', borderRadius: '12px', border: '1px solid #ddd' }}>
66
+ <strong style={{ display: 'block', fontSize: '0.9rem', color: '#666', marginBottom: '5px' }}>FROM WORLD</strong>
67
+ <span style={{ fontSize: '1.5rem', fontWeight: 'bold', color: '#333' }}>{configData.fromWorldNumber || "?"}</span>
68
+ </div>
69
+ <div style={{ background: '#f5f5f5', padding: '15px', borderRadius: '12px', border: '1px solid #ddd' }}>
70
+ <strong style={{ display: 'block', fontSize: '0.9rem', color: '#666', marginBottom: '5px' }}>FROM ZONE</strong>
71
+ <span style={{ fontSize: '1.5rem', fontWeight: 'bold', color: '#333' }}>{configData.fromZoneNumber || "?"}</span>
72
+ </div>
73
+ {configData.power && (
74
+ <div style={{ background: '#e3f2fd', padding: '15px', borderRadius: '12px', border: '1px solid #90caf9' }}>
75
+ <strong style={{ display: 'block', fontSize: '0.9rem', color: '#1565c0', marginBottom: '5px' }}>POWER</strong>
76
+ <span style={{ fontSize: '1.5rem', fontWeight: 'bold', color: '#1565c0' }}>{configData.power}</span>
77
+ </div>
78
+ )}
79
+ {configData.exclusiveLevel && (
80
+ <div style={{ background: '#f3e5f5', padding: '15px', borderRadius: '12px', border: '1px solid #ce93d8' }}>
81
+ <strong style={{ display: 'block', fontSize: '0.9rem', color: '#7b1fa2', marginBottom: '5px' }}>EXCLUSIVE LEVEL</strong>
82
+ <span style={{ fontSize: '1.5rem', fontWeight: 'bold', color: '#7b1fa2' }}>{configData.exclusiveLevel}</span>
83
+ </div>
84
+ )}
85
+ </div>
86
+
87
+ {/* Badges */}
88
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '10px' }}>
89
+ {configData.indexObtainable && <span className="badge" style={{ background: '#4caf50', color: 'white', padding: '8px 16px', borderRadius: '20px', fontWeight: 'bold' }}>Index Obtainable</span>}
90
+ {configData.huge && <span className="badge" style={{ background: '#ff9800', color: 'white', padding: '8px 16px', borderRadius: '20px', fontWeight: 'bold' }}>HUGE</span>}
91
+ {configData.fly && <span className="badge" style={{ background: '#2196f3', color: 'white', padding: '8px 16px', borderRadius: '20px', fontWeight: 'bold' }}>Fly</span>}
92
+ {configData.tradable && <span className="badge" style={{ background: '#9c27b0', color: 'white', padding: '8px 16px', borderRadius: '20px', fontWeight: 'bold' }}>Tradable</span>}
93
+ {configData.secret && <span className="badge" style={{ background: '#607d8b', color: 'white', padding: '8px 16px', borderRadius: '20px', fontWeight: 'bold' }}>Secret</span>}
94
+ {configData.hidden && <span className="badge" style={{ background: '#333', color: 'white', padding: '8px 16px', borderRadius: '20px', fontWeight: 'bold' }}>Hidden</span>}
95
+ </div>
96
+
97
+ {/* Description / Index Desc */}
98
+ {configData.indexDesc && (
99
+ <div style={{
100
+ marginTop: '10px',
101
+ padding: '20px',
102
+ background: '#fff3e0',
103
+ borderLeft: '5px solid #ff9800',
104
+ borderRadius: '4px',
105
+ fontStyle: 'italic',
106
+ fontSize: '1.1rem',
107
+ lineHeight: '1.6',
108
+ color: '#5d4037'
109
+ }}>
110
+ "{configData.indexDesc}"
39
111
  </div>
40
112
  )}
41
- {data.animations && (
42
- <div>
43
- <h3>Animations:</h3>
44
- <ul>
45
- {Object.entries(data.animations).map(([key, value], index) => (
46
- <li key={index}>
47
- {key}: {JSON.stringify(value)}
48
- </li>
49
- ))}
50
- </ul>
113
+
114
+ {/* Cached Power (Debug/Extra info) */}
115
+ {configData.cachedPower && (
116
+ <div style={{ marginTop: 'auto', paddingTop: '20px', borderTop: '1px solid #eee' }}>
117
+ <h4 style={{ margin: '0 0 10px 0', color: '#999', fontSize: '0.8rem', textTransform: 'uppercase' }}>Technical Data</h4>
118
+ <div style={{ fontSize: '0.86rem', color: '#aaa', fontFamily: 'monospace' }}>
119
+ Cached Power: {configData.cachedPower.join(', ')}
120
+ </div>
51
121
  </div>
52
122
  )}
53
- {data.indexDesc && <p>Description: {data.indexDesc}</p>}
54
- {data.exclusiveLevel && <p>Exclusive Level: {data.exclusiveLevel}</p>}
55
- {data.power && <p>Power: {data.power}</p>}
123
+
56
124
  </div>
57
- )}
58
- />
125
+ </div>
126
+ </div>
59
127
  );
60
128
  };
61
129
 
130
+
62
131
  export default PetsComponent;
@@ -1,48 +1,65 @@
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 PotionsComponent: React.FC<{
7
- configData?: CollectionConfigData<"Potions">;
8
+ configData: CollectionConfigData<"Potions">;
8
9
  }> = ({ configData }) => {
10
+ const { getRarityColor } = useItemResolution();
11
+
9
12
  return (
10
- <GenericFetchComponent<CollectionConfigData<"Potions">>
11
- collectionName="Potions"
12
- configData={configData}
13
- render={(data) => (
14
- <div>
15
- <h2>Potion: {data.Tiers[0].DisplayName}</h2>
16
- <div>
17
- <ImageComponent
18
- src={data.Tiers[0].Icon}
19
- alt={data.Tiers[0].DisplayName}
20
- />
21
- <p>Description: {data.Tiers[0].Desc}</p>
22
- <p>Primary Color: {data.PrimaryColor}</p>
23
- <p>Secondary Color: {data.SecondaryColor}</p>
24
- <p>Base Tier: {data.BaseTier}</p>
25
- <p>Max Tier: {data.MaxTier}</p>
26
- </div>
27
- <div>
28
- <h3>Tiers:</h3>
29
- <ul>
30
- {data.Tiers.map((tier, index) => (
31
- <li key={index}>
32
- <p>Tier {index + 1}:</p>
33
- <p>Display Name: {tier.DisplayName}</p>
34
- <p>Description: {tier.Desc}</p>
35
- <ImageComponent src={tier.Icon} alt={tier.DisplayName} />
36
- <p>Power: {tier.Power}</p>
37
- <p>Time: {tier.Time}</p>
38
- <p>Rarity: {tier.Rarity.DisplayName}</p>
39
- </li>
40
- ))}
41
- </ul>
13
+
14
+ <div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
15
+
16
+ <div style={{ display: 'flex', gap: '20px', alignItems: 'center', marginBottom: '20px', flexWrap: 'wrap', justifyContent: 'center' }}>
17
+ <div style={{ width: '150px', height: '150px' }}>
18
+ <ImageComponent
19
+ src={configData.Tiers[0].Icon}
20
+ alt={configData.Tiers[0].DisplayName}
21
+ />
22
+ </div>
23
+ <div style={{ textAlign: 'left', flex: 1, minWidth: '200px' }}>
24
+ <p style={{ marginBottom: '8px' }}><strong>Description:</strong> {configData.Tiers[0].Desc}</p>
25
+ <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '8px' }}>
26
+ <p><strong>Primary:</strong> <span style={{ color: configData.PrimaryColor }}>{configData.PrimaryColor}</span></p>
27
+ <p><strong>Secondary:</strong> <span style={{ color: configData.SecondaryColor }}>{configData.SecondaryColor}</span></p>
28
+ <p><strong>Base Tier:</strong> {configData.BaseTier}</p>
29
+ <p><strong>Max Tier:</strong> {configData.MaxTier}</p>
42
30
  </div>
43
31
  </div>
44
- )}
45
- />
32
+ </div>
33
+
34
+ <div style={{ marginTop: '20px' }}>
35
+ <h3>Tiers</h3>
36
+ <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(220px, 1fr))', gap: '15px' }}>
37
+ {configData.Tiers.map((tier, index) => {
38
+ const rarityColor = tier.Rarity ? getRarityColor(tier.Rarity) : null;
39
+ return (
40
+ <div key={index} style={{ marginBottom: '10px' }}>
41
+ <ItemCard
42
+ id={tier.DisplayName}
43
+ amount={1}
44
+ label={tier.DisplayName}
45
+ tn={index + 1}
46
+ itemData={{
47
+ icon: tier.Icon,
48
+ rarity: tier.Rarity,
49
+ name: tier.DisplayName
50
+ }}
51
+ rarityColor={rarityColor}
52
+ />
53
+ <div style={{ marginTop: '10px', fontSize: '0.9em', color: '#666', textAlign: 'center' }}>
54
+ <p style={{ fontWeight: 'bold' }}>Power: {tier.Power}</p>
55
+ <p>Time: {tier.Time}s</p>
56
+ </div>
57
+ </div>
58
+ );
59
+ })}
60
+ </div>
61
+ </div>
62
+ </div>
46
63
  );
47
64
  };
48
65