diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..606ad8fc --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +#API KEY +code/token.js + +# Ignore node_modules folder +node_modules + +# Ignore files related to API keys +.env \ No newline at end of file diff --git a/README.md b/README.md index 1613a3b0..fd8dfe0c 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,11 @@ # 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. +The brief was to create a tracker over all of our forked technigo projects from Github, as well as a chart showing our current progress. ## 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? +I first tried the API and played around to try to find the different data I would need, and where I would find the links to the rest of the fetches I needed. After feeling comfortable with where I could find the different information I needed, I began setting up my javascript. I didn't spend much time on design this week, and I struggled a bit with the chart.js documentation, but in the end I was very satisfied with the solutions I came up with. I also spent some time debugging a few small issues I had and refactoring my code to make it more readable. ## 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. +https://github-tracker-week7.netlify.app diff --git a/code/android-chrome-192x192.png b/code/android-chrome-192x192.png new file mode 100644 index 00000000..5ec76f5e Binary files /dev/null and b/code/android-chrome-192x192.png differ diff --git a/code/android-chrome-512x512.png b/code/android-chrome-512x512.png new file mode 100644 index 00000000..1a59e368 Binary files /dev/null and b/code/android-chrome-512x512.png differ diff --git a/code/apple-touch-icon.png b/code/apple-touch-icon.png new file mode 100644 index 00000000..8d2b0c28 Binary files /dev/null and b/code/apple-touch-icon.png differ diff --git a/code/chart.js b/code/chart.js index 92e85a30..f6b7ba3e 100644 --- a/code/chart.js +++ b/code/chart.js @@ -1,4 +1,49 @@ -//DOM-selector for the canvas 👇 -const ctx = document.getElementById('chart').getContext('2d') +// Global Font Family for Chart +Chart.defaults.font.family = 'Roboto Mono, monospace'; -//"Draw" the chart here 👇 +//DOM-selector +const ctx = document.getElementById('myChart') + +//Drawing the doughnut chart +const completedProjects = (complete) => { + const data = { + labels: [ + `Completed Projects`, + `Incomplete Projects`, + ], + // Pulls complete from script.js + datasets: [{ + data: [complete, (19 - complete)], + backgroundColor: [ + '#2d2e2f', + '#DCD8DC' + ], + hoverOffset: 8, + hoverBorderColor: [ + '#FFFFFF' + ] + }] + }; + + const config = { + type: 'doughnut', + data: data, + options: { + plugins: { + legend: { + position: 'top', + labels: { + font: { + size: 16, + }, + } + }, + } + } + }; + + const myChart = new Chart( + ctx, + config, + ); +} diff --git a/code/chart2.js b/code/chart2.js new file mode 100644 index 00000000..0546b850 --- /dev/null +++ b/code/chart2.js @@ -0,0 +1,235 @@ +//DOM-selector for sprint chart +const sprint = document.getElementById('sprintChart') + +// Date Consts (In Epoch - Milliseconds) +const feb21 = 1645401600000 +const feb28 = 1646006400000 +const mar7 = 1646611200000 +const mar14 = 1647216000000 +const mar21 = 1647820800000 +const mar28 = 1648425600000 +const apr4 = 1649030400000 +const apr11 = 1649635200000 +const apr18 = 1650240000000 +const apr25 = 1650844800000 +const may2 = 1651449600000 +const may9 = 1652054400000 +const may16 = 1652659200000 +const may23 = 1653264000000 +const may30 = 1653868800000 +const jun6 = 1654473600000 +const jun13 = 1655078400000 +const jun20 = 1655683200000 + +// Let declarations +let sprint1 +let sprint2 +let sprint3 +let sprint4 +let sprint5 +let sprint6 +let colors = [] +let now = Date.now() + +// Function for determining date & setting sprint data and colors on bar chart +const dataColors = () => { + // If statements for sprints + if (now > feb21 && now < feb28) { + sprint1 = 4; + sprint2 = 3; + sprint3 = 0; + sprint4 = 0; + sprint5 = 0; + sprint6 = 0; + colors = ['#2d2e2f', '#DCD8DC', '#DCD8DC', '#DCD8DC', '#DCD8DC', '#DCD8DC'] + } if (now > feb28 && now < mar7) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 0; + sprint4 = 0; + sprint5 = 0; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#DCD8DC', '#DCD8DC', '#DCD8DC', '#DCD8DC'] + } if (now > mar7 && now < mar14) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 1; + sprint4 = 0; + sprint5 = 0; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#DCD8DC', '#DCD8DC', '#DCD8DC', '#DCD8DC'] + } if (now > mar14 && now < mar21) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 2; + sprint4 = 0; + sprint5 = 0; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#DCD8DC', '#DCD8DC', '#DCD8DC', '#DCD8DC'] + } if (now > mar21 && now < mar28) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 3; + sprint4 = 0; + sprint5 = 0; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#DCD8DC', '#DCD8DC', '#DCD8DC', '#DCD8DC'] + } if (now > mar28 && now < apr4) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 0; + sprint5 = 0; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#DCD8DC', '#DCD8DC', '#DCD8DC'] + } if (now > apr4 && now < apr11) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 1; + sprint5 = 0; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#DCD8DC', '#DCD8DC', '#DCD8DC'] + } if (now > apr11 && now < apr18) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 2; + sprint5 = 0; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#DCD8DC', '#DCD8DC', '#DCD8DC'] + } if (now > apr18 && now < apr25) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 3; + sprint5 = 0; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#DCD8DC', '#DCD8DC', '#DCD8DC'] + } if (now > apr25 && now < may2) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 4; + sprint5 = 0; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#DCD8DC', '#DCD8DC'] + } if (now > may2 && now < may9) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 4; + sprint5 = 1; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#DCD8DC', '#DCD8DC'] + } if (now > may9 && now < may16) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 4; + sprint5 = 2; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#DCD8DC', '#DCD8DC'] + } if (now > may16 && now < may23) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 4; + sprint5 = 3; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#DCD8DC', '#DCD8DC'] + } if (now > may23 && now < may30) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 4; + sprint5 = 4; + sprint6 = 0; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#DCD8DC'] + } if (now > may30 && now < jun6) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 4; + sprint5 = 4; + sprint6 = 1; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#DCD8DC'] + } if (now > jun6 && now < jun13) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 4; + sprint5 = 4; + sprint6 = 2; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#DCD8DC'] + } if (now > jun13 && now < jun20) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 4; + sprint5 = 4; + sprint6 = 3; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#DCD8DC'] + } else if (now > jun20) { + sprint1 = 4; + sprint2 = 4; + sprint3 = 4; + sprint4 = 4; + sprint5 = 4; + sprint6 = 4; + colors = ['#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f', '#2d2e2f'] + } +} + +//Chart function +const sprintProgress = () => { + + // Run dataColors for determining sprint progress & bar colors + dataColors(); + + const data = { + labels: [ + 'Sprint 1', + 'Sprint 2', + 'Sprint 3', + 'Sprint 4', + 'Sprint 5', + 'Sprint 6' + ], + datasets: [{ + data: [sprint1, sprint2, sprint3, sprint4, sprint5, sprint6], + backgroundColor: colors, + hoverOffset: 8, + hoverBorderColor: [ + '#FFFFFF' + ], + }] + }; + + const config = { + type: 'bar', + data: data, + options: { + scales: { + x: { + ticks: { + stepSize: 1, + } + } + }, + indexAxis: 'y', + plugins: { + legend: { + display: false, + }, + } + } + }; + + const sprintChart = new Chart( + sprint, + config, + ); +} + +sprintProgress(); diff --git a/code/favicon-16x16.png b/code/favicon-16x16.png new file mode 100644 index 00000000..d1517b34 Binary files /dev/null and b/code/favicon-16x16.png differ diff --git a/code/favicon-32x32.png b/code/favicon-32x32.png new file mode 100644 index 00000000..8c61291f Binary files /dev/null and b/code/favicon-32x32.png differ diff --git a/code/favicon.ico b/code/favicon.ico new file mode 100644 index 00000000..9a6ce77c Binary files /dev/null and b/code/favicon.ico differ diff --git a/code/index.html b/code/index.html index 2fb5e0ae..b97cc66e 100644 --- a/code/index.html +++ b/code/index.html @@ -3,19 +3,52 @@ - - Project GitHub Tracker + + Technigo Projects GitHub Tracker | michaelchangdk + + + + + + + + + + + + -

