diff --git a/components/Footer.js b/components/Footer.js new file mode 100644 index 0000000..c73f619 --- /dev/null +++ b/components/Footer.js @@ -0,0 +1,12 @@ +export default function Footer() { + return ( + + ); +} diff --git a/components/Header.js b/components/Header.js new file mode 100644 index 0000000..d778c16 --- /dev/null +++ b/components/Header.js @@ -0,0 +1,32 @@ +import Link from 'next/link'; + +export default function Header({ showNavLinks = true }) { + return ( +
+ +
+ ); +} diff --git a/components/Layout.js b/components/Layout.js new file mode 100644 index 0000000..39f6398 --- /dev/null +++ b/components/Layout.js @@ -0,0 +1,24 @@ +import Head from 'next/head'; +import Header from './Header'; +import Footer from './Footer'; + +export default function Layout({ children, title, description, showNavLinks = true }) { + return ( +
+ + {title || 'Jesse Egbosionu - Software Engineer'} + + + + +
+ +
{children}
+ +
+ ); +} diff --git a/components/RateCard.js b/components/RateCard.js new file mode 100644 index 0000000..e0b1a80 --- /dev/null +++ b/components/RateCard.js @@ -0,0 +1,22 @@ +export default function RateCard({ provider, rate, href, isBest, index, formatCurrency }) { + return ( + +
+

{provider}

+ {isBest && Best Rate} +
+ +
{formatCurrency(rate)}
+ +
+ Visit provider → +
+
+ ); +} diff --git a/components/SocialLinks.js b/components/SocialLinks.js new file mode 100644 index 0000000..c2c3827 --- /dev/null +++ b/components/SocialLinks.js @@ -0,0 +1,48 @@ +const socialData = [ + { + name: 'GitHub', + url: 'https://github.com/jesseinit', + icon: ( + + + + ), + }, + { + name: 'LinkedIn', + url: 'https://linkedin.com/in/jesse-egbosionu/', + icon: ( + + + + ), + }, + { + name: 'Twitter', + url: 'https://twitter.com/jesseinit/', + icon: ( + + + + ), + }, +]; + +export default function SocialLinks() { + return ( +
+ {socialData.map((social) => ( + + {social.icon} + {social.name} + + ))} +
+ ); +} diff --git a/pages/index.js b/pages/index.js index 7648658..5476059 100644 --- a/pages/index.js +++ b/pages/index.js @@ -1,94 +1,50 @@ -import Head from 'next/head'; +import Layout from '../components/Layout'; +import SocialLinks from '../components/SocialLinks'; export default function Home() { return ( -
- - Jesse Egbosionu - Software Engineer - - - + +
+
+

+ Hey, I'm Jesse Egbosionu +

-
- -
+

+ Software Engineer crafting scalable backend systems in Amsterdam +

-
-
-
-

- Hey, I'm Jesse Egbosionu -

- -

- Software Engineer crafting scalable backend systems in Amsterdam +

+

+ I build and scale software backends with a focus on creating impact through + teamwork and continuous learning. My passion lies in solving complex problems + and delivering solutions that enrich people's lives.

-
-

- I build and scale software backends with a focus on creating impact through - teamwork and continuous learning. My passion lies in solving complex problems - and delivering solutions that enrich people's lives. -

- -

- Over the past 6 years, I've shipped products across finance, ecommerce, - and logistics, working with technologies like Python (Django, FastAPI), - JavaScript (React, Node.js), PostgreSQL, Docker, Redis, and Kubernetes. -

-
- - - - - Let's work together - → - +

+ Over the past 6 years, I've shipped products across finance, ecommerce, + and logistics, working with technologies like Python (Django, FastAPI), + JavaScript (React, Node.js), PostgreSQL, Docker, Redis, and Kubernetes. +

-
-
- Jesse Egbosionu -
-
-
+ - -
+ +
+
+ Jesse Egbosionu +
+
+
); } diff --git a/pages/rates.js b/pages/rates.js index 082c4e7..9df5610 100644 --- a/pages/rates.js +++ b/pages/rates.js @@ -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 ( -
- - EUR to NGN Exchange Rates - Jesse Egbosionu - - - - -
- -
-
-
-
-
-

Exchange Rates

-

- Compare live EUR to NGN rates across providers -

+
+
+ €1 EUR + → + NGN
+
+
-
-
- €1 EUR - → - NGN + {loading && ( +
+ {[...Array(6)].map((_, index) => ( +
+
+
+
-
+ ))}
+ )} - {loading && ( + {error && ( +
+
⚠️
+

Unable to load rates

+

{error}

+ +
+ )} + + {!loading && !error && sortedData && sortedData.length > 0 && ( + <>
- {[...Array(6)].map((_, index) => ( -
-
-
-
-
+ {sortedData.map((item, index) => ( + ))}
- )} - {error && ( -
-
⚠️
-

Unable to load rates

-

{error}

- -
- )} - - {!loading && !error && sortedData && sortedData.length > 0 && ( - <> - - -
-

- Rates are updated regularly. Click on a provider to visit their website. -

-

- Last updated: {new Date().toLocaleString('en-GB', { - dateStyle: 'medium', - timeStyle: 'short' - })} -

-
- - )} - - {!loading && !error && (!data || data.length === 0) && ( -
-
📊
-

No rates available

-

- We couldn't find any exchange rates at the moment. +

+

+ Rates are updated regularly. Click on a provider to visit their website. +

+

+ Last updated: {new Date().toLocaleString('en-GB', { + dateStyle: 'medium', + timeStyle: 'short' + })}

- )} -
-
+ + )} -
-
-

Built with ❤️ and Amara Chukwu

-

© {new Date().getFullYear()}

-
-
-
+ {!loading && !error && (!data || data.length === 0) && ( +
+
📊
+

No rates available

+

+ We couldn't find any exchange rates at the moment. +

+
+ )} + + ); } diff --git a/styles/globals.scss b/styles/globals.scss index de0d28b..d1a80a5 100644 --- a/styles/globals.scss +++ b/styles/globals.scss @@ -81,7 +81,7 @@ strong { // HEADER .header { padding: var(--spacing-lg) 0; - border-bottom: 2px solid var(--color-primary); + border-bottom: 1px solid var(--color-primary); background: rgba(255, 254, 240, 0.95); backdrop-filter: blur(10px); position: sticky; @@ -105,7 +105,7 @@ strong { background: var(--color-accent); color: var(--color-primary); padding: 0.25rem 0.75rem; - border: 2px solid var(--color-primary); + border: 1px solid var(--color-primary); border-radius: var(--radius-sm); } } @@ -132,7 +132,7 @@ strong { &.cta { color: var(--color-primary); background: var(--color-accent); - border: 2px solid var(--color-primary); + border: 1px solid var(--color-primary); &:hover { background: var(--color-accent-hover); @@ -175,7 +175,7 @@ strong { background: var(--color-accent); color: var(--color-primary); padding: 0 0.5rem; - border-bottom: 4px solid var(--color-primary); + border-bottom: 2px solid var(--color-primary); } } @@ -215,7 +215,7 @@ strong { gap: var(--spacing-xs); padding: var(--spacing-xs) var(--spacing-md); background: var(--color-background); - border: 2px solid var(--color-primary); + border: 1px solid var(--color-primary); border-radius: var(--radius-sm); font-size: 0.9375rem; font-weight: 600; @@ -253,7 +253,7 @@ strong { box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); animation: fadeInUp 0.6s ease 0.4s forwards; opacity: 0; - border: 3px solid var(--color-primary); + border: 1px solid var(--color-primary); &:hover { background: var(--color-accent); @@ -289,7 +289,7 @@ strong { border-radius: var(--radius-lg); transform: rotate(-6deg); z-index: -1; - border: 3px solid var(--color-primary); + border: 1px solid var(--color-primary); } .hero-image { @@ -300,7 +300,7 @@ strong { box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15); object-fit: cover; transition: transform 0.3s ease; - border: 3px solid var(--color-primary); + border: 1px solid var(--color-primary); &:hover { transform: scale(1.02) rotate(2deg); @@ -309,7 +309,7 @@ strong { // FOOTER .footer { - border-top: 2px solid var(--color-primary); + border-top: 1px solid var(--color-primary); padding: var(--spacing-lg) 0; margin-top: auto; } @@ -393,7 +393,7 @@ strong { gap: var(--spacing-lg); text-align: center; padding-bottom: var(--spacing-lg); - border-bottom: 2px solid var(--color-primary); + border-bottom: 1px solid var(--color-primary); } .rates-title-group { @@ -429,7 +429,7 @@ strong { gap: var(--spacing-md); padding: var(--spacing-md) var(--spacing-lg); background: var(--color-accent); - border: 3px solid var(--color-primary); + border: 1px solid var(--color-primary); border-radius: var(--radius-lg); font-weight: 700; font-size: 1.125rem; @@ -461,7 +461,7 @@ strong { gap: var(--spacing-md); padding: var(--spacing-lg); background: var(--color-background); - border: 3px solid var(--color-primary); + border: 1px solid var(--color-primary); border-radius: var(--radius-lg); transition: all 0.3s ease; cursor: pointer; @@ -480,7 +480,7 @@ strong { } &.best { - border-width: 4px; + border-width: 2px; background: var(--color-accent); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); position: relative; @@ -492,7 +492,7 @@ strong { top: 0; left: 0; right: 0; - height: 6px; + height: 3px; background: var(--color-primary); } @@ -537,7 +537,7 @@ strong { letter-spacing: 0.75px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); animation: badgePulse 2s ease-in-out infinite; - border: 2px solid var(--color-primary); + border: 1px solid var(--color-primary); &::before { content: '★'; @@ -598,7 +598,7 @@ strong { gap: var(--spacing-xs); padding: var(--spacing-lg); background: var(--color-surface); - border: 2px solid var(--color-primary); + border: 1px solid var(--color-primary); border-radius: var(--radius-lg); text-align: center; animation: fadeInUp 0.6s ease 0.4s forwards; @@ -700,7 +700,7 @@ strong { padding: var(--spacing-sm) var(--spacing-lg); background: var(--color-primary); color: var(--color-accent); - border: 3px solid var(--color-primary); + border: 1px solid var(--color-primary); border-radius: var(--radius-md); font-size: 1rem; font-weight: 600;