diff --git a/README.md b/README.md index 1613a3b0..89cbefb6 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,16 @@ # GitHub Tracker -Replace this readme with your own information about your project. - -Start by briefly describing the assignment in a sentence or two. Keep it short and to the point. +This week's assignment is about exploring more about API and JavaScript. +We are suppose to create a GitHub Tracker, to keep track of our repos that we have created at Technigo. +Throughout the Technigo boot camp (24 weeks) we will be doing 19 different projects. ## The problem -Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next? +When reading this week's project, I did not really know how to start with this assignment. It was a link to GitHub's own documentation and some example of different Endpoints we can use. +We didn't get any particular design or style to get inspired by. So it took some time to think and plan on how to structure the data we get from GitHub. But it was quite nice to try to practice designing yourself without some standard requirements. +The documentation from GitHub was massive! So it was easier to Google and apparently many had the same problems as I did. It helped a lot, especially Stack Overflow. +If I had more time I would want to tackle the higher level requirements such as adding a search field or try to sort the repos. ## View it live -Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about. +Netlify link: https://katiewu1-githubtracker.netlify.app/ diff --git a/code/assets/Octocat.png b/code/assets/Octocat.png new file mode 100644 index 00000000..91057da4 Binary files /dev/null and b/code/assets/Octocat.png differ diff --git a/code/assets/bg-image.jpg b/code/assets/bg-image.jpg new file mode 100644 index 00000000..669d1852 Binary files /dev/null and b/code/assets/bg-image.jpg differ diff --git a/code/assets/email.png b/code/assets/email.png new file mode 100644 index 00000000..6e410e3a Binary files /dev/null and b/code/assets/email.png differ diff --git a/code/assets/favicon.png b/code/assets/favicon.png new file mode 100644 index 00000000..07deb6d2 Binary files /dev/null and b/code/assets/favicon.png differ diff --git a/code/assets/github.png b/code/assets/github.png new file mode 100644 index 00000000..d0a89ff8 Binary files /dev/null and b/code/assets/github.png differ diff --git a/code/assets/linkedin.png b/code/assets/linkedin.png new file mode 100644 index 00000000..4067bdd7 Binary files /dev/null and b/code/assets/linkedin.png differ diff --git a/code/assets/loading.gif b/code/assets/loading.gif new file mode 100644 index 00000000..7bdae668 Binary files /dev/null and b/code/assets/loading.gif differ diff --git a/code/chart.js b/code/chart.js index 92e85a30..3e235d80 100644 --- a/code/chart.js +++ b/code/chart.js @@ -1,4 +1,84 @@ -//DOM-selector for the canvas 👇 -const ctx = document.getElementById('chart').getContext('2d') +const ctx = document.getElementById('progressChart').getContext('2d'); -//"Draw" the chart here 👇 +// function to draw the Progress Chart, we have passed in amount of repos as an argument +const drawProgressChart = (amountOfRepos) => { + const data = { + labels: ['Finished Projects', 'Unfinished Projects'], + datasets: [ + { + label: 'Technigo boot camp projects', + data: [amountOfRepos, 19 - amountOfRepos], + // length of the repo array, total 19 projects minus length of the repo array + backgroundColor: ['rgb(255, 99, 132)', 'rgb(255, 205, 86)'], + hoverOffset: 4, + }, + ], + }; + const config = { + type: 'doughnut', + data: data, + options: { + plugins: { + legend: { + labels: { + color: 'white', + font: { + size: 12, + }, + }, + }, + }, + }, + }; + const progressChart = new Chart(ctx, config); +}; + +// function to draw the Language Chart with three arguments +const drawLanguagesChart = (html_percent, css_percent, js_percent) => { + // put the variable for the second canvas here because + // the id="languageChart" hasn't been created yet because of innerHTML in script.js + // if we put it at start of the chart.js, we wouldn't know what languageChart is + const ctx_languages = document.getElementById('languagesChart'); + const data_2 = { + labels: ['HTML', 'CSS', 'Javascript'], + datasets: [ + { + label: 'Language use (percentage)', + data: [html_percent, css_percent, js_percent], + backgroundColor: [ + 'rgb(255, 159, 64)', + 'rgb(128, 0, 128)', + 'rgb(255, 205, 86)', + ], + borderColor: [ + 'rgba(255, 159, 64, 0.2)', + 'rgb(128, 0, 128, 0.2)', + 'rgba(255, 205, 86, 0.2)', + ], + borderWidth: 2, + }, + ], + }; + + const stackedBar = new Chart(ctx_languages, { + type: 'bar', + data: data_2, + options: { + scales: { + y: { + beginAtZero: true, + }, + }, + plugins: { + legend: { + labels: { + color: 'black', + font: { + size: 12, + }, + }, + }, + }, + }, + }); +}; diff --git a/code/index.html b/code/index.html index 2fb5e0ae..098c2378 100644 --- a/code/index.html +++ b/code/index.html @@ -1,21 +1,59 @@ - - - - - Project GitHub Tracker - - - -