GitHub Tracker

-

Projects:

-
- - + +
+ + +
+

Technigo Projects:

+
+ + +
+
+

Bootcamp Progress:

+ + +
+ +
+

Sprint Progress:

+ + +
+
+ + + - + + - \ No newline at end of file + diff --git a/code/script.js b/code/script.js index e69de29b..ee478ec5 100644 --- a/code/script.js +++ b/code/script.js @@ -0,0 +1,215 @@ +//Global DOM Selectors +const header = document.querySelector('header') +const projects = document.getElementById('projects') + +// Github API +const username = 'michaelchangdk' +const GITHUB_API = `https://api.github.com/users/${username}/repos` + +// GITHUB Authentication +const options = { + method: 'GET', + headers: { + Authorization: `token ${API_KEY}` + } +} + +// Function to Sort Projects By Created Date +compareCreateDate = (a, b) => { + if (a.created_at < b.created_at) { + return -1; + } if (a.created_at > b.created_at) { + return 1; + } + return 0; +}; + +// Function for opening and closing the projects items +const openSesame = (projectID) => { + let projectHeader = document.getElementById(projectID); + let projectBody = projectHeader.nextElementSibling; + if (getComputedStyle(projectBody).display === "none") { + projectBody.style.display = "block"; + projectHeader.classList.add("project-header--active") + } else { + projectBody.style.display = "none"; + projectHeader.classList.remove("project-header--active") + } +} + +const getProjects = async () => { + const projectsWait = await fetch(GITHUB_API, options); + const data = await projectsWait.json(); + + // Filter Technigo Projects Only & Sort by Date Created + const technigoProjects = data.filter(item => item.archive_url.includes('project') === true).sort(compareCreateDate); + + // Length of completed projects and sending the number to chart.js + const completedNumber = data.filter(item => item.archive_url.includes('project') === true).length + completedProjects(completedNumber) + + // Create Header Section + header.innerHTML = ` + profile picture +

