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,42 +1,49 @@
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 FishingRodsComponent: React.FC<{
7
- configData?: CollectionConfigData<"FishingRods">;
7
+ configData: CollectionConfigData<"FishingRods">;
8
8
  }> = ({ configData }) => {
9
9
  return (
10
- <GenericFetchComponent<CollectionConfigData<"FishingRods">>
11
- collectionName="FishingRods"
12
- configData={configData}
13
- render={(data) => (
14
- <div>
15
- <h2>{data.DisplayName}</h2>
16
- <ImageComponent src={data.Icon} alt={data.DisplayName} />
17
- <p>Fishing Chance: {data.FishingChance}</p>
18
- <p>Fishing Currency Multiplier: {data.FishingCurrencyMultiplier}</p>
19
- <p>Minimum Fishing Time: {data.MinFishingTime} seconds</p>
20
- <p>
21
- Fishing Game Speed Multiplier: {data.FishingGameSpeedMultiplier}
22
- </p>
23
- <p>Bar Size: {data.BarSize}</p>
24
- <p>Associated Item ID: {data.AssociatedItemID}</p>
25
- {data.MerchantSalePrice && (
26
- <p>Merchant Sale Price: {data.MerchantSalePrice}</p>
27
- )}
28
- <h3>Fishing Odds:</h3>
29
- <ul>
30
- {data.FishingOdds.map((odds, index) => (
31
- <li key={index}>
32
- {odds[0]}: {odds[1]}%
33
- </li>
34
- ))}
35
- </ul>
10
+ <div className="game-card" style={{ maxWidth: '500px', margin: '0 auto' }}>
11
+ <h2>{configData.DisplayName}</h2>
12
+
13
+ <div style={{ maxWidth: '300px', margin: '0 auto 15px auto' }}>
14
+ <ItemCard
15
+ id={configData.DisplayName}
16
+ amount={1}
17
+ label={configData.DisplayName}
18
+ itemData={{
19
+ icon: configData.Icon,
20
+ rarity: undefined,
21
+ name: configData.DisplayName
22
+ }}
23
+ rarityColor={null}
24
+ />
25
+ </div>
26
+
27
+ <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '10px', marginBottom: '20px', textAlign: 'left' }}>
28
+ <p><strong>Chance:</strong> {configData.FishingChance}%</p>
29
+ <p><strong>Currency:</strong> {configData.FishingCurrencyMultiplier}x</p>
30
+ <p><strong>Speed:</strong> {configData.FishingGameSpeedMultiplier}x</p>
31
+ <p><strong>Bar Size:</strong> {configData.BarSize}</p>
32
+ </div>
33
+
34
+ <div style={{ borderTop: '1px solid #eee', paddingTop: '15px' }}>
35
+ <h3 style={{ fontSize: '1.1em', marginBottom: '10px' }}>Fishing Odds</h3>
36
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px', justifyContent: 'center' }}>
37
+ {configData.FishingOdds.map((odds, index) => (
38
+ <span key={index} className="badge" style={{ background: '#e3f2fd', color: '#1565c0', border: '1px solid #90caf9' }}>
39
+ {odds[0]}: {odds[1]}%
40
+ </span>
41
+ ))}
36
42
  </div>
37
- )}
38
- />
43
+ </div>
44
+ </div>
39
45
  );
40
46
  };
41
47
 
48
+
42
49
  export default FishingRodsComponent;
@@ -1,12 +1,49 @@
1
- import React from "react";
1
+ import React, { useState, useEffect } from "react";
2
+ import { useOnlineStatus } from "../hooks/useOnlineStatus";
2
3
 