GitHub Tracker

-

Projects:

-
+ + + + + Katie Wu - GitHub Tracker + + + + + + + +
+ cover image of plants +

+ octocat GitHub + Tracker +

+
+
- - +
- - - - \ No newline at end of file +
+

Repositories

+
+ +
+ +

Technigo boot camp projects

+
+ +
+ + + + + + + + diff --git a/code/script.js b/code/script.js index e69de29b..7337db18 100644 --- a/code/script.js +++ b/code/script.js @@ -0,0 +1,242 @@ +const username = 'katiewu1'; +const API_REPOS = `https://api.github.com/users/${username}/repos`; +const API_USER = `https://api.github.com/users/${username}`; + +const projectContainer = document.getElementById('projectContainer'); +const profile = document.getElementById('profile'); +const modalContent = document.getElementById('modalContent'); +const closeBtn = document.getElementById('closeBtn'); +const modalProject = document.getElementById('modalProject'); + +// function to get the repositories +const getRepos = () => { + fetch(API_REPOS) + .then((response) => { + return response.json(); + }) + .then((json) => { + // filter out the repos that have been forked + // in 'json[index].fork' we can find the boolean (true/false) + // we want all the repo with boolean true + + // we also want to filter and find the projects from Technigo + // the repos from Technigo always start with 'project-' + const filteredTechnigoRepos = json.filter( + (repo) => repo.fork && repo.name.includes('project-') + ); + + // store the project names in a new array called namesOfTheRepos + const namesOfTheRepos = filteredTechnigoRepos.map((repo) => repo.name); + + // for each project, create a button element and class project-card + // inside each card display the project name and a emoji + // with append we push each project card + // and add an eventListener so we can click on each card and get a modal(popup) + namesOfTheRepos.forEach((projectName) => { + const projectCard = document.createElement('button'); + projectCard.classList.add('project-card'); + projectCard.innerHTML = `${projectName}
👆
`; + projectContainer.append(projectCard); + projectCard.addEventListener('click', () => getModal(projectName)); + }); + + // invoke a function to draw a chart, where we can see finished and unfinished project throughout the boot camp + // the function is declared in chart.js + drawProgressChart(filteredTechnigoRepos.length); + }); +}; + +// function to get information about the GitHub user +const getUserInfo = () => { + fetch(API_USER) + .then((response) => { + return response.json(); + }) + .then((json) => { + // display profile image, user and contact info + profile.innerHTML += ` +
+ + + + + + + + + + + + + +
Username:${json.login}
Name:${json.name}
Location:${json.location}
+

Bio: ${json.bio}