${technigoProjects[0].owner.login}

+ ` + + // For Loop to Create Main Project Elements || & Fetch Number of Commits as well as Pull Requests + for (let i = 0; i < technigoProjects.length; i++) { + + // Creating Project classname & project ID + let projectName = technigoProjects[i].name.replaceAll("-",""); + let projectID = technigoProjects[i].id + + // Create DIV in the correct sorted order + projects.innerHTML += ` +
+
+ ` + + // Creating the Project Elements + document.getElementById(projectID).innerHTML += ` + +
+

Repo can be found here.

+
+ ` + + // Fetching Commit Data + commitFetch(technigoProjects[i], projectID); + + // Fetching Pull Requests + pullFetch(technigoProjects[i].name, projectID) + } + } + +// Function for Fetching Commits and Live Link +const commitFetch = (projects, projectsID) => { + const GIT_COMMIT_API = projects.commits_url.replace("{/sha}", "") + "?per_page=100" + fetch(GIT_COMMIT_API, options) + .then(res => res.json()) + .then(data => { + + const filteredCommits = [] + + for (let i = 0; i < data.length; i++) { + let author = data[i].commit.author.name + if (author.includes("Chang") === true) { + filteredCommits.push('1') + } + }; + + // Defining number of commits + let numberOfCommits = filteredCommits.length; + + // Last Commit Message - Committed Netlify Link to add to live link. + let lastCommitLink = data[0].commit.message + + // Formatting the DATES + // Last Commit Date which is NOT the Netlify commit (Index 1 instead of 0) + // Formatting DAYS + let lastCommitDateRaw = data[1].commit.committer.date.substring(0,10) + // Removing the 0 from dates before the 10th for DAYS + let lastCommitDayRaw = lastCommitDateRaw.substring(8,10) + let lastCommitDay + if (lastCommitDayRaw < 10) { + lastCommitDay = lastCommitDayRaw.replace("0", "") + } else { + lastCommitDay = lastCommitDayRaw; + } + // Formatting MONTHS + const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] + let lastCommitMonthNumber + if (lastCommitDateRaw.substring(5,7) !== 10) { + lastCommitMonthNumber = lastCommitDateRaw.substring(5,7).replace("0","") + } else { + lastCommitMonthNumber = lastCommitDateRaw.substring(5,7) + } + let lastCommitMonth = months[lastCommitMonthNumber - 1] + // Extracting YEAR + let lastCommitYear = lastCommitDateRaw.substring(0,4) + + let commitDate = `${lastCommitMonth} ${lastCommitDay}, ${lastCommitYear}` + + // Creating the Project Elements + document.getElementById(`${projectsID}2`).innerHTML += ` +

