Files
personal-site/pages/rates.js
T

144 lines
5.8 KiB
JavaScript
Raw Normal View History

2024-05-28 02:13:04 +02:00
import { useEffect, useState } from "react";
2025-12-01 22:57:12 +01:00
import Layout from "../components/Layout";
import RateCard from "../components/RateCard";
import Icon from "../components/Icon";
2024-05-28 02:13:04 +02:00
import { fetchData } from "../utils/api";
export default function Rates() {
const [data, setData] = useState(null);
2024-01-14 16:53:56 +01:00
const [loading, setLoading] = useState(true);
2025-12-01 22:23:08 +01:00
const [error, setError] = useState(null);
useEffect(() => {
const fetchDataFromApi = async () => {
try {
const apiData = await fetchData();
setData(apiData);
2025-12-01 22:23:08 +01:00
setError(null);
} catch (error) {
2024-05-28 02:13:04 +02:00
console.error("Error fetching data:", error);
2025-12-01 22:23:08 +01:00
setError("Failed to load exchange rates. Please try again later.");
2024-01-14 16:53:56 +01:00
} finally {
2025-12-01 22:23:08 +01:00
setLoading(false);
}
};
fetchDataFromApi();
}, []);
const formatCurrency = (value) => {
2024-05-28 02:13:04 +02:00
return new Intl.NumberFormat("en-NG", {
style: "currency",
currency: "NGN",
}).format(value);
};
2025-12-01 22:23:08 +01:00
const getBestRate = () => {
if (!data || data.length === 0) return null;
return Math.max(...data.map(item => item.rate));
};
const bestRate = getBestRate();
const sortedData = data ? [...data].sort((a, b) => {
const aIsBest = a.bestRate || a.rate === bestRate;
const bIsBest = b.bestRate || b.rate === bestRate;
if (aIsBest && !bIsBest) return -1;
if (!aIsBest && bIsBest) return 1;
return b.rate - a.rate;
}) : null;
return (
2025-12-01 22:57:12 +01:00
<Layout
title="EUR → NGN Exchange Rates · Jesse Egbosionu"
2025-12-01 22:57:12 +01:00
description="Compare live EUR to NGN exchange rates across multiple providers."
>
<section className="section rates-section">
<div className="shell">
<div className="rates-header">
<div className="section-heading">
<p className="eyebrow">
<span className="live-dot" />
Live tool
</p>
<h1 className="section-title">Exchange rates</h1>
<p className="section-lead">
Compare live EUR → NGN rates across remittance providers, fetched in real time.
</p>
</div>
2025-12-01 22:23:08 +01:00
2025-12-01 22:57:12 +01:00
<div className="currency-badge">
<span>€1 EUR</span>
<Icon name="arrowRight" size={16} />
<span>NGN</span>
</div>
2025-12-01 22:57:12 +01:00
</div>
2025-12-01 22:23:08 +01:00
{loading && (
2025-12-01 22:23:08 +01:00
<div className="rates-grid">
{[...Array(6)].map((_, index) => (
<div key={index} className="rate-card skeleton">
<div className="skeleton-line skeleton-header"></div>
<div className="skeleton-line skeleton-rate"></div>
<div className="skeleton-line skeleton-badge"></div>
</div>
2025-12-01 22:23:08 +01:00
))}
</div>
)}
2025-12-01 22:23:08 +01:00
{error && (
<div className="state-card">
<h3 className="state-title">Unable to load rates</h3>
<p className="state-message">{error}</p>
<button
onClick={() => window.location.reload()}
className="button button-primary"
>
Try again
</button>
</div>
)}
{!loading && !error && sortedData && sortedData.length > 0 && (
<>
<div className="rates-grid">
{sortedData.map((item, index) => (
<RateCard
key={item.provider}
provider={item.provider}
rate={item.rate}
href={item.href}
isBest={item.bestRate || item.rate === bestRate}
index={index}
formatCurrency={formatCurrency}
logo={item.logo}
/>
))}
</div>
<div className="rates-info">
<p>Rates are fetched live on each visit. Click a provider to visit their website.</p>
<p className="rates-timestamp">
Last updated {new Date().toLocaleString('en-GB', {
dateStyle: 'medium',
timeStyle: 'short'
})}
</p>
</div>
</>
)}
{!loading && !error && (!data || data.length === 0) && (
<div className="state-card">
<h3 className="state-title">No rates available</h3>
<p className="state-message">
We couldn't find any exchange rates at the moment.
2025-12-01 22:23:08 +01:00
</p>
</div>
)}
</div>
2025-12-01 22:57:12 +01:00
</section>
</Layout>
);
}