1
2
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
import React, { useState, useEffect } from 'react';
interface FFXIVItemPriceProps {
itemId: number;
itemName: string;
itemImageUrl: string;
}
const FFXIVItemPrice: React.FC<FFXIVItemPriceProps> = ({ itemId = 5530, itemName="Coke", itemImageUrl="https://xivapi.com/i/021000/021462_hr1.png"}) => {
const [selectedWorld, setSelectedWorld] = useState<string | null>(null);
const [dailySaleVelocity, setDailySaleVelocity] = useState<number | null>(null);
const [averageSalePrice, setAverageSalePrice] = useState<number | null>(null);
const [inputQuantity, setInputQuantity] = useState<number>(0);
const [potentialGil, setPotentialGil] = useState<number>(0);
const fetchData = (attempt: number = 1) => {
fetch(`https://universalis.app/api/v2/aggregated/${selectedWorld}/${itemId}`)
.then(response => response.json())
.then(data => {
const result = data.results[0];
setDailySaleVelocity(Math.round(result.nq.dailySaleVelocity.world.quantity));
setAverageSalePrice(Math.round(result.nq.averageSalePrice.world.price));
})
.catch(error => {
console.error(`Error fetching data (attempt ${attempt}):`, error);
if (attempt < 3) {
setTimeout(() => fetchData(attempt + 1), 5000);
}
});
};
useEffect(() => {
const savedWorld = localStorage.getItem('selectedWorld');
if (savedWorld) {
setSelectedWorld(savedWorld);
} else {
setSelectedWorld('Midgardsormr');
}
fetchData();
}, [itemId, selectedWorld]);
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const quantity = parseInt(e.target.value, 10);
setInputQuantity(quantity);
setPotentialGil(quantity * (averageSalePrice || 0));
};
const formatNumber = (number: number | null) => {
return number !== null ? new Intl.NumberFormat().format(number) : 'N/A';
};
return (
<div className="ffxiv-container">
<div className="ffxiv-header">
<h1 className="ffxiv-h1">{itemName}</h1>
<img src={itemImageUrl} alt={itemName} className="ffxiv-item-icon" />
</div>
<table className="ffxiv-table">
<tbody>
<tr>
<td className="ffxiv-label">Average Price/Item:</td>
<td className="ffxiv-value">{formatNumber(averageSalePrice)} gil</td>
</tr>
<tr>
<td className="ffxiv-label">Daily Sale Velocity:</td>
<td className="ffxiv-value">{formatNumber(dailySaleVelocity)} items</td>
</tr>
<tr>
<td className="ffxiv-label">Enter Quantity:</td>
<td>
<input
type="number"
value={inputQuantity}
onChange={handleInputChange}
placeholder="Enter quantity"
className="ffxiv-input"
/>
</td>
</tr>
<tr>
<td className="ffxiv-label">Potential Gil Earnings:</td>
<td className="ffxiv-value">{formatNumber(potentialGil)} gil</td>
</tr>
</tbody>
</table>
<footer>
{selectedWorld} Marketboard Data provided by Universalis API. <a className="eorzeadb_link" href="#" onClick={(e) => { e.preventDefault(); fetchData(); }}>Click here to reload data</a>
<br />
</footer>
</div>
);
};
export default FFXIVItemPrice;
|