diff --git a/.vscode/settings.json b/.vscode/settings.json index f7a55df12..e2e5d8e80 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,6 +5,9 @@ "editor.detectIndentation": false, "editor.tabSize": 2, "cSpell.words": [ + "Cramer", + "linebreak", + "plusplus", "tabindex" ] } \ No newline at end of file diff --git a/homework/App.js b/homework/App.js index 5f81a47a1..c38143143 100644 --- a/homework/App.js +++ b/homework/App.js @@ -72,7 +72,7 @@ class App { * @param {Error} error An Error object describing the error. */ renderError(error) { - console.log(error); // TODO: replace with your own code + throw new Error(error); // TODO: replace with your own code } } diff --git a/homework/index.js b/homework/index.js index d8a04f271..44393a060 100644 --- a/homework/index.js +++ b/homework/index.js @@ -1,47 +1,153 @@ -'use strict'; - -{ - function fetchJSON(url, cb) { - const xhr = new XMLHttpRequest(); - xhr.open('GET', url); - xhr.responseType = 'json'; - xhr.onload = () => { - if (xhr.status < 400) { - cb(null, xhr.response); - } else { - cb(new Error(`Network error: ${xhr.status} - ${xhr.statusText}`)); - } - }; - xhr.onerror = () => cb(new Error('Network request failed')); - xhr.send(); - } +const mainDiv = document.body; +function createElement(parentElement, element, nameId) { + const newElement = document.createElement(element); + newElement.setAttribute('id', nameId); + parentElement.appendChild(newElement); + return newElement; +} - function createAndAppend(name, parent, options = {}) { - const elem = document.createElement(name); - parent.appendChild(elem); - Object.keys(options).forEach(key => { - const value = options[key]; - if (key === 'text') { - elem.textContent = value; - } else { - elem.setAttribute(key, value); - } - }); - return elem; - } +const fetchRepositories = async url => { + const response = await fetch(url); + const dataList = await response.json(); + return dataList; +}; + +const createOption = (item, index) => { + const newOption = document.createElement('option'); + const repositoriesSelect = document.getElementById('repositoriesSelect'); + newOption.className = 'repositoryOption'; + newOption.text = item.name; + newOption.value = index; + repositoriesSelect.appendChild(newOption); +}; + +function appendToLi(indexLi, element, nameIdP1, textNodeP1, nameIdP2, textNodeP2) { + const p1 = createElement(indexLi, element, nameIdP1); + const contentP1 = document.createTextNode(textNodeP1); + p1.appendChild(contentP1); + const p2 = createElement(indexLi, element, nameIdP2); + const contentP2 = document.createTextNode(textNodeP2); + p2.appendChild(contentP2); +} - function main(url) { - fetchJSON(url, (err, data) => { - const root = document.getElementById('root'); - if (err) { - createAndAppend('div', root, { text: err.message, class: 'alert-error' }); - } else { - createAndAppend('pre', root, { text: JSON.stringify(data, null, 2) }); - } - }); +function createList(parentElement, element, lio, li1, li2, li3, aUrl) { + for (let y = 0; y < 4; y++) { + createElement(parentElement, element, `li${y}`); } + const liTags = document.getElementsByTagName('li'); + const p0 = createElement(liTags[0], 'p', 'repository'); + const contentP0 = document.createTextNode('Repository:'); + p0.appendChild(contentP0); + const aElement = createElement(liTags[0], 'a', 'repositoryValue'); + aElement.href = aUrl; + const aContent = document.createTextNode(lio); + aElement.appendChild(aContent); + appendToLi(liTags[1], 'p', 'description', 'Description:', 'descriptionValue', li1); + appendToLi(liTags[2], 'p', 'forks', 'Forks:', 'forksValue', li2); + appendToLi(liTags[3], 'p', 'updated', 'Updated:', 'updatedValue', li3); +} + +function handleContributors(contributors) { + const rightDiv = document.getElementById('rightDiv'); + rightDiv.innerHTML = ''; + const contributorsTitle = createElement(rightDiv, 'div', 'contributorsTitle'); + const contributorsTitleContent = document.createTextNode('Contributors'); + contributorsTitle.appendChild(contributorsTitleContent); + contributors.forEach(contributor => { + // create subDiv for each contributor + const subDiv = document.createElement('div'); + subDiv.className = 'contributor'; + rightDiv.appendChild(subDiv); + const image = document.createElement('img'); + image.className = 'image'; + image.setAttribute('src', contributor.avatar_url); + subDiv.appendChild(image); + const login = createElement(subDiv, 'p', 'login'); + const loginContent = document.createTextNode(contributor.login); + login.appendChild(loginContent); + const contributions = createElement(subDiv, 'p', 'contributions'); + const contributionsContent = document.createTextNode(contributor.contributions); + contributions.appendChild(contributionsContent); + }); +} - const REPOS_URL = 'https://api.github.com/orgs/foocoding/repos?per_page=100'; +const logContributors = async url => { + try { + const contributorsData = await fetchRepositories(url); + const FooCodingContributors = contributorsData.sort((a, b) => + a.login.localeCompare(b.login, 'fr', { ignorePunctuation: true }), + ); + handleContributors(FooCodingContributors); + } catch (error) { + const errorDiv = createElement(mainDiv, 'div', 'errorDiv'); + const errorContent = document.createTextNode('error'); + errorDiv.appendChild(errorContent); + } +}; - window.onload = () => main(REPOS_URL); +function createPage(repositories) { + // create upperDiv + const upperDiv = createElement(mainDiv, 'div', 'upperDiv'); + const upperDivP = createElement(upperDiv, 'p', 'upperDivP'); + const upperDivPContent = document.createTextNode('FooCoding Repositories'); + upperDivP.appendChild(upperDivPContent); + const select = createElement(upperDiv, 'select', 'repositoriesSelect'); + repositories.forEach(createOption); + // create leftRightDivDiv + const leftRightDiv = createElement(mainDiv, 'div', 'leftRightDiv'); + // create leftDiv + const leftDiv = createElement(leftRightDiv, 'div', 'leftDiv'); + const repositoryDetails = createElement(leftDiv, 'ul', 'repositoryDetails'); + const defaultRepository = repositories[0]; + const updated = defaultRepository.updated_at; + const formatUpdated = updated.replace(/T/, ', ').replace(/Z/, ''); + createList( + repositoryDetails, + 'li', + defaultRepository.name, + defaultRepository.description, + defaultRepository.forks, + formatUpdated, + defaultRepository.html_url, + ); + // create rightDiv + createElement(leftRightDiv, 'div', 'rightDiv'); + // fetch contributors to implement the rightDiv + const defaultContributorsUrl = defaultRepository.contributors_url; + logContributors(defaultContributorsUrl); + // addEventListener on select change + select.addEventListener('change', () => { + const liElement = document.querySelectorAll('li'); + for (let i = 0; i < liElement.length; i++) { + liElement[i].parentElement.removeChild(liElement[i]); + } + const selectedRepository = repositories[select.value]; + const updatedForSelect = selectedRepository.updated_at; + const formatUpdatedForSelect = updatedForSelect.replace(/T/, ', ').replace(/Z/, ''); + createList( + repositoryDetails, + 'li', + selectedRepository.name, + selectedRepository.description, + selectedRepository.forks, + formatUpdatedForSelect, + ); + const ContributorsUrl = selectedRepository.contributors_url; + logContributors(ContributorsUrl); + }); } + +const logData = async url => { + try { + const repositoriesData = await fetchRepositories(url); + const FooCodingRepositories = repositoriesData.sort((a, b) => + a.name.localeCompare(b.name, 'fr', { ignorePunctuation: true }), + ); + createPage(FooCodingRepositories); + } catch (error) { + const errorDiv = createElement(mainDiv, 'div', 'errorDiv'); + const errorContent = document.createTextNode('error'); + errorDiv.appendChild(errorContent); + } +}; +logData('https://api.github.com/orgs/foocoding/repos?per_page=100'); diff --git a/homework/style.css b/homework/style.css index a8985a8a5..cfcb5c955 100644 --- a/homework/style.css +++ b/homework/style.css @@ -1,3 +1,129 @@ -.alert-error { - color: red; -} \ No newline at end of file +body { + max-width: 768px; + width: 100%; + margin-top: 0px; + margin-right: auto; + margin-left: auto; + background-color: #d3d3d3; + font-family: 'Roboto', sans-serif; + text-align: left; + color: rgb(0, 0, 0, 87%); +} +#upperDiv { + display: flex; + width: 93%; + margin-left: 3%; + margin-right: 4%; + background-color: #0072ce; +} +#upperDivP { + color: white; + width: 40%; + padding-left: 2%; + font-size: 10px; +} +#repositoriesSelect { + width: 40%; + border-radius: 4px; + font-size: 9px; +} +#leftRightDiv { + display: flex; + font-size: 8px; +} +#leftDiv { + margin-top: 2%; + margin-left: 3%; + margin-right: 3%; + width: 45%; + height: 45%; + background-color: white; +} +#rightDiv { + width: 45%; + margin-top: 2%; + background-color: white; +} +ul { + margin-right: 4%; +} +#li0, +#li1, +#li2, +#li3 { + display: flex; +} +#repository, +#description, +#forks, +#updated { + width: 30%; + margin-top: 1%; + margin-left: 0%; + font-weight: bold; +} +#repositoryValue, +#descriptionValue, +#forksValue, +#updatedValue { + width: 70%; + margin-top: 1%; + padding-left: 6%; + padding-right: 6%; +} +#contributorsTitle { + margin-left: 10%; + font-weight: bold; + color: #a9a9a9; + margin-top: auto; + padding-top: 4%; + padding-bottom: 4%; +} +.contributor { + display: flex; + margin-top: auto; + border-bottom: 0.3px solid #a9a9a9; + padding-top: 4%; + padding-bottom: 4%; +} +.contributor > .image { + width: 20%; + height: 20%; + margin-left: 10%; + border-radius: 4px; +} +#login { + width: 30%; + margin-top: 15%; + margin-left: 3%; + font-weight: bold; +} +#contributions { + width: 8%; + height: 14%; + margin-top: 15%; + margin-left: 20%; + background-color: rgb(169, 169, 169); + text-align: center; + border-radius: 4px; + font-weight: bold; + color: white; +} +@media (min-width: 769px) and (max-width: 1024px) { + #leftRightDiv { + font-size: 12px; + } +} +@media (min-width: 1025px) and (max-width: 1280px) { + #upperDivP { + width: 30%; + font-size: 14px; + } + #repositoriesSelect { + width: 22%; + font-size: 12px; + } + #leftRightDiv { + font-size: 14px; + } +}