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

This commit is contained in:
ribardej
2025-11-05 23:14:12 +01:00
parent 72494c4aae
commit 0c9882e9b3

View File

@@ -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[]>([]);