3
4
  const Footer: React.FC = () => {
5
+ const isOnline = useOnlineStatus();
6
+ const [lastUpdate, setLastUpdate] = useState<string | null>(null);
7
+ const [loading, setLoading] = useState(false);
8
+
9
+ const updateLastUpdate = () => {
10
+ setLastUpdate(new Date().toLocaleString());
11
+ };
12
+
13
+ useEffect(() => {
14
+ const fetchData = async () => {
15
+ setLoading(true);
16
+ // Simulate a fetch call to update lastUpdate time
17
+ await new Promise((resolve) => setTimeout(resolve, 1000));
18
+ updateLastUpdate();
19
+ setLoading(false);
20
+ };
21
+
22
+ fetchData();
23
+
24
+ window.addEventListener("online", updateLastUpdate);
25
+ window.addEventListener("offline", updateLastUpdate);
26
+
27
+ return () => {
28
+ window.removeEventListener("online", updateLastUpdate);
29
+ window.removeEventListener("offline", updateLastUpdate);
30
+ };
31
+ }, []);
32
+
4
33
  return (
5
- <footer
6
- style={{ textAlign: "center", padding: "1em", background: "#f8f9fa" }}
7
- >
8
- <div>
9
- <a href="https://badge.fury.io/js/ps99-api">
34
+ <footer className="game-footer">
35
+ <div
36
+ style={{
37
+ display: "flex",
38
+ justifyContent: "center",
39
+ alignItems: "center",
40
+ marginBottom: "1em",
41
+ }}
42
+ >
43
+ <a
44
+ href="https://badge.fury.io/js/ps99-api"
45
+ style={{ display: "flex", alignItems: "center" }}
46
+ >
10
47
  <img
11
48
  src="https://badge.fury.io/js/ps99-api.svg"
12
49
  alt="npm version"
@@ -14,25 +51,45 @@ const Footer: React.FC = () => {
14
51
  />
15
52
  </a>
16
53
  &nbsp;
17
- <a href="https://github.com/joekiller/node-ps99-api">
54
+ <a
55
+ href="https://github.com/joekiller/node-ps99-api"
56
+ style={{ display: "flex", alignItems: "center" }}
57
+ >
18
58
  <img
19
59
  src="https://img.shields.io/badge/source-github-blue?logo=github"
20
60
  alt="GitHub source"
21
61
  height="18"
22
62
  />
23
63
  </a>
64
+ &nbsp;
65
+ <div style={{ display: "flex", alignItems: "center", height: "18px" }}>
66
+ {isOnline ? (
67
+ <span style={{ color: "green", lineHeight: "18px" }}>● Online</span>
68
+ ) : (
69
+ <span style={{ color: "red", lineHeight: "18px" }}>● Offline</span>
70
+ )}
71
+ </div>
72
+ </div>
73
+ <div style={{ marginBottom: "1em" }}>
74
+ {loading ? (
75
+ <span>♻️ Loading...</span>
76
+ ) : (
77
+ <span>Last update: {lastUpdate}</span>
78
+ )}
79
+ </div>
80
+ <div>
81
+ <p>
82
+ &copy; {new Date().getFullYear()} Joseph "
83
+ <a
84
+ href="https://joekiller.com"
85
+ target="_blank"
86
+ rel="noopener noreferrer"
87
+ >
88
+ joekiller
89
+ </a>
90
+ " Lawson. All rights reserved.
91
+ </p>
24
92
  </div>
25
- <p>
26
- &copy; {new Date().getFullYear()} Joseph "
27
- <a
28
- href="https://joekiller.com"
29
- target="_blank"
30
- rel="noopener noreferrer"
31
- >
32
- joekiller
33
- </a>
34
- " Lawson. All rights reserved.
35
- </p>
36
93
  </footer>
37
94
  );
38
95
  };
@@ -1,35 +1,51 @@
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 FruitsComponent: React.FC<{
7
- configData?: CollectionConfigData<"Fruits">;
7
+ configData: CollectionConfigData<"Fruits">;
8
8
  }> = ({ configData }) => {
9
+ const { getRarityColor } = useItemResolution();
10
+ const rarityColor = configData.Rarity ? getRarityColor(configData.Rarity) : null;
11
+
9
12
  return (
10
- <GenericFetchComponent<CollectionConfigData<"Fruits">>
11
- collectionName="Fruits"
12
- configData={configData}
13
- render={(data) => (
14
- <div>
15
- <h2>{data.DisplayName}</h2>
16
- <ImageComponent src={data.Icon} alt={data.DisplayName} />
17
- <p>Duration: {data.Duration} seconds</p>
18
- <p>Rarity: {data.Rarity.DisplayName}</p>
19
- <p>Rarity Number: {data.Rarity.RarityNumber}</p>
20
- {data.Desc && <p>Description: {data.Desc}</p>}
21
- {data.IgnoreFruitMachine && <p>Ignore Fruit Machine: Yes</p>}
22
- <h3>Boosts:</h3>
23
- <ul>
24
- {data.Boost.map((boost, index) => (
25
- <li key={index}>
26
- {boost.Type}: {boost.Amount}
27
- </li>
28
- ))}
29
- </ul>
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>Duration: {configData.Duration}s</p>
27
+ {configData.Desc && <p>{configData.Desc}</p>}
28
+ {configData.IgnoreFruitMachine && <p>Ignore Fruit Machine</p>}
29
+ </div>
30
+ <div style={{ marginTop: '15px' }}>
31
+ <h3 style={{ textAlign: 'center', fontSize: '1.1em' }}>Boosts</h3>
32
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px', justifyContent: 'center' }}>
33
+ {configData.Boost.map((boost, index) => (
34
+ <span key={index} style={{
35
+ background: '#e8f5e9',
36
+ color: '#2e7d32',
37
+ padding: '5px 10px',
38
+ borderRadius: '12px',
39
+ fontSize: '0.9em',
40
+ fontWeight: 'bold',
41
+ border: '1px solid #a5d6a7'
42
+ }}>
43
+ {boost.Type}: {boost.Amount}
44
+ </span>
45
+ ))}
30
46
  </div>
31
- )}
32
- />
47
+ </div>
48
+ </div>
33
49
  );
34
50
  };
35
51
 
@@ -1,45 +1,55 @@
1
1
  import React, { useEffect, useState, ReactNode } from "react";
2
- import { useParams } from "react-router-dom";
3
2
  import { PetSimulator99API, ApiResponseBody, CollectionName } from "ps99-api";
4
3
 
5
4
  interface GenericFetchComponentProps<T> {
6
5
  collectionName: CollectionName;
6
+ configName: string;
7
7
  render: (data: T) => ReactNode;
8
- configData?: T;
9
8
  }
10
9
 
11
10
  export const GenericFetchComponent = <T,>({
12
11
  collectionName,
12
+ configName,
13
13
  render,
14
- configData,
15
14
  }: GenericFetchComponentProps<T>) => {
16
- const { configName } = useParams<{ configName: string }>();
17
- const [data, setData] = useState<T | null>(configData || null);
15
+ const [data, setData] = useState<T | null>(null);
18
16
  const [error, setError] = useState<string | null>(null);
19
17
 
20
18
  useEffect(() => {
21
- if (configData) return;
19
+ let isMounted = true;
22
20
 
23
21
  const fetchData = async () => {
24
- if (!configName) return;
25
- const api = new PetSimulator99API();
26
- const response: ApiResponseBody<any[]> =
27
- await api.getCollection(collectionName);
28
- if (response.status === "ok") {
29
- const item = response.data.find(
30
- (item) => item.configName === configName,
31
- );
32
- if (item) {
33
- setData(item.configData);
34
- } else {
35
- setError("Configuration not found");
22
+ try {
23
+ const api = new PetSimulator99API();
24
+ const response: ApiResponseBody<any[]> =
25
+ await api.getCollection(collectionName);
26
+ if (isMounted) {
27
+ if (response.status === "ok") {
28
+ const item = response.data.find(
29
+ (item) => item.configName === configName,
30
+ );
31
+ if (item) {
32
+ setData(item.configData);
33
+ } else {
34
+ setError("Configuration not found");
35
+ }
36
+ } else {
37
+ setError(response.error.message);
38
+ }
39
+ }
40
+ } catch (err) {
41
+ if (isMounted) {
42
+ setError(err.message);
36
43
  }
37
- } else {
38
- setError(response.error.message);
39
44
  }
40
45
  };
46
+
41
47
  fetchData();
42
- }, [collectionName, configName, configData]);
48
+
49
+ return () => {
50
+ isMounted = false;
51
+ };
52
+ }, [collectionName, configName]);
43
53
 
44
54
  if (error) {
45
55
  return <div>Error: {error}</div>;
@@ -49,5 +59,13 @@ export const GenericFetchComponent = <T,>({
49
59
  return <div>Loading...</div>;
50
60
  }
51
61
 
52
- return <div>{render(data)}</div>;
62
+ return (
63
+ <div style={{
64
+ width: '100%',
65
+ padding: '20px',
66
+ boxSizing: 'border-box'
67
+ }}>
68
+ {render(data)}
69
+ </div>
70
+ );
53
71
  };
@@ -1,75 +1,103 @@
1
1
  import React from "react";
2
- import { CollectionConfigData } from "ps99-api";
3
- import { GenericFetchComponent } from "./GenericFetchComponent";
2
+ import { CollectionConfigData, GuildBattlePlacementReward, GuildBattleRewardItem } from "ps99-api";
3
+ import DynamicCollectionConfigData from "./DynamicCollectionConfigData";
4
+ import { useItemResolution } from "../hooks/useItemResolution";
5
+ import ItemCard from "./ItemCard";
6
+ import ImageComponent from "./ImageComponent";
7
+ // @ts-ignore
8
+ import placeholderIcon from "../assets/guild_placeholder.png";
9
+
10
+ const rewardTypes = ["Clan Gift", "Booth", "Hoverboard", "Pet"];
4
11
 
5
12
  const GuildBattlesComponent: React.FC<{
6
- configData?: CollectionConfigData<"GuildBattles">;
13
+ configData: CollectionConfigData<"GuildBattles">;
7
14
  }> = ({ configData }) => {
15
+ const { resolveItem, getRarityColor, loading } = useItemResolution();
16
+
17
+ if (loading) return <div>Loading data...</div>;
18
+
19
+ const renderRewardItem = (reward: GuildBattlePlacementReward | GuildBattleRewardItem, index: number) => {
20
+ let id: string;
21
+ let amount = 1; // Default amount
22
+ let pt: number | undefined;
23
+
24
+ if ("Item" in reward) { // GuildBattlePlacementReward
25
+ id = reward.Item._data.id;
26
+ pt = reward.Item._data.pt;
27
+ amount = (reward.Item._data as any)._am || 1;
28
+ } else { // GuildBattleRewardItem
29
+ id = reward._data.id;
30
+ amount = (reward._data as any)._am || 1;
31
+ }
32
+
33
+ // Try to resolve the item using the hook
34
+ const itemData = resolveItem(id, pt);
35
+ const rarityColor = itemData?.rarity ? getRarityColor(itemData.rarity) : null;
36
+
37
+ return (
38
+ <ItemCard
39
+ key={index}
40
+ id={id}
41
+ amount={amount}
42
+ label={id}
43
+ tn={pt}
44
+ itemData={itemData}
45
+ rarityColor={rarityColor}
46
+ />
47
+ );
48
+ };
49
+
8
50
  return (
9
- <GenericFetchComponent<CollectionConfigData<"GuildBattles">>
10
- collectionName="GuildBattles"
11
- configData={configData}
12
- render={(data) => (
13
- <div>
14
- <h2>{data.Title}</h2>
15
- <p>Start Time: {new Date(data.StartTime * 1000).toLocaleString()}</p>
16
- <p>
17
- Finish Time: {new Date(data.FinishTime * 1000).toLocaleString()}
18
- </p>
19
- {data.HasGoals && <p>Has Goals: Yes</p>}
20
- <h3>Placement Rewards:</h3>
21
- <ul>
22
- {data.PlacementRewards?.map((reward, index) => (
23
- <li key={index}>
24
- <p>Item ID: {reward.Item._data.id}</p>
25
- {reward.Item._data.pt && <p>Points: {reward.Item._data.pt}</p>}
26
- <p>Best: {reward.Best}</p>
27
- <p>Worst: {reward.Worst}</p>
28
- </li>
29
- ))}
30
- </ul>
31
- <h3>Rewards:</h3>
32
- <div>
33
- <h4>Gold:</h4>
34
- <ul>
35
- {data.Rewards.Gold.map((reward, index) => (
36
- <li key={index}>
37
- <p>Item ID: {reward._data.id}</p>
38
- </li>
39
- ))}
40
- </ul>
41
- <h4>Silver:</h4>
42
- <ul>
43
- {data.Rewards.Silver.map((reward, index) => (
44
- <li key={index}>
45
- <p>Item ID: {reward._data.id}</p>
46
- </li>
47
- ))}
48
- </ul>
49
- <h4>Bronze:</h4>
50
- <ul>
51
- {data.Rewards.Bronze.map((reward, index) => (
52
- <li key={index}>
53
- <p>Item ID: {reward._data.id}</p>
54
- </li>
51
+ <div style={{ padding: "1em", border: "1px solid #ccc", borderRadius: "8px", marginBottom: "2em" }}>
52
+ <div style={{ display: "flex", alignItems: "center", gap: "1em", borderBottom: "2px solid #ccc", paddingBottom: "0.5em" }}>
53
+ <div style={{ width: "64px", height: "64px" }}>
54
+ <ImageComponent src={(configData as any).Icon || placeholderIcon} alt={configData.Title} />
55
+ </div>
56
+ <h2 style={{ margin: 0 }}>{configData.Title}</h2>
57
+ </div>
58
+ <p><strong>Start Time:</strong> {new Date(configData.StartTime * 1000).toLocaleString()}</p>
59
+ <p><strong>Finish Time:</strong> {new Date(configData.FinishTime * 1000).toLocaleString()}</p>
60
+ {configData.HasGoals && <p><strong>Has Goals:</strong> Yes</p>}
61
+
62
+ <h3>Placement Rewards:</h3>
63
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '1em' }}>
64
+ {configData.PlacementRewards?.map((reward, index) => (
65
+ <div key={index} style={{
66
+ flex: '1 1 300px', // Allow flex but with base width
67
+ maxWidth: '350px',
68
+ margin: '0 auto'
69
+ }}>
70
+ {renderRewardItem(reward, index)}
71
+ <div style={{ textAlign: 'center', marginTop: '10px', fontSize: '0.9em', color: '#666' }}>
72
+ <p style={{ margin: '2px 0' }}><strong>Best:</strong> {reward.Best}</p>
73
+ <p style={{ margin: '2px 0' }}><strong>Worst:</strong> {reward.Worst}</p>
74
+ </div>
75
+ </div>
76
+ ))}
77
+ </div>
78
+
79
+ <h3>Rewards:</h3>
80
+ {["Gold", "Silver", "Bronze", "Good"].map(tier => (
81
+ configData.Rewards[tier]?.length > 0 && (
82
+ <div key={tier}>
83
+ <h4>{tier}</h4>
84
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: '1em' }}>
85
+ {configData.Rewards[tier].map((reward, index) => (
86
+ <div key={index} style={{
87
+ border: '1px solid #ccc',
88
+ borderRadius: '8px',
89
+ padding: '1em',
90
+ flex: '1 1 calc(33% - 1em)',
91
+ boxSizing: 'border-box',
92
+ }}>
93
+ {renderRewardItem(reward, index)}
94
+ </div>
55
95
  ))}
56
- </ul>
57
- {data.Rewards.Good && (
58
- <div>
59
- <h4>Good:</h4>
60
- <ul>
61
- {data.Rewards.Good.map((reward, index) => (
62
- <li key={index}>
63
- <p>Item ID: {reward._data.id}</p>
64
- </li>
65
- ))}
66
- </ul>
67
- </div>
68
- )}
96
+ </div>
69
97
  </div>
70
- </div>
71
- )}
72
- />
98
+ )
99
+ ))}
100
+ </div>
73
101
  );
74
102
  };
75
103
 
@@ -6,22 +6,12 @@ const Header: React.FC = () => {
6
6
  const pathnames = location.pathname.split("/").filter((x) => x);
7
7
 
8
8
  return (
9
- <nav
10
- style={{
11
- padding: "1em",
12
- background: "#f8f9fa",
13
- borderBottom: "1px solid #e7e7e7",
14
- display: "flex",
15
- justifyContent: "space-between",
16
- alignItems: "center",
17
- }}
18
- >
9
+ <nav className="game-header">
19
10
  <div style={{ display: "flex", alignItems: "center" }}>
20
11
  <Link
21
12
  to="/"
22
13
  style={{
23
14
  textDecoration: "none",
24
- color: "#007bff",
25
15
  display: "flex",
26
16
  alignItems: "center",
27
17
  }}
@@ -29,35 +19,13 @@ const Header: React.FC = () => {
29
19
  <img
30
20
  src="/node-ps99-api/icons/icon-192x192.png"
31
21
  alt="Home"
32
- style={{ width: "40px", height: "40px", marginRight: "0.5em" }}
22
+ style={{ width: "40px", height: "40px", marginRight: "0.5em", border: "2px solid white", borderRadius: "50%" }}
33
23
  />
34
24
  </Link>
35
- <ol
36
- style={{ display: "flex", listStyle: "none", padding: 0, margin: 0 }}
37
- >
38
- <li style={{ margin: "0 0.5em" }}>
39
- <Link to="/" style={{ textDecoration: "none", color: "#007bff" }}>
40
- Home
41
- </Link>
42
- </li>
43
- {pathnames.map((value, index) => {
44
- const to = `/${pathnames.slice(0, index + 1).join("/")}`;
45
- return (
46
- <li key={to} style={{ margin: "0 0.5em" }}>
47
- <Link
48
- to={to}
49
- style={{ textDecoration: "none", color: "#007bff" }}
50
- >
51
- {decodeURIComponent(value)}
52
- </Link>
53
- </li>
54
- );
55
- })}
56
- </ol>
57
25
  </div>
58
- <h1 style={{ margin: 0 }}>
59
- <Link to="/" style={{ textDecoration: "none", color: "#007bff" }}>
60
- Pet Simulator 99 API
26
+ <h1 style={{ margin: 0, fontSize: '1.5rem', color: 'white' }}>
27
+ <Link to="/">
28
+ Pet Simulator 99
61
29
  </Link>
62
30
  </h1>
63
31
  </nav>
@@ -3,22 +3,22 @@ import { Link } from "react-router-dom";
3
3
 
4
4
  const HomePage: React.FC = () => {
5
5
  return (
6
- <div style={{ textAlign: "center", padding: "2em" }}>
7
- <h1>Welcome to Pet Simulator 99 API</h1>
8
- <p>Your one-stop solution for accessing all Pet Simulator 99 data.</p>
9
- <p>Select a collection to get started:</p>
10
- <Link
11
- to="/collections"
12
- style={{
13
- padding: "0.5em 1em",
14
- background: "#007bff",
15
- color: "#fff",
16
- textDecoration: "none",
17
- borderRadius: "4px",
18
- }}
19
- >
20
- View Collections
21
- </Link>
6
+ <div style={{ textAlign: "center", padding: "4em", marginTop: "50px" }}>
7
+ <h1 style={{ fontSize: "3rem", marginBottom: "0.5em", color: "var(--primary-color)", textShadow: "4px 4px 0px var(--border-color)" }}>
8
+ Pet Simulator 99
9
+ </h1>
10
+ <p style={{ fontSize: "1.2rem", marginBottom: "2em" }}>
11
+ Explore the world of Pet Simulator 99! Check out all the items, pets, and collections.
12
+ </p>
13
+
14
+ <div style={{ display: "flex", justifyContent: "center", gap: "20px" }}>
15
+ <Link
16
+ to="/collections"
17
+ className="game-button"
18
+ >
19
+ Collections
20
+ </Link>
21
+ </div>
22
22
  </div>
23
23
  );
24
24
  };