From a4fe4f09f8ccb1a40b8fed8c94f076a3ca1ccbc0 Mon Sep 17 00:00:00 2001 From: Ansul Agrawal Date: Sun, 7 May 2023 14:29:04 +0530 Subject: [PATCH] Fix Pageing issue --- .gitignore | 84 ++++++++++++++++++++++++ index.html | 13 ++++ package.json | 28 ++++++++ postcss.config.js | 6 ++ src/App.jsx | 20 ++++++ src/app/apiSlices/api.js | 39 +++++++++++ src/app/index.js | 12 ++++ src/assets/ArrowLeft.jsx | 11 ++++ src/assets/MagnifyingGlass.jsx | 11 ++++ src/assets/Moon.jsx | 34 ++++++++++ src/components/Card.jsx | 25 +++++++ src/components/Cards.jsx | 40 ++++++++++++ src/components/Loading.jsx | 21 ++++++ src/components/NotFound.jsx | 22 +++++++ src/components/Search.jsx | 22 +++++++ src/components/navbar.jsx | 41 ++++++++++++ src/index.css | 32 +++++++++ src/main.jsx | 14 ++++ src/pages/Country.jsx | 20 ++++++ src/pages/CountryDetail.jsx | 116 +++++++++++++++++++++++++++++++++ src/pages/Home.jsx | 14 ++++ tailwind.config.js | 19 ++++++ vercel.json | 3 + vite.config.js | 5 ++ 24 files changed, 652 insertions(+) create mode 100644 .gitignore create mode 100644 index.html create mode 100644 package.json create mode 100644 postcss.config.js create mode 100644 src/App.jsx create mode 100644 src/app/apiSlices/api.js create mode 100644 src/app/index.js create mode 100644 src/assets/ArrowLeft.jsx create mode 100644 src/assets/MagnifyingGlass.jsx create mode 100644 src/assets/Moon.jsx create mode 100644 src/components/Card.jsx create mode 100644 src/components/Cards.jsx create mode 100644 src/components/Loading.jsx create mode 100644 src/components/NotFound.jsx create mode 100644 src/components/Search.jsx create mode 100644 src/components/navbar.jsx create mode 100644 src/index.css create mode 100644 src/main.jsx create mode 100644 src/pages/Country.jsx create mode 100644 src/pages/CountryDetail.jsx create mode 100644 src/pages/Home.jsx create mode 100644 tailwind.config.js create mode 100644 vercel.json create mode 100644 vite.config.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2d19ef9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,84 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +# dependencies +node_modules +dist +dist-ssr +*.local +/.pnp +.pnp.js + +# testing +/coverage +__snapshots__/ + + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + + +# production +/build +/dist + +# misc +.DS_Store +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + + +# Compiled Java class files +*.class + +# Compiled Python bytecode +*.py[cod] + + +# Package files +*.jar + +# Maven +target/ + +# JetBrains IDE +.idea/ + +# Unit test reports +TEST*.xml + +# Generated by Windows +Thumbs.db + + +# Applications +*.app +*.exe +*.war + +# Large media files +*.mp4 +*.tiff +*.avi +*.flv +*.mov +*.wmv + +package-lock.json \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..3b969b5 --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + + Countries Around the World + + +
+ + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..9b1ed37 --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "rest-countries-api-with-color-theme-switcher", + "private": false, + "version": "1.0.0", + "type": "module", + "author": "Ansul Agrawal", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@reduxjs/toolkit": "^1.9.3", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-redux": "^8.0.5", + "react-router-dom": "^6.10.0" + }, + "devDependencies": { + "@types/react": "^18.0.28", + "@types/react-dom": "^18.0.11", + "@vitejs/plugin-react": "^3.1.0", + "autoprefixer": "^10.4.14", + "postcss": "^8.4.21", + "tailwindcss": "^3.3.1", + "vite": "^4.2.0" + } +} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..2aa7205 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/src/App.jsx b/src/App.jsx new file mode 100644 index 0000000..74c35f6 --- /dev/null +++ b/src/App.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { createBrowserRouter, RouterProvider } from 'react-router-dom'; +import Country from './pages/Country'; +import CountryDetail from './pages/CountryDetail'; +import Home from './pages/Home'; + +const router = createBrowserRouter([ + { + path: '/', + element: , + children: [ + { path: '/', element: }, + { path: '/:id', element: }, + ], + }, +]); + +export default function App() { + return ; +} diff --git a/src/app/apiSlices/api.js b/src/app/apiSlices/api.js new file mode 100644 index 0000000..28c65ad --- /dev/null +++ b/src/app/apiSlices/api.js @@ -0,0 +1,39 @@ +import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"; + +export const countryApi = createApi({ + reducerPath: "countryApi", + baseQuery: fetchBaseQuery({ baseUrl: "https://restcountries.com/v3.1" }), + endpoints: builder => ({ + getAllCountries: builder.query({ + query: name => { + if (name === "") { + return "all"; + } else return `name/${name.trim()}`; + }, + transformResponse: response => { + response.sort((a, b) => { + if (a.name.common < b.name.common) { + return -1; + } + if (a.name.common > b.name.common) { + return 1; + } + return 0; + }); + return response; + }, + }), + getCountryDetail: builder.query({ + query: name => { + return `name/${name.trim()}?fullText=true`; + }, + }), + getBorderCountries: builder.query({ + query: name => { + return `alpha?codes=${name}`; + }, + }), + }), +}); + +export const { useGetAllCountriesQuery, useGetCountryDetailQuery, useGetBorderCountriesQuery } = countryApi; diff --git a/src/app/index.js b/src/app/index.js new file mode 100644 index 0000000..629e0e4 --- /dev/null +++ b/src/app/index.js @@ -0,0 +1,12 @@ +import { configureStore } from "@reduxjs/toolkit"; +import { setupListeners } from "@reduxjs/toolkit/query"; +import { countryApi } from "./apiSlices/api"; + +export const store = configureStore({ + reducer: { + [countryApi.reducerPath]: countryApi.reducer, + }, + middleware: getDefaultMiddleware => getDefaultMiddleware().concat(countryApi.middleware), +}); + +setupListeners(store.dispatch); diff --git a/src/assets/ArrowLeft.jsx b/src/assets/ArrowLeft.jsx new file mode 100644 index 0000000..1ffea95 --- /dev/null +++ b/src/assets/ArrowLeft.jsx @@ -0,0 +1,11 @@ +import React from "react"; + +function ArrowLeft({ className }) { + return ( + + + + ); +} + +export default ArrowLeft; diff --git a/src/assets/MagnifyingGlass.jsx b/src/assets/MagnifyingGlass.jsx new file mode 100644 index 0000000..3b5abfc --- /dev/null +++ b/src/assets/MagnifyingGlass.jsx @@ -0,0 +1,11 @@ +import React from "react"; + +function MagnifyingGlass({ className }) { + return ( + + + + ); +} + +export default MagnifyingGlass; diff --git a/src/assets/Moon.jsx b/src/assets/Moon.jsx new file mode 100644 index 0000000..9a17730 --- /dev/null +++ b/src/assets/Moon.jsx @@ -0,0 +1,34 @@ +import React from 'react'; + +function MoonOutline({ className }) { + return ( + + + + ); +} + +function MoonSolid({ className }) { + return ( + + + + ); +} + +export default function Moon({ dark, className }) { + return ( + <> + {!dark && } + {dark && } + + ); +} diff --git a/src/components/Card.jsx b/src/components/Card.jsx new file mode 100644 index 0000000..255e8df --- /dev/null +++ b/src/components/Card.jsx @@ -0,0 +1,25 @@ +import React from 'react'; + +const Card = ({ data: { flags, name, population, region, capital } }) => { + return ( +
+
+ {`${flags.alt} +
+
+

{name.common}

+

+ Population: {population} +

+

+ Region: {region} +

+

+ Capital: {capital} +

+
+
+ ); +}; + +export default Card; diff --git a/src/components/Cards.jsx b/src/components/Cards.jsx new file mode 100644 index 0000000..45f4495 --- /dev/null +++ b/src/components/Cards.jsx @@ -0,0 +1,40 @@ +import React from 'react'; +import Card from './Card'; +import { useGetAllCountriesQuery } from '../app/apiSlices/api'; +import { Link } from 'react-router-dom'; +import Loading from './Loading.jsx'; +import NotFound from './NotFound.jsx'; + +const Cards = ({ search, setSearch, setText }) => { + const { data, isLoading, isError } = useGetAllCountriesQuery(search.get('search') || ''); + + if (isLoading) { + return ; + } + + if (isError) { + return ( + { + setText(''); + setSearch({ search: '' }); + }} + /> + ); + } + + return ( +
+ {data?.map(data => ( +
  • + + + +
  • + ))} +
    + ); +}; + +export default Cards; diff --git a/src/components/Loading.jsx b/src/components/Loading.jsx new file mode 100644 index 0000000..6df0a14 --- /dev/null +++ b/src/components/Loading.jsx @@ -0,0 +1,21 @@ +import React from 'react'; + +export default function Loading() { + return ( +
    +
    + + Loading... +
    +
    + ); +} diff --git a/src/components/NotFound.jsx b/src/components/NotFound.jsx new file mode 100644 index 0000000..d25f020 --- /dev/null +++ b/src/components/NotFound.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; + +function NotFound({ action }) { + return ( +
    +

    404

    +
    Page Not Found
    +
    +
    + + + + + +
    +
    +
    + ); +} + +export default NotFound; diff --git a/src/components/Search.jsx b/src/components/Search.jsx new file mode 100644 index 0000000..d740b07 --- /dev/null +++ b/src/components/Search.jsx @@ -0,0 +1,22 @@ +import React, { useState } from "react"; +import MagnifyingGlass from "../assets/MagnifyingGlass"; +import { Form } from "react-router-dom"; + +function Search({ text, setText, setValue }) { + const handleChange = event => { + event.preventDefault(); + setText(event.target.value); + setValue({ search: event.target.value }); + }; + + return ( +
    + + {/*
    */} + + {/*
    */} +
    + ); +} + +export default Search; diff --git a/src/components/navbar.jsx b/src/components/navbar.jsx new file mode 100644 index 0000000..408dac2 --- /dev/null +++ b/src/components/navbar.jsx @@ -0,0 +1,41 @@ +import React, { useEffect, useState } from 'react'; +import { Link } from 'react-router-dom'; +import Moon from '../assets/Moon.jsx'; + +const navbar = () => { + const [dark, setDark] = useState(() => { + if (localStorage.getItem('mode') === 'true') { + document.documentElement.classList.add('dark'); + return true; + } + return false; + }); + + useEffect(() => { + if (!localStorage.getItem('mode')) { + localStorage.setItem('mode', true); + setDark(true); + } + }); + const darkModeHandler = () => { + setDark(!dark); + localStorage.setItem('mode', !dark); + document.documentElement.classList.toggle('dark'); + }; + + return ( + + ); +}; + +export default navbar; diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..890d758 --- /dev/null +++ b/src/index.css @@ -0,0 +1,32 @@ +/* Fonts */ +@import url('https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@300;600;800&display=swap'); + +/* Tailwind CSS */ +@tailwind base; +@tailwind components; +@tailwind utilities; + +*::-webkit-scrollbar { + width: 0px; + height: 0px; +} + +body { + min-height: 100vh; +} + +.text-color { + @apply text-veryDarkBlue-2 dark:text-white; +} + +.bg-color { + @apply bg-veryLightGray dark:bg-veryDarkBlue-1; +} + +.bg-color-component { + @apply bg-white dark:bg-darkBlue; +} + +.padding-x { + @apply px-[20px] md:px-[80px]; +} diff --git a/src/main.jsx b/src/main.jsx new file mode 100644 index 0000000..5504950 --- /dev/null +++ b/src/main.jsx @@ -0,0 +1,14 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App"; +import "./index.css"; +import { Provider } from "react-redux"; +import { store } from './app/index' + +ReactDOM.createRoot(document.getElementById("root")).render( + + + + + +); diff --git a/src/pages/Country.jsx b/src/pages/Country.jsx new file mode 100644 index 0000000..2813f33 --- /dev/null +++ b/src/pages/Country.jsx @@ -0,0 +1,20 @@ +import React, { useState } from 'react'; +import Cards from '../components/Cards'; +import Search from '../components/Search'; +import { useSearchParams } from 'react-router-dom'; + +const Country = () => { + const [searchParams, setSearchParams] = useSearchParams(); + const [text, setText] = useState(); + + return ( +
    +
    + +
    + +
    + ); +}; + +export default Country; diff --git a/src/pages/CountryDetail.jsx b/src/pages/CountryDetail.jsx new file mode 100644 index 0000000..90dd431 --- /dev/null +++ b/src/pages/CountryDetail.jsx @@ -0,0 +1,116 @@ +import React from 'react'; +import { useGetBorderCountriesQuery, useGetCountryDetailQuery } from '../app/apiSlices/api'; +import { Link, useNavigate, useParams } from 'react-router-dom'; +import ArrowLeft from '../assets/ArrowLeft'; +import Loading from '../components/Loading.jsx'; +import NotFound from '../components/NotFound'; + +function CountryDetail() { + const { id = '' } = useParams(); + const { data, isError, isLoading } = useGetCountryDetailQuery(id); + const { data: borderData, isLoading: isBorderLoading, isError: isBorderError } = useGetBorderCountriesQuery(data?.[0]?.borders?.join(','), { skip: !data?.[0]?.borders }); + const navigate = useNavigate(); + + if (isLoading || isBorderLoading) { + return ; + } + if (isError || isBorderError) { + return navigate('/')} />; + } + if (data && data.length) { + const { + flags: { svg }, + name: { common, nativeName }, + population, + independent, + region, + subregion, + maps, + capital, + tld, + currencies, + languages, + } = data[0] || { flags: {}, name: {} }; + + return ( +
    + +
    +
    + {`${common} +
    +
    +

    {common}

    +
    +
    +
    + Native Name:{' '} + + {Object.values(nativeName || {}) + ?.map(nName => nName?.common) + ?.join(', ')} + +
    +
    + Population: + {population?.toLocaleString()} +
    +
    + Region: + {region} +
    +
    + Sub Region: {subregion} +
    +
    + Capital: {capital} +
    +
    +
    +
    + Top Level Domain: {tld} +
    +
    + Currencies:{' '} + + {Object.values(currencies || {}) + ?.map(currency => currency?.name) + ?.join(', ')} + +
    +
    + Languages: {Object.values(languages || {})?.join(', ')} +
    +
    + Independent: {independent ? 'Self Governing' : 'Non Self Governing'} +
    +
    + + View on map + +
    +
    +
    + {borderData && ( +
    +
    Border Countries:
    + {borderData.map(country => ( + + {country?.name?.common} + + ))} +
    + )} +
    +
    +
    + ); + } + + return <>; +} + +export default CountryDetail; diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx new file mode 100644 index 0000000..0f98959 --- /dev/null +++ b/src/pages/Home.jsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { Outlet } from 'react-router-dom'; +import Navbar from '../components/navbar.jsx'; + +const Home = () => { + return ( + <> + + + + ); +}; + +export default Home; diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..9182e9b --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,19 @@ +/** @type {import('tailwindcss').Config} */ + +export const content = ['./src/**/*.{js,jsx,ts,tsx}']; +export const darkMode = 'class'; +export const theme = { + extend: { + colors: { + darkBlue: 'hsl(209, 23%, 22%)', + 'veryDarkBlue-1': 'hsl(207, 26%, 17%)', + 'veryDarkBlue-2': 'hsl(200, 15%, 8%)', + darkGray: 'hsl(0, 0%, 52%)', + veryLightGray: 'hsl(0, 0%, 98%)', + white: 'hsl(0, 0%, 100%)', + }, + fontFamily: { nunito: ['Nunito Sans', 'sans-serif'] }, + }, + fontWeight: { light: '300', semiBold: '600', extraBold: '800' }, +}; +export const plugins = []; diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..3a48e56 --- /dev/null +++ b/vercel.json @@ -0,0 +1,3 @@ +{ + "rewrites": [{ "source": "/(.*)", "destination": "/" }] +} diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..7244dae --- /dev/null +++ b/vite.config.js @@ -0,0 +1,5 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +// https://vitejs.dev/config/ +export default defineConfig({ plugins: [react()] });