Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 3 additions & 10 deletions details.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,40 +9,33 @@
/>
<meta name="author" content="Mohamed Aridah" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@300;600;800&display=swap"
rel="stylesheet"
/>
<!-- Fontawesome Library -->
<link
rel="stylesheet"
href="https://site-assets.fontawesome.com/releases/v6.0.0/css/all.css"
/>
<!-- Shared CSS Styles File -->
<link rel="stylesheet" href="./css/common.css" />
<!-- Page Custom CSS File -->
<link rel="stylesheet" href="./css/details.css" />
<title>Details Page</title>
</head>
<body>
<!-- Loader -->
<div class="loader">
<div class="spinner">
<i class="fa-regular fa-circle-notch fa-spin icon"></i>
</div>
</div>
<!-- Scroll Top -->
<button
type="button"
aria-label="Scroll Top Button"
class="button scroll-top flex flex-ai-c flex-jc-c scale-effect"
>
<i class="fa-regular fa-chevron-up icon"></i>
</button>
<!-- Header -->
<header class="header">
<div class="container flex flex-jc-sb flex-ai-c">
<div class="logo">
Expand All @@ -60,7 +53,6 @@ <h1>Where in the world?</h1>
</button>
</div>
</header>
<!-- Back Button -->
<div class="back-button">
<div class="container">
<a href="index.html" class="btn btn-back btn-with-icon">
Expand All @@ -74,7 +66,8 @@ <h1>Where in the world?</h1>
<section class="country-details"></section>
</div>
</main>
<script src="./js/common.js"></script>
<script src="./js/details.js"></script>
<!-- <script src="./js/common.js"></script>
<script src="./js/details.js"></script> -->
<script src="script.js"></script>
</body>
Copy link
Member

Choose a reason for hiding this comment

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

Delete code dont comment it out

</html>
81 changes: 2 additions & 79 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,28 @@
/>
<meta name="author" content="Mohamed Aridah" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@300;600;800&display=swap"
rel="stylesheet"
/>
<!-- Fontawesome Library -->
<link
rel="stylesheet"
href="https://site-assets.fontawesome.com/releases/v6.0.0/css/all.css"
/>
<!-- Shared CSS Styles File -->
<link rel="stylesheet" href="./css/common.css" />
<!-- Page Custom CSS File -->
<link rel="stylesheet" href="./css/main.css" />
<title>Countries Info</title>
</head>
<body>
<!-- Scroll Top -->
<button
type="button"
aria-label="Scroll Top Button"
class="button scroll-top flex flex-ai-c flex-jc-c scale-effect"
>
<i class="fa-regular fa-chevron-up icon"></i>
</button>
<!-- Header -->
<header class="header">
<div class="container flex flex-jc-sb flex-ai-c">
<div class="logo">
Expand All @@ -54,7 +48,6 @@ <h1>Where in the world?</h1>
</button>
</div>
</header>
<!-- Filters -->

<section class="filters">
<div class="container">
Expand All @@ -72,87 +65,17 @@ <h1>Where in the world?</h1>
<i class="fa-regular fa-chevron-down icon"></i>
</div>
<div class="dropdown-body">
<ul>
<li data-region="all">All</li>
<li data-region="africa">Africa</li>
<li data-region="americas">America</li>
<li data-region="asia">Asia</li>
<li data-region="europe">Europe</li>
<li data-region="oceania">Oceania</li>
</ul>
</div>
</div>
</div>
</section>
<!-- Countries -->