Number of commits: ${numberOfCommits}

+

Last commit date: ${commitDate}

+ ` + + // If statement to check if last commit includes netlify link + if (lastCommitLink.includes('https://') === true) { + document.getElementById(`${projectsID}2`).innerHTML += ` +

View it live here. + ` + } else { + document.getElementById(`${projectsID}2`).innerHTML += ` +

No live link available.

+ ` + } + }) +} + +// Fetching the pull requests +const pullFetch = (projectName, projectsID) => { + const GIT_FETCH_API = `https://api.github.com/repos/technigo/${projectName}/pulls?per_page=100` + fetch(GIT_FETCH_API, options) + .then(res => res.json()) + .then(data => { + + // Filter Pull Requests by my username + const filteredPR = data.filter(function (pr) { + return pr.user.login === username + }) + + // Check if there is a pull request with my username + let exist = false + for (let i = 0; i < data.length; i++) { + if (data[i].user.login === username) { + exist = true + } + } + + // Conditional for pull request innerHTML + if (exist === true) { + document.getElementById(`${projectsID}2`).innerHTML += ` +

Pull request with comments here.

+ ` + } if (exist === false) { + document.getElementById(`${projectsID}2`).innerHTML += ` +

Pull request unavailable.

+ ` + } + + // Fetch Comments - Commented out because I didn't want them, but left it here to show how to do it + // pullComments(filteredPR, projectsID); + + }) +} + +// Fetch Comments - Commented out because I didn't want them, but left it here to show how to do it +// const pullComments = (pullRequests, projectsID) => { +// for (let i = 0; i < pullRequests.length; i++) { +// fetch(pullRequests[0].review_comments_url, options) +// .then(res => res.json()) +// .then(data => { +// document.getElementById(`${projectsID}2`).innerHTML += ` +//
+//

Review comments:

