Skip to content

Commit

Permalink
Merge pull request #17 from princemuel:patch-02
Browse files Browse the repository at this point in the history
Patch-02: update main w/ current changes #15, #16, #12
  • Loading branch information
princemuel authored Oct 6, 2023
2 parents ad79ea7 + 3af82ec commit a1dde2a
Show file tree
Hide file tree
Showing 23 changed files with 740 additions and 555 deletions.
2 changes: 1 addition & 1 deletion @types/defaults.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@ interface IParams {
}

interface CSSStyleProps extends React.CSSProperties {
"--min-column-size": string;
"--min-col-size": string;
}
134 changes: 122 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,81 @@
# Countries of the World

This application uses the REST Countries API to get info about countries on planet earth.

## Table of contents

- [Countries of the World](#countries-of-the-world)
- [Table of contents](#table-of-contents)
- [Overview](#overview)
- [The challenge](#the-challenge)
- [Screenshot](#screenshot)
- [Links](#links)
- [Project Development](#project-development)
- [Running the App](#running-the-app)
- [Deploying the App](#deploying-the-app)
- [My process](#my-process)
- [Built with](#built-with)
- [What I learned](#what-i-learned)
- [Continued development](#continued-development)
- [Useful resources](#useful-resources)
- [Author](#author)
- [Acknowledgments](#acknowledgments)

## Overview

This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).

## Getting Started
### The challenge

Users should be able to:

- See all countries from the API on the homepage
- Search for a country using an `input` field
- Filter countries by region
- Click on a country to see more detailed information on a separate page
- Click through to the border countries on the detail page
- Toggle the color scheme between light and dark mode _(optional)_

### Screenshot

![](./screenshot.jpg)

### Links

Press <kbd>.</kbd> on the keyboard to view this project's code in the _`github.dev`_ code editor just like in _`Visual Studio Code`_

First, run the development server:
- Code: [Github Repository](https://github.com/princemuel/rest-countries)
- Live Site: [REST Countries of the World](https://rest-countries-mocha.vercel.app/)

## Project Development

### Running the App

- Clone the repo by running the command:

```bash
git clone https://github.com/princemuel/rest-countries.git
```

- Enter into the project folder:

```bash
cd rest-countries
```

- Install the project's dependencies:

```bash
# NOTE: This project uses pnpm as package manager but that's optional. You may delete the lockfile and install the deps using your choice of package manager

npm install
# or
yarn install
# or
pnpm install
```

- Then, run the development server:

```bash
npm run dev
Expand All @@ -12,23 +85,60 @@ yarn dev
pnpm dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.

This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
### Deploying the App

Your app is ready to be deployed! The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out the [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

## My process

### Built with

- Semantic HTML5 markup
- CSS custom properties
- Flexbox
- CSS Grid
- Mobile-first workflow
- [React](https://react.dev/) - JS library
- [Next.js](https://nextjs.org/docs) - React framework
- [Tailwind CSS](https://tailwindcss.com/docs) - For composing component styles using utility classes

### What I learned

```html
<h1>Some HTML code I'm proud of</h1>
```

```css
.proud-of-this-css {
color: papayawhip;
}
```

```js
const proudOfThisFunc = () => {
console.log('🎉');
};
```

## Learn More
### Continued development

To learn more about Next.js, take a look at the following resources:
Use this section to outline areas that you want to continue focusing on in future projects. These could be concepts you're still not completely comfortable with or techniques you found useful that you want to refine and perfect.

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
### Useful resources

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
- [Example resource 1](https://www.example.com) - This helped me for XYZ reason. I really liked this pattern and will use it going forward.
- [Example resource 2](https://www.example.com) - This is an amazing article which helped me finally understand XYZ. I'd recommend it to anyone still learning this concept.

## Deploy on Vercel
## Author

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
- Website (In Development) - [Prince Muel](https://princemuel.vercel.app/)
- LinkedIn - [@princemuel](https://linkedin.com/in/princemuel/)
- Twitter - [@iamprincemuel](https://twitter.com/iamprincemuel)

Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
## Acknowledgments
22 changes: 14 additions & 8 deletions app/countries/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { defineMeta } from '@/config';
import { hasValues } from '@/helpers';
import {
getAllCountries,
getCountryBySlug,
preloadBase64,
preloadCountry,
} from '@/lib';
import type { Metadata } from 'next';
import { notFound } from 'next/navigation';
import CountryDetailsTemplate from './country';

interface Props {
Expand All @@ -16,7 +19,9 @@ async function PageRoute({ params: { slug } }: Props) {

const imageResponse = await getCountryBySlug(slug);

preloadBase64(imageResponse[0].flags.svg);
if (!imageResponse || !hasValues(imageResponse)) throw notFound();

preloadBase64(imageResponse[0]?.flags?.svg);

return <CountryDetailsTemplate slug={slug} />;
}
Expand All @@ -35,16 +40,17 @@ export async function generateMetadata({
const response = await getCountryBySlug(slug);

const country = response[0];
if (!country) throw notFound();

if (!country)
return {
title: 'Country Not Found',
description: 'The requested resource does not exist',
};
// if (!country)
// return {
// title: 'Country Not Found',
// description: 'The requested resource does not exist',
// };

const title = `${country?.name?.common} `;

return {
return defineMeta({
title: title,
description: country?.flags?.alt,
icons: [
Expand All @@ -70,5 +76,5 @@ export async function generateMetadata({
description: country?.flags?.alt,
images: [country?.flags?.png, country?.flags?.svg],
},
};
});
}
15 changes: 13 additions & 2 deletions app/fonts/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import { cn } from '@/helpers';
import { Nunito_Sans } from 'next/font/google';
import localFont from 'next/font/local';

const FontSans = localFont({
const isProduction = process.env.NODE_ENV === 'production';

const FontSans_DEV = localFont({
display: 'swap',
variable: '--font-sans',
src: './nunito-sans.ttf',
});

export const fonts = cn(FontSans.variable);
const FontSans_PROD = Nunito_Sans({
display: 'swap',
variable: '--font-sans',
subsets: ['latin'],
});

export const fonts = cn(
isProduction ? [FontSans_PROD.variable] : [FontSans_DEV.variable]
);
4 changes: 2 additions & 2 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { BaseLayout } from '@/components';
import { seo } from '@/config';
import { defineMeta } from '@/config';
import { Providers } from '@/context';
import { cn } from '@/helpers';
import { Analytics } from '@vercel/analytics/react';
import { fonts } from './fonts';
import './globals.css';

export const metadata = seo;
export const metadata = defineMeta();

export default function RootLayout({
children,
Expand Down
11 changes: 3 additions & 8 deletions components/atoms/theme.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,9 @@ import * as React from 'react';
export function ThemeSwitch() {
const { resolvedTheme, setTheme } = useTheme();
const [_, startTransition] = React.useTransition();
const hasMounted = React.useRef(false);
const [hasMounted, setHasMounted] = React.useState(false);

React.useEffect(() => {
hasMounted.current = true;
return () => {
hasMounted.current = false;
};
}, []);
React.useEffect(() => setHasMounted(true), []);

const isDarkTheme = resolvedTheme === 'dark';

Expand All @@ -26,7 +21,7 @@ export function ThemeSwitch() {

const text = isDarkTheme ? 'dark mode' : 'light mode';

if (!hasMounted.current) return null;
if (!hasMounted) return null;

return (
<button
Expand Down
10 changes: 7 additions & 3 deletions components/molecules/filter-form.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
'use client';

import { useCountries, useFilterDispatch, useFilterState } from '@/context';
import {
filter,
useCountries,
useFilterDispatch,
useFilterState,
} from '@/context';
import { cn } from '@/helpers';
import { Listbox, Transition } from '@headlessui/react';
import { ChevronDownIcon } from 'lucide-react';
Expand All @@ -27,8 +32,7 @@ export function FilterForm() {
name='current-choice'
onChange={(value) => {
startTransition(() => {
dispatch({ type: 'SET_FILTER_TERM', payload: value });
dispatch({ type: 'FILTER', payload: value });
filter(dispatch, value);
});
}}
>
Expand Down
5 changes: 2 additions & 3 deletions components/molecules/search-form.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { useFilterDispatch, useFilterState } from '@/context';
import { search, useFilterDispatch, useFilterState } from '@/context';
import { SearchIcon } from 'lucide-react';
import { useTransition } from 'react';

Expand All @@ -11,8 +11,7 @@ export function SearchForm() {

function handleSearch(e: ReactInputEvent) {
startTransition(() => {
dispatch({ type: 'SET_SEARCH_TERM', payload: e.target.value });
dispatch({ type: 'SEARCH', payload: e.target.value });
search(dispatch, e.target.value);
});
}

Expand Down
26 changes: 14 additions & 12 deletions components/organisms/country-details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,34 +16,42 @@ interface Props {
export const CountryDetails = async ({ slug }: Props) => {
const response = (await getCountryBySlug(slug)) || [];
const country = response[0];
const blurDataUrl = await toBase64(country.flags.svg);
const blurDataUrl = await toBase64(country?.flags?.svg);

const borders = await Promise.all(
(country?.borders ?? []).map(async (border) => {
try {
const response = await getCountryBySlug(border);
return response[0];
} catch (error) {
console.log(error);

return null;
}
})
);

const nativeNameList = new Set<string>(
Object.values(country?.name?.nativeName || {}).map((name) => name?.common)
);

const nativeNames = Array.from(nativeNameList);

return (
<article>
<div className='flex flex-col flex-wrap gap-16 md:flex-row md:gap-10 lg:gap-20'>
<figure className='overflow-hidden rounded-xl'>
<NextImage
src={country?.flags?.svg}
alt={country?.flags?.alt || ''}
src={country?.flags?.svg || ''}
alt={country?.flags?.alt ?? `The country's flag`}
width={672}
height={378}
style={{ height: 'auto' }}
sizes='(min-width: 720px) 672px, calc(95.5vw - 19px)'
className='aspect-video rounded-xl object-cover'
priority={true}
blurDataURL={blurDataUrl}
placeholder='blur'
placeholder={blurDataUrl ? 'blur' : 'empty'}
/>
<figcaption className='sr-only'>{country?.name?.official}</figcaption>
</figure>
Expand All @@ -59,13 +67,7 @@ export const CountryDetails = async ({ slug }: Props) => {
<div className='flex flex-1 flex-col gap-2'>
<dl className='flex flex-row gap-2'>
<dt className='whitespace-pre font-semibold'>Native Name:</dt>
<dd className='font-light'>
{lf.format(
(Object.values(country?.name?.nativeName || {}) || []).map(
(name) => name?.common
)
)}
</dd>
<dd className='font-light'>{lf.format(nativeNames)}</dd>
</dl>

<dl className='flex flex-row gap-2'>
Expand Down Expand Up @@ -144,7 +146,7 @@ export const CountryDetails = async ({ slug }: Props) => {
Border Countries:
</dt>

<React.Suspense fallback={<div>Loading..</div>}>
<React.Suspense fallback={<div>Loading...</div>}>
<div className='flex flex-wrap gap-3'>
{hasValues(borders) ? (
borders?.map((border) => {
Expand Down
Loading

1 comment on commit a1dde2a

@vercel
Copy link

@vercel vercel bot commented on a1dde2a Oct 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.