chore: modularize ui components

This commit is contained in:
Jesse Egbosionu
2025-12-01 22:57:12 +01:00
parent 5c22b9b844
commit c460ea27d4
8 changed files with 270 additions and 216 deletions
+78 -118
View File
@@ -1,5 +1,6 @@
import Head from "next/head";
import { useEffect, useState } from "react";
import Layout from "../components/Layout";
import RateCard from "../components/RateCard";
import { fetchData } from "../utils/api";
export default function Rates() {
@@ -47,136 +48,95 @@ export default function Rates() {
}) : null;
return (
<div className="container">
<Head>
<title>EUR to NGN Exchange Rates - Jesse Egbosionu</title>
<link rel="icon" href="/favicon.ico" />
<meta name="description" content="Compare live EUR to NGN exchange rates across multiple providers." />
</Head>
<header className="header">
<nav className="navbar">
<div className="logo">
<a href="/">
<span className="logo-text">JE</span>
</a>
<Layout
title="EUR to NGN Exchange Rates - Jesse Egbosionu"
description="Compare live EUR to NGN exchange rates across multiple providers."
showNavLinks={true}
>
<section className="rates-section">
<div className="rates-header">
<div className="rates-title-group">
<h1 className="rates-title">Exchange Rates</h1>
<p className="rates-subtitle">
Compare live EUR to NGN rates across providers
</p>
</div>
<div className="nav-links">
<a href="/" className="nav-link">Home</a>
<a href="/rates" className="nav-link cta">Rates</a>
</div>
</nav>
</header>
<main className="main-content">
<section className="rates-section">
<div className="rates-header">
<div className="rates-title-group">
<h1 className="rates-title">Exchange Rates</h1>
<p className="rates-subtitle">
Compare live EUR to NGN rates across providers
</p>
<div className="currency-display">
<div className="currency-badge">
<span className="currency-from">€1 EUR</span>
<span className="currency-arrow">→</span>
<span className="currency-to">NGN</span>
</div>
</div>
</div>
<div className="currency-display">
<div className="currency-badge">
<span className="currency-from">€1 EUR</span>
<span className="currency-arrow">→</span>
<span className="currency-to">NGN</span>
{loading && (
<div className="rates-grid">
{[...Array(6)].map((_, index) => (
<div key={index} className="rate-card skeleton">
<div className="skeleton-header"></div>
<div className="skeleton-rate"></div>
<div className="skeleton-badge"></div>
</div>
</div>
))}
</div>
)}
{loading && (
{error && (
<div className="error-state">
<div className="error-icon">⚠️</div>
<h3 className="error-title">Unable to load rates</h3>
<p className="error-message">{error}</p>
<button
onClick={() => window.location.reload()}
className="retry-button"
>
Try again
</button>
</div>
)}
{!loading && !error && sortedData && sortedData.length > 0 && (
<>
<div className="rates-grid">
{[...Array(6)].map((_, index) => (
<div key={index} className="rate-card skeleton">
<div className="skeleton-header"></div>
<div className="skeleton-rate"></div>
<div className="skeleton-badge"></div>
</div>
{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}
/>
))}
</div>
)}
{error && (
<div className="error-state">
<div className="error-icon">⚠️</div>
<h3 className="error-title">Unable to load rates</h3>
<p className="error-message">{error}</p>
<button
onClick={() => window.location.reload()}
className="retry-button"
>
Try again
</button>
</div>
)}
{!loading && !error && sortedData && sortedData.length > 0 && (
<>
<div className="rates-grid">
{sortedData.map((item, index) => (
<a
key={item.provider}
href={item.href}
target="_blank"
rel="noopener noreferrer"
className={`rate-card ${item.bestRate || item.rate === bestRate ? 'best' : ''}`}
style={{ animationDelay: `${index * 0.1}s` }}
>
<div className="rate-card-header">
<h3 className="provider-name">{item.provider}</h3>
{(item.bestRate || item.rate === bestRate) && (
<span className="best-badge">Best Rate</span>
)}
</div>
<div className="rate-value">
{formatCurrency(item.rate)}
</div>
<div className="rate-card-footer">
<span className="visit-link">
Visit provider →
</span>
</div>
</a>
))}
</div>
<div className="rates-info">
<p className="info-text">
Rates are updated regularly. Click on a provider to visit their website.
</p>
<p className="info-timestamp">
Last updated: {new Date().toLocaleString('en-GB', {
dateStyle: 'medium',
timeStyle: 'short'
})}
</p>
</div>
</>
)}
{!loading && !error && (!data || data.length === 0) && (
<div className="empty-state">
<div className="empty-icon">📊</div>
<h3 className="empty-title">No rates available</h3>
<p className="empty-message">
We couldn't find any exchange rates at the moment.
<div className="rates-info">
<p className="info-text">
Rates are updated regularly. Click on a provider to visit their website.
</p>
<p className="info-timestamp">
Last updated: {new Date().toLocaleString('en-GB', {
dateStyle: 'medium',
timeStyle: 'short'
})}
</p>
</div>
)}
</section>
</main>
</>
)}
<footer className="footer">
<div className="footer-content">
<p>Built with <span className="heart">❤️</span> and Amara Chukwu</p>
<p className="footer-year">© {new Date().getFullYear()}</p>
</div>
</footer>
</div>
{!loading && !error && (!data || data.length === 0) && (
<div className="empty-state">
<div className="empty-icon">📊</div>
<h3 className="empty-title">No rates available</h3>
<p className="empty-message">
We couldn't find any exchange rates at the moment.
</p>
</div>
)}
</section>
</Layout>
);
}