+
+
+ profile image +
+ + linkedin icon + + + github icon + + + email icon + +
+
+ + `; + }); +}; + +// function to get a modal(popup) +const getModal = (projectName) => { + // put a loading gif so the user know that it's fetching the data that will present in the modal + // there is some delay because we are fetching new data when switching project card + modalContent.innerHTML = ` + loading gif + `; + + modalProject.style.display = 'block'; // pop up the modal when clicking on a project card + document.body.style.overflow = 'hidden'; // disable the scrolling on the main page + + fetch(`https://api.github.com/repos/${username}/${projectName}`) + .then((response) => { + return response.json(); + }) + .then((json) => { + // get the latest push (date and time) + const pushDateTime = new Date(json.pushed_at); + + // a function where we put an 0 before each number that is < 10 + const addZero = (i) => { + if (i < 10) { + i = '0' + i; + } + return i; + }; + + const getPullRequest = (projectName) => { + // fetch(`https://api.github.com/repos/technigo/${projectName}/pulls`) + // couldn't fetch all the pull requests because of the max limit at 30 + // I used the search method instead and type in the project name and the username + // use a 'return' fetch to give a promise, so it will wait for the fetch before moving on to the next step + return fetch( + `https://api.github.com/search/issues?q=repo:Technigo/${projectName}+author:${username}+type:pr` + ) + .then((response) => { + return response.json(); + }) + .then((json) => { + // if we get no PR from the fetch (the user didn't do a PR) then display some message + if (json.items.length === 0) { + return [`found no pull request by ${username}`, '0']; + } + // do a new fetch when we know the PR number so we can get the count of commit messages + return fetch( + `https://api.github.com/repos/Technigo/${projectName}/pulls/${json.items[0].number}/commits` + ) + .then((response) => { + return response.json(); + }) + .then((data) => { + // store the length of the data, because it's equal to amount of commit messages + const commitLength = data.length; + // if we get a fetch, return the PR url and commitLength + const pullRequestUrl = json.items[0].pull_request.html_url; + return [pullRequestUrl, commitLength]; + // } + }); + }); + }; + + // function to get a chart of language use of each repo + const getLanguagesChart = (projectName) => { + fetch( + `https://api.github.com/repos/${username}/${projectName}/languages` + ) + .then((response) => { + return response.json(); + }) + .then((json) => { + // if we have a value in json.'language' then the value will be store in a variable + // if we don't have a value then the variable will get a value of 0 + const html = json.HTML || 0; + const css = json.CSS || 0; + const js = json.JavaScript || 0; + + // get the sum of all values so we can divide with the total to get a percentage + const total = html + css + js; + + // get the percentage of each language and with one decimal + const html_percent = ((html / total) * 100).toFixed(1); + const css_percent = ((css / total) * 100).toFixed(1); + const js_percent = ((js / total) * 100).toFixed(1); + + // draw the language chart with three arguments. The function is declared in chart.js + drawLanguagesChart(html_percent, css_percent, js_percent); + }); + }; + + // invoke function getPullRequest and it will return a promise + // wait until the fetch is done and 'then' use the pullRequestUrl and commitLength we get + getPullRequest(projectName).then(([pullRequestUrl, commitLength]) => { + // the loading gif will be replaced by the below innerHTML + modalContent.innerHTML = ` +
${projectName}
+ + + + + +
+ +
+ `; + // invoke the function getLanguageChart + getLanguagesChart(projectName); + }); + }); +}; + +// these two functions will be invoked in the beginning +getRepos(); +getUserInfo(); + +const closeModal = () => { + modalProject.style.display = 'none'; //close the modal + document.body.style.overflow = 'auto'; //enable the scrolling on the main page +}; + +// can close the modal with close button and when clicking outside of the modal content +closeBtn.addEventListener('click', () => closeModal()); +modalProject.addEventListener('click', () => closeModal()); +// added an EventListener for esc-key so we can close the modal with esc-key too +document.body.addEventListener('keydown', (event) => { + if (event.key === 'Escape') { + return closeModal(); + } +}); + +// prevent closing the modal when clicking inside of the modal content +modalContent.onclick = (event) => { + event.stopPropagation(); +}; diff --git a/code/style.css b/code/style.css index 7c8ad447..bccf0181 100644 --- a/code/style.css +++ b/code/style.css @@ -1,3 +1,335 @@ body { - background: #FFECE9; -} \ No newline at end of file + margin: 10px; + background-color: black; + font-family: 'Itim', cursive; + color: aliceblue; +} + +.background-image { + position: fixed; + top: 0; + left: 0; + object-fit: cover; + width: 100%; + height: 100%; + opacity: 0.4; + z-index: -1; /* keep the image behind the content */ +} + +.octacat { + width: 18px; + height: 18px; + background-color: white; + border-radius: 50%; + padding: 2px; + margin: 0 6px 0 10px; +} + +.header-title { + position: relative; + font-size: 18px; + display: flex; +} + +.profile { + width: 90%; + margin: auto; + display: grid; + grid-template-columns: 1fr 1fr; +} + +.profile-info { + grid-column: span 1; + font-size: 12px; + width: 100%; + height: 100%; +} + +table { + width: 100%; +} + +td { + width: 50%; +} + +.profile-image-icons { + grid-column: span 1; + width: 90%; + height: 100%; + display: flex; + flex-direction: column; + align-items: center; +} + +.profile-image { + border: 1px solid #ccc; + border-radius: 50%; + width: 74px; + height: 74px; + margin-bottom: 10px; + filter: drop-shadow(8px 8px 16px #ccc); + transform: scale(1); + transition: 0.3s ease-in-out; +} + +.profile-image:hover { + cursor: pointer; + transform: scale(1.5); +} + +.profile-image:active { + filter: grayscale(1); +} + +.contact-icons { + border-radius: 5px; + background-color: #ccc; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; +} + +.icon { + padding: 4px; +} + +.icon:hover { + width: 26px; + height: 26px; +} + +a { + text-decoration: none; + color: #b76501; +} + +a:visited { + color: #b76501; +} + +.projects { + width: 90%; + margin: auto; +} + +h3 { + font-size: 16px; + text-transform: uppercase; + text-align: center; +} + +.project-container { + display: grid; + grid-template-columns: 1fr 1fr; + grid-gap: 10px; +} + +/* all project-card is a button so have to style them again */ +.project-card { + color: aliceblue; + background: transparent; + font-family: 'Itim', cursive; + border: 2px solid white; + border-radius: 5px; + grid-column: span 2; + font-size: 16px; + padding: 20px 0 20px 20px; + display: flex; + flex-direction: row; + justify-content: space-between; +} + +/* in order to able to focus on the cards, +used buttons instead of divs when creating the cards */ +.project-card:hover, +.project-card:focus { + background: black; + opacity: 0.7; +} + +.card-button:focus { + background: black; + opacity: 0.7; +} + +.project-emoji { + margin-right: 18px; +} + +.modal-project { + display: none; /* hidden by default */ + position: fixed; /* stay in place */ + z-index: 1; /* sit on top */ + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; /* enable scroll if needed */ + background-color: rgba(0, 0, 0, 0.5); /* Black with opacity 0.5 */ +} + +.modal-content { + background-color: #fee7d7; + color: black; + width: 70%; + border: 2px solid #b76501; + border-radius: 5px; + margin: 20% auto; + padding: 34px; + font-size: 14px; +} + +.modal-div { + margin: 6px; +} + +.project-name { + font-size: 24px; + margin-bottom: 14px; + text-align: center; + color: #b76501; +} + +/****** the Close Button ******/ +.close-btn { + color: #b76501; + background: #fee7d7; + border: 2px solid #b76501; + border-radius: 5px; + font-size: 32px; + font-weight: bolder; + position: absolute; + top: 10px; + right: 20px; +} + +.close-btn:hover, +.close-btn:focus { + /* when hovering or focusing on the close button */ + color: black; + text-decoration: none; + cursor: pointer; +} + +.progress-chart { + width: 200px; + height: 200px; + margin: 10px auto; +} + +.languages-chart { + width: 90%; + height: 90%; + margin-top: 20px; +} + +/****** GIF ******/ +.loading-gif { + width: 100%; + height: 50%; + margin: 0 auto; /* center */ +} + +footer { + font-size: 10px; + margin: 20px 0 5px 10px; +} + +footer p { + margin: 0; +} + +/* Flaired edges, by Tomas Theunissen */ +hr { + overflow: visible; /* For IE */ + height: 30px; + border-style: solid; + border-color: white; + border-width: 1px 0 0 0; + border-radius: 20px; + /* added margins */ + margin-top: 10px; + margin-bottom: -20px; +} + +hr:before { + display: block; + content: ''; + height: 30px; + margin-top: -31px; + border-style: solid; + border-color: white; + border-width: 0 0 1px 0; + border-radius: 20px; +} + +@media (min-width: 768px) { + .octacat { + width: 32px; + height: 32px; + } + .header-title { + font-size: 32px; + } + + .profile-info { + font-size: 20px; + } + + .profile-image { + width: 134px; + height: 134px; + } + + .icon { + padding: 6px; + width: 30px; + height: 30px; + } + + h3 { + font-size: 26px; + } + + .project-card { + font-size: 22px; + padding: 30px 0 30px 26px; + grid-column: span 1; + } + + .modal-content { + font-size: 20px; + width: 60%; + } + + .project-name { + font-size: 34px; + margin-bottom: 22px; + } + + .close-btn { + top: 30px; + right: 45px; + } + + .progress-chart { + width: 300px; + height: 300px; + margin-top: 32px; + } + + .languages-chart { + margin-top: 26px; + } + + .loading-gif { + width: 100%; + height: 50%; + } + + footer { + font-size: 16px; + } +}