<main class="main">
<div class="container">
<section class="countries-grid">
<a
href="#"
class="country scale-effect"
data-country-name="Afghanistan"
>
<div class="country-flag">
<img
src="https://upload.wikimedia.org/wikipedia/commons/5/5c/Flag_of_the_Taliban.svg"
alt="Afghanistan FLag"
/>
</div>
<div class="country-info">
<h2 class="country-title">Afghanistan</h2>
<ul class="country-brief">
<li><strong>population: </strong>40218234</li>
<li><strong>Region: </strong>Asia</li>
<li><strong>capital: </strong>Kabul</li>
</ul>
</div>
</a>
<a
href="#"
class="country scale-effect"
data-country-name="Åland Islands"
>
<div class="country-flag">
<img src="https://flagcdn.com/ax.svg" alt="Åland Islands FLag" />
</div>
<div class="country-info">
<h2 class="country-title">Åland Islands</h2>
<ul class="country-brief">
<li><strong>population: </strong>28875</li>
<li><strong>Region: </strong>Europe</li>
<li><strong>capital: </strong>Mariehamn</li>
</ul>
</div>
</a>
<a href="#" class="country scale-effect" data-country-name="Aruba">
<div class="country-flag">
<img src="https://flagcdn.com/aw.svg" alt="Aruba FLag" />
</div>
<div class="country-info">
<h2 class="country-title">Aruba</h2>
<ul class="country-brief">
<li><strong>population: </strong>106766</li>
<li><strong>Region: </strong>Americas</li>
<li><strong>capital: </strong>Oranjestad</li>
</ul>
</div>
</a>
<a href="#" class="country scale-effect" data-country-name="Belize">
<div class="country-flag">
<img src="https://flagcdn.com/bz.svg" alt="Belize FLag" />
</div>
<div class="country-info">
<h2 class="country-title">Belize</h2>
<ul class="country-brief">
<li><strong>population: </strong>397621</li>
<li><strong>Region: </strong>Americas</li>
<li><strong>capital: </strong>Belmopan</li>
</ul>
</div>
</a>
</section>
</div>
</main>
<script src="script.js"></script>
</body>
</html>
167 changes: 167 additions & 0 deletions script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
const countriesGrid = document.querySelector('.countries-grid');
let countryFetchedData = null;

async function fetchCountriesData() {
if (countryFetchedData) {
return;
}
try {
Copy link
Member

Choose a reason for hiding this comment

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

  1. you can write this like if (countryFetchedData) return

  2. This function should do 1 thing and that fetch the data I recommend writing your logic before calling the function

const response = await fetch('./CountriesData.json');
if (!response.ok) {
Copy link
Member

Choose a reason for hiding this comment

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

Also you can save your strings inside constants file or variable and not use them as hardcoded values

throw `Failed to fetch countries data with status - ${response.status}`;
}
countryFetchedData = await response.json();
} catch (error) {
console.error('Error fetching countries data:', error);
}
}

function getDropdownsRegions(data) {
const dropdownRegionFilter = document.querySelector('.dropdown-body');
const uniqueRegionsSet = new Set(data.map(country => country.region));
const uniqueRegions = [...uniqueRegionsSet]
Copy link
Member

Choose a reason for hiding this comment

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

In here you can destruct your object, it is generally a good practice:

const uniqueRegionsSet = new Set(data.map(({ region }) => region));


const dropdownList = document.createElement('ul');
const allOption = document.createElement('li');
allOption.setAttribute('data-region', 'all');
allOption.textContent = 'All';
dropdownList.appendChild(allOption);

uniqueRegions.forEach(region => {
const dropdownItem = document.createElement('li');
dropdownItem.setAttribute('data-region', region);
dropdownItem.textContent = region;
dropdownList.appendChild(dropdownItem);
});
dropdownRegionFilter.appendChild(dropdownList);
}

function getSearchdata() {
const searchInput = document.querySelector('.search-input');
searchInput.addEventListener('input', (e) => {
searchValue = e.target.value;
const filteredData = filterDataSearch(countryFetchedData, searchValue);
countriesGrid.innerHTML = '';
analyzeMainContainer(filteredData);
});
}

Copy link
Member

Choose a reason for hiding this comment

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

  • Never use innetHTMl, its a dangerous method

  • The name of the function does not reflect what the function is actually doing (adding event listener is not getting data). so please separate your code into helper functions

function filterDataSearch(data, searchValue) {
return data.filter(({ name }) => name.toLowerCase().includes(searchValue.toLowerCase()));
}