+// ` +// for (let i = 0; i < data.length; i++) { +// if (data[i].user.login !== username) { +// document.getElementById(`${projectsID}2`).innerHTML += ` +// +// ` +// } +// } +// }) +// } +// } + +getProjects(); diff --git a/code/site.webmanifest b/code/site.webmanifest new file mode 100644 index 00000000..45dc8a20 --- /dev/null +++ b/code/site.webmanifest @@ -0,0 +1 @@ +{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/code/style.css b/code/style.css index 7c8ad447..ac80f9be 100644 --- a/code/style.css +++ b/code/style.css @@ -1,3 +1,246 @@ +/* Remove Pre-set Styling */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + text-decoration: none; +} + +/* Overall Styling */ body { - background: #FFECE9; -} \ No newline at end of file + background: white; + font-family: 'Roboto Mono', monospace; + padding: 7vmin; +} + +a { + color: black; + text-decoration: underline; +} + +p { + line-height: 1.5; +} + +/* Elements Across Sections */ +.title { + background-color: #2D2E2F; + padding: 15px 0 20px; + color: white; + text-align: center; + border-radius: 5px; + overflow: hidden; +} + +.layout { + max-width: 500px; + margin-left: auto; + margin-right: auto; +} + +/* Header Section with Avatar & Username */ +header { + display: flex; + align-items: center; + gap: 2vmin; + justify-content: center; + margin-bottom: 5vmin; +} + +.avatar { + border-radius: 50%; + height: 40px; + margin-top: 1vmin; +} + +/* Projects Section */ +.project-wrapper { + margin: 5vmin 0; + border-radius: 5px; + overflow: hidden; +} + +.project-header { + color: #2d2e2f; + background-color: #DCD8DC; + font-family: 'Roboto Mono', monospace; + font-weight: 700; + font-size: 18px; + text-align: left; + padding: 10px 25px; + width: 100%; + border: none; + outline: none; +} + +.project-info { + color: #2d2e2f; + background-color: #bfb8b9; + font-family: 'Montserrat', sans-serif; + font-size: 16px; + font-weight: 500; + line-height: 1.5; + padding: 10px 25px; + display: none; +} + +/* Adding background color to button when clicked on and when moused over */ +.project-header:hover { + background-color: #726a6a; + color: #DCD8DC; + cursor: pointer; +} + +/* Add .active in JS */ +.project-header--active { + background-color: #726a6a; + color: #DCD8DC; + cursor: pointer; + border-radius: 0; +} + +/* Chart Styling */ +.chart-container { + margin-bottom: 5vmin; +} + +.chart { + margin-top: 5vmin; +} + + +/* Media Queries */ + +/* Media Query - Tablet */ +@media (min-width: 668px) and (max-width: 930px) { + body { + display: grid; + grid-template-columns: 2; + grid-template-rows: 3; + gap: 2vmin; + padding: 5vw; + } + + header { + grid-column: 1 / span 2; + grid-row: 1 / span 1; + margin-bottom: 1vw; + margin-top: -1vw; + } + + .layout { + max-width: 44vw; + } + + .project-wrapper { + margin: 2vmin 0; + } + + .projects { + grid-column: 2 / span 1; + grid-row: 2 / span 2; + margin-left: 0; + } + + .charts-wrapper { + grid-column: 1 / span 1; + grid-row: 2 / span 2; + margin-right: 0; + } + + .chart { + margin-top: 2vmin; + } +} + +/* Media Query - Desktop */ +@media (min-width: 930px) { + body { + display: grid; + grid-template-columns: 2; + grid-template-rows: 2; + gap: 2vmin; + padding: 3vw; + } + + .layout { + width: 44vw; + max-width: 500px; + } + + header { + grid-column: 1 / span 2; + grid-row: 1 / span 1; + margin-bottom: 1vw; + margin-top: -1vw; + } + + .projects { + margin-left: 0; + } + + .project-wrapper { + margin: 2vmin 0; + } + + .chart { + margin-top: 2vh; + } + + .charts-wrapper { + grid-column: 1 / span 1; + grid-row: 2 / span 2; + margin-right: 0; + max-width: 400px; + } +} + +/* Media Query - Mobile Landscape */ +@media (min-width: 640px) and (max-height: 420px) and (orientation: landscape) { + body { + display: grid; + grid-template-columns: 2; + grid-template-rows: 2; + gap: 3vw; + padding: 3vw; + } + + .layout { + width: 44vw; + max-width: 500px; + /* padding-left: env(safe-area-inset-left); + padding-right: env(safe-area-inset-right); */ + } + + header { + grid-column: 1 / span 2; + grid-row: 1 / span 1; + margin-bottom: 2vmin; + } + + .projects { + margin-left: 0; + max-width: 450px; + padding-right: env(safe-area-inset-right); + } + + .project-wrapper { + margin: 2vmin 0; + } + + .chart { + margin-top: 2vh; + } + + .charts-wrapper { + grid-column: 1 / span 1; + grid-row: 2 / span 2; + margin-right: 0; + max-width: 400px; + padding-left: env(safe-area-inset-left); + } +}