mirror of
https://github.com/dat515-2025/Group-8.git
synced 2026-03-22 06:57:47 +01:00
feat(frontend): fixed exchange rates
Some checks are pending
Deploy Prod / Run Python Tests (push) Waiting to run
Deploy Prod / Build and push image (reusable) (push) Waiting to run
Deploy Prod / Generate Production URLs (push) Waiting to run
Deploy Prod / Frontend - Build and Deploy to Cloudflare Pages (prod) (push) Blocked by required conditions
Deploy Prod / Helm upgrade/install (prod) (push) Blocked by required conditions
Some checks are pending
Deploy Prod / Run Python Tests (push) Waiting to run
Deploy Prod / Build and push image (reusable) (push) Waiting to run
Deploy Prod / Generate Production URLs (push) Waiting to run
Deploy Prod / Frontend - Build and Deploy to Cloudflare Pages (prod) (push) Blocked by required conditions
Deploy Prod / Helm upgrade/install (prod) (push) Blocked by required conditions
This commit is contained in:
@@ -12,28 +12,31 @@ function formatAmount(n: number) {
|
|||||||
return new Intl.NumberFormat(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(n);
|
return new Intl.NumberFormat(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add this new component to your Dashboard.tsx file, above the Dashboard component
|
//https://unirateapi.com/
|
||||||
|
|
||||||
|
|
||||||
// Define the structure for the rate data we care about
|
// Define the structure for the rate data we care about
|
||||||
type CnbRate = {
|
type RateData = {
|
||||||
currencyCode: string;
|
currencyCode: string;
|
||||||
rate: number;
|
rate: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
// The part of the API response structure we need
|
// The part of the API response structure we need
|
||||||
type CnbApiResponse = {
|
type UnirateApiResponse = {
|
||||||
rates: Array<{
|
base: string;
|
||||||
amount: number;
|
rates: { [key: string]: number };
|
||||||
currencyCode: string;
|
// We'll also check for error formats just in case
|
||||||
rate: number;
|
message?: string;
|
||||||
}>;
|
error?: {
|
||||||
|
info: string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// The currencies you want to display
|
// The currencies you want to display
|
||||||
const TARGET_CURRENCIES = ['EUR', 'USD', 'NOK'];
|
const TARGET_CURRENCIES = ['EUR', 'USD', 'NOK'];
|
||||||
|
|
||||||
function CurrencyRates() {
|
function CurrencyRates() {
|
||||||
const [rates, setRates] = useState<CnbRate[]>([]);
|
const [rates, setRates] = useState<RateData[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
@@ -42,31 +45,49 @@ function CurrencyRates() {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
// Get today's date in YYYY-MM-DD format for the API
|
const API_KEY = import.meta.env.VITE_UNIRATE_API_KEY;
|
||||||
const today = new Date().toISOString().split('T')[0];
|
|
||||||
const CNB_API_URL = `https://api.cnb.cz/cnbapi/exrates/daily?date=${today}&lang=EN`;
|
// We need to get the CZK rate as well, to use it for conversion
|
||||||
|
const allSymbols = [...TARGET_CURRENCIES, 'CZK'].join(',');
|
||||||
|
|
||||||
|
// We remove the `base` param, as the API seems to force base=USD
|
||||||
|
const UNIRATE_API_URL = `https://unirateapi.com/api/rates?api_key=${API_KEY}&symbols=${allSymbols}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(CNB_API_URL);
|
const res = await fetch(UNIRATE_API_URL);
|
||||||
if (!res.ok) {
|
const data: UnirateApiResponse = await res.json();
|
||||||
// This can happen on weekends/holidays or if rates aren't posted yet
|
|
||||||
throw new Error(`Rates unavailable (Status: ${res.status})`);
|
|
||||||
}
|
|
||||||
const data: CnbApiResponse = await res.json();
|
|
||||||
|
|
||||||
|
// --- THIS IS THE NEW, CORRECTED LOGIC ---
|
||||||
|
|
||||||
|
// 1. Check if the 'rates' object exists. If not, it's an error.
|
||||||
if (!data.rates) {
|
if (!data.rates) {
|
||||||
throw new Error("Invalid API response");
|
let errorMessage = data.message || (data.error ? data.error.info : 'Invalid API response');
|
||||||
|
throw new Error(errorMessage || 'Could not load rates');
|
||||||
}
|
}
|
||||||
|
|
||||||
const filteredRates = data.rates
|
// 2. Check that we got the base currency (USD) and our conversion currency (CZK)
|
||||||
.filter(rate => TARGET_CURRENCIES.includes(rate.currencyCode))
|
if (data.base !== 'USD' || !data.rates.CZK) {
|
||||||
.map(rate => ({
|
throw new Error('API response is missing required data for conversion (USD or CZK)');
|
||||||
currencyCode: rate.currencyCode,
|
}
|
||||||
// Handle 'amount' field (e.g., JPY is per 100)
|
|
||||||
rate: rate.rate / rate.amount
|
|
||||||
}));
|
|
||||||
|
|
||||||
setRates(filteredRates);
|
// 3. Get our main conversion factor
|
||||||
|
const czkPerUsd = data.rates.CZK; // e.g., 23.0
|
||||||
|
|
||||||
|
// 4. Calculate the rates for our target currencies
|
||||||
|
const formattedRates = TARGET_CURRENCIES.map(code => {
|
||||||
|
const targetPerUsd = data.rates[code]; // e.g., 0.9 for EUR
|
||||||
|
|
||||||
|
// This calculates: (CZK per USD) / (TARGET per USD) = CZK per TARGET
|
||||||
|
// e.g. (23.0 CZK / 1 USD) / (0.9 EUR / 1 USD) = 25.55 CZK / 1 EUR
|
||||||
|
const rate = czkPerUsd / targetPerUsd;
|
||||||
|
|
||||||
|
return {
|
||||||
|
currencyCode: code,
|
||||||
|
rate: rate,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
setRates(formattedRates);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
setError(err.message || 'Could not load rates');
|
setError(err.message || 'Could not load rates');
|
||||||
} finally {
|
} finally {
|
||||||
@@ -108,10 +129,26 @@ function CurrencyRates() {
|
|||||||
)) : <li style={{color: '#8a91b4'}}>No rates found.</li>}
|
)) : <li style={{color: '#8a91b4'}}>No rates found.</li>}
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<a
|
||||||
|
href="https://unirateapi.com"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
style={{
|
||||||
|
display: 'block',
|
||||||
|
marginTop: '1rem',
|
||||||
|
fontSize: '0.8em',
|
||||||
|
color: '#8a91b4', // Muted color
|
||||||
|
textDecoration: 'none'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Exchange Rates By UniRateAPI
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default function Dashboard({ onLogout }: { onLogout: () => void }) {
|
export default function Dashboard({ onLogout }: { onLogout: () => void }) {
|
||||||
const [current, setCurrent] = useState<'home' | 'manual' | 'account' | 'appearance'>('home');
|
const [current, setCurrent] = useState<'home' | 'manual' | 'account' | 'appearance'>('home');
|
||||||
const [transactions, setTransactions] = useState<Transaction[]>([]);
|
const [transactions, setTransactions] = useState<Transaction[]>([]);
|
||||||
|
|||||||
Reference in New Issue
Block a user