function analyzeMainContainer(data) {
data.forEach(({ name, flag, population, region, capital }) => {
const countryCard = document.createElement('a');
countryCard.href = './details.html';
countryCard.addEventListener('click', function () {
Copy link
Member

Choose a reason for hiding this comment

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

Same thing here about constants

const countryData = { name, flag, population, region, capital };
localStorage.setItem('countryDetails', JSON.stringify(countryData));
});

Copy link
Member

Choose a reason for hiding this comment

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

Instead of using localStorage like this, create a service that expose method related to localStorage.

This will help you avoid repeat your code and seperate different concepts

countryCard.className = 'country scale-effect';
countryCard.setAttribute('data-country-name', name);

countryCard.innerHTML = `
<div class="country-flag">
<img src="${flag}" alt="${name} Flag">
Copy link
Member

Choose a reason for hiding this comment

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

Same comment in here - do not use innerHTMl

</div>
<div class="country-info">
<h2 class="country-title">${name}</h2>
<ul class="country-brief">
<li><strong>Population: </strong>${population}</li>
<li><strong>Region: </strong>${region}</li>
<li><strong>Capital: </strong>${capital}</li>
</ul>
</div>`;

countriesGrid.appendChild(countryCard);
});
}

function handleDropdownClick() {
const dropdownWrapper = document.querySelector('.dropdown-wrapper');
dropdownWrapper.addEventListener('click', function () {
this.classList.toggle('open');
});

const dropdownItems = dropdownWrapper.querySelector('.dropdown-body ul');
dropdownItems.addEventListener('click', function (e) {
e.stopPropagation();
const selectedRegion = e.target.getAttribute('data-region');
if (selectedRegion) {
dropdownWrapper.classList.remove('open');
getCountriesByRegion(selectedRegion);
}
});
}

function getCountriesByRegion(region) {
let selectedDropdoenOrigin;
if (region === 'all') {
selectedDropdoenOrigin = countryFetchedData;
} else {
selectedDropdoenOrigin = countryFetchedData.filter(country => country.region === region);
}
countriesGrid.innerHTML = '';
analyzeMainContainer(selectedDropdoenOrigin);
}

async function displayData() {
await fetchCountriesData();
if (countryFetchedData) {
analyzeMainContainer(countryFetchedData);
Copy link
Member

Choose a reason for hiding this comment

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

You can use guard clause for early return making the code cleaner :

async function displayData() {
    await fetchCountriesData();

    if (!countryFetchedData) {
        console.error('Error displaying countries data');
        return;
    }

    analyzeMainContainer(countryFetchedData);
    getDropdownsRegions(countryFetchedData);
    getSearchdata();
    handleDropdownClick();
}

getDropdownsRegions(countryFetchedData);
getSearchdata();
handleDropdownClick();
}
else {
console.error('Error display countries data');
}
}
displayData();

function displayCountryDetails() {
const countryInfo = document.querySelector('.country-details');
const countryData = JSON.parse(localStorage.getItem('countryDetails'));

Copy link
Member

Choose a reason for hiding this comment

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

This for example could be a function coming from localsessionService and this is why this is a good practice. In your approach you will have to write that parsing logic again and again

if (countryData) {
const { name, flag, population, region, capital } = countryData;
const countryDetailsHTML = `
<div class="country-flag">
<img src="${flag}" alt="${name} Flag">
</div>
<div class="country-info">
<h1 class="country-title">${name}</h1>
<ul class="country-brief">
<li><strong>Population: </strong>${population}</li>
<li><strong>Region: </strong>${region}</li>
<li><strong>Capital: </strong>${capital}</li>
</ul>
</div>`;

countryInfo.innerHTML = countryDetailsHTML;
} else {
countryInfo.innerHTML = '<h1 class="country-info">No country selected</h1>';
}
}

function handleLoaderSpinner() {
const loaderSpinner = document.querySelector('.loader');
window.addEventListener('load', () => {
setTimeout(() => {
loaderSpinner.style.display = 'none';
}, 700);
});
}

function handleSwitchThemeMode() {
const themeSwitcherButton = document.querySelector('.theme-toggle');
themeSwitcherButton.addEventListener('click', function () {
document.body.classList.toggle('dark-theme');
});
}

handleSwitchThemeMode();
displayCountryDetails();
handleLoaderSpinner();