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
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -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.
A GitHub clone website to practice REST API with vanilla Javascript.

## 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?
The biggest challenge was rate limit boundary even though the request header contains authentication token. In case of exceeding rate limit boundary, the website will display an error message and ask users to visit after an hour. The issue is caused by sending too many requests when page is loading and this was not avoidable because the website requires data from different paths. So for the display purpose, the deployed website is based on a dummy data.

## 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-clone-2022.netlify.app
4 changes: 4 additions & 0 deletions code/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"presets": ["@babel/env"],
"plugins": ["@babel/plugin-transform-runtime"]
}
5 changes: 5 additions & 0 deletions code/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
build
.env
dummyData
.netlify
Binary file added code/assets/dummy.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions code/assets/icons/arrow-down.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions code/assets/icons/bars-solid.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions code/assets/icons/bell-regular.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions code/assets/icons/checked.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions code/assets/icons/code-commit-solid.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions code/assets/icons/code-fork-solid.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions code/assets/icons/code-pull-request-solid.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added code/assets/icons/favicon.ico
Binary file not shown.
1 change: 1 addition & 0 deletions code/assets/icons/github-brands.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 0 additions & 4 deletions code/chart.js

This file was deleted.

40 changes: 40 additions & 0 deletions code/components/filter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { createElement } from "../utility/createElem";
import FilterButtonComp from "./filterButton";
export default function FilterComp() {
const filterContainer = createElement("div", "filter-container");
const filterForm = createElement("form", "filter-form");
const filterFormInput = createElement("input", "filter-input");
filterFormInput.setAttribute("type", "text");
filterFormInput.setAttribute("placeholder", "Find a repository..");
filterForm.appendChild(filterFormInput);

const sortByLanguage = ["javascript", "typescript", "html", "css"];
const sortBy = ["last updated", "name"];

const filterByLanguage = FilterButtonComp(
"Language",
"languageSelector",
"programing-language",
"language-item",
sortByLanguage
);
const filterByDate = FilterButtonComp(
"Sort",
"sortSelector",
"sort-by",
"sort-item",
sortBy
);

filterForm.appendChild(filterByLanguage);
filterForm.appendChild(filterByDate);

filterForm.addEventListener("submit", (e) => {
e.preventDefault();
console.log("filter form submitted");
filterForm.reset();
});

filterContainer.appendChild(filterForm);
return filterContainer;
}
89 changes: 89 additions & 0 deletions code/components/filterButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import arrowButton from "../assets/icons/arrow-down.svg";
import checkedIcon from "../assets/icons/checked.svg";
import { createElement } from "../utility/createElem";
import { capitalizeFirstLetter } from "../utility/capitalizeFirst";

export default function FilterButtonComp(
buttonName,
filterId,
filterTypeClassname,
listItemClassname,
filterLists
) {
const filterBtnContainer = createElement("div", "filter-btn-container");
const filterBtn = createElement("button", "filter-btn");
filterBtn.setAttribute("type", "button");

filterBtn.id = "languageBtn";
filterBtn.innerHTML = `
<span> ${buttonName} </span>
<img
src=${arrowButton}
alt="checked icon"
class="arrow-icon"
/>`;

const filterBtnPositioner = createElement("div", "filter-btn-positioner");
const filterSelectorLists = createElement("ul", filterTypeClassname);
filterSelectorLists.id = filterId;

const filterTitleContainer = createElement("li", listItemClassname);
filterTitleContainer.classList.add("filter-title");

const filterTitle = createElement("span", "span-title");
filterTitle.innerText = "Select type";

const closeBtn = createElement("button", "close-btn");
closeBtn.setAttribute("type", "button");
closeBtn.innerHTML = "&times;";

filterTitleContainer.appendChild(filterTitle);
filterTitleContainer.appendChild(closeBtn);
filterSelectorLists.appendChild(filterTitleContainer);

const listAttributes = filterLists;
listAttributes.forEach((item) => {
const li = createList(listItemClassname, item);
filterSelectorLists.appendChild(li);
});
filterBtnPositioner.appendChild(filterSelectorLists);

filterBtnContainer.appendChild(filterBtn);
filterBtnContainer.appendChild(filterBtnPositioner);

// addEventlisteners
filterSelectorLists.addEventListener("click", (e) => {
if (e.target.nodeName !== "BUTTON" && e.target.nodeName !== "LI") {
return;
}
if (e.target.id !== "") {
console.log("filter by", e.target.id);
}
filterSelectorLists.classList.remove("active");
});

filterBtn.onclick = function displaySelection() {
const sortBtn = document.querySelector("#sortSelector");
const languageBtn = document.querySelector("#languageSelector");
if (
sortBtn.className.includes("active") ||
languageBtn.className.includes("active")
) {
sortBtn.classList.remove("active");
languageBtn.classList.remove("active");
} else {
filterSelectorLists.classList.add("active");
}
};
return filterBtnContainer;
}

function createList(classname, text) {
const listItem = createElement("li", classname);
listItem.id = text.replace(/\s/g, "");
listItem.innerHTML = `
<img class="filter-check-icon" src=${checkedIcon} alt="checked icon" />
${capitalizeFirstLetter(text)}
`;
return listItem;
}
72 changes: 72 additions & 0 deletions code/components/header.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import notificationIconSrc from "../assets/icons/bell-regular.svg";
import githugLogoSrc from "../assets/icons/github-brands.svg";
import { createElement } from "../utility/createElem";

export default function HeaderComp(userProfileImage) {
const header = createElement("header", "header");
//[todo] unable to change color in svg when it is in img tag. update it later
const barIcon = `
<svg
class="expandable-icon"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 448 512"
>
<!--! Font Awesome Pro 6.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. -->
<path
fill="currentColor"
class="expandable-icon-path"
d="M0 96C0 78.33 14.33 64 32 64H416C433.7 64 448 78.33 448 96C448 113.7 433.7 128 416 128H32C14.33 128 0 113.7 0 96zM0 256C0 238.3 14.33 224 32 224H416C433.7 224 448 238.3 448 256C448 273.7 433.7 288 416 288H32C14.33 288 0 273.7 0 256zM416 448H32C14.33 448 0 433.7 0 416C0 398.3 14.33 384 32 384H416C433.7 384 448 398.3 448 416C448 433.7 433.7 448 416 448z"
/>
</svg>
`;

const headerContainer = createElement("div", "header-container");

const githubLogo = createElement("img", "logo");
githubLogo.src = githugLogoSrc;

const searchForm = createElement("form", "search-container");
searchForm.innerHTML = `
<input
type="text"
class="search"
placeholder="Search or jump into.."
/><button type="submit" class="search-btn">&#47;</button>
`;
searchForm.onsubmit = (e) => {
e.preventDefault();
//[todo] update function
console.log("search form is called");
searchForm.reset();
};

const nav = createElement("nav", "nav");
const ul = createElement("ul", "nav-list");
ul.innerHTML = `
<li class="nav-item">Pull Requests</li>
<li class="nav-item">Explore</li>
`;
nav.appendChild(ul);

const githubLogoMobile = createElement("img", "logo-mobile");
githubLogoMobile.src = githugLogoSrc;

headerContainer.appendChild(githubLogo);
headerContainer.appendChild(searchForm);
headerContainer.appendChild(nav);

const userProfileCorner = createElement("img", "profile-thumbnail-corner");
userProfileCorner.src = userProfileImage;

const notificationIcon = createElement("img", "notification");
notificationIcon.src = notificationIconSrc;

//attach by order
header.innerHTML = barIcon;
header.appendChild(headerContainer);
header.appendChild(githubLogoMobile);
header.appendChild(userProfileCorner);
header.appendChild(notificationIcon);

return header;
}
21 changes: 21 additions & 0 deletions code/components/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createElement } from "../utility/createElem";
import UserProfileComp from "./userProfile.js";
import RepositoryComp from "./repository";

export default function MainComp(
userProfileImage,
userId,
name,
githubLink,
technigoRepoData
) {
const main = createElement("main", "main");
main.id = "projects";

const userInfo = UserProfileComp(userProfileImage, userId, name, githubLink);
const repository = RepositoryComp(technigoRepoData);

main.appendChild(userInfo);
main.appendChild(repository);
return main;
}
113 changes: 113 additions & 0 deletions code/components/repoCard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { createElement } from "../utility/createElem";
import { capitalizeFirstLetter } from "../utility/capitalizeFirst";
import commitIcon from "../assets/icons/code-commit-solid.svg";
import forkedIcon from "../assets/icons/code-fork-solid.svg";
import pullIcon from "../assets/icons/code-pull-request-solid.svg";

export default function RepoCardComp(
title,
forkedFrom,
forksURL,
languageIdentity,
forkedCount,
lastUpdate,
branch,
repoURL,
allCommitCount,
latestCommitAuthor,
latestCommitMessage,
latestCommitUrl,
pullRequestTitle,
pullRequestMessage,
pullRequestUpdatedAt,
pullRequestURL
) {
const li = createElement("li", "repository-card");

const titleContainer = createElement("div", "repository-title-container");
const aElem = createElement("a", "link-to-repo");
aElem.setAttribute("href", repoURL);
const cardTitle = createElement("h2", "card-title");
cardTitle.innerText = title;
aElem.appendChild(cardTitle);
titleContainer.appendChild(aElem);

const forkedOrigin = createElement("div", "card-forked-branch");
forkedOrigin.innerHTML = `
<p class="card-forked-from">Forked from <a class="go-to-forked-origin" href="${forksURL}">${forkedFrom}</a></p>
<span class="branch">Branch: ${branch}</span>
`;

const spec = createElement("article", "card-spec");

const languageSpec = createElement("div", "spec-container");
const languageId = languageIdentity || "unknown";
languageSpec.innerHTML = `
<div class="language-identification ${languageId.toLowerCase()}"></div>
<span class="language">${capitalizeFirstLetter(languageId)}</span>
`;

const forkedContent = `<img class="forked-icon" src=${forkedIcon} alt="forked icon"/>${forkedCount}`;
const forked = specItem(forkedContent, "language");

const lastUpdated = specItem(displayLastUpdatedDate(lastUpdate), "date");
const commitCountContent = `<img class="commit-icon" src=${commitIcon} alt="commit icon"/> ${allCommitCount}`;
const commitCount = specItem(commitCountContent, "commit-count");
const commitAuthor = specItem(latestCommitAuthor, "commit-author");

const commitMessage = createElement("div", "spec-container");
commitMessage.innerHTML = `
<a href=${latestCommitUrl} class="go-to-commit">
<span class="commit-message">${substractMessage(
latestCommitMessage,
27
)}</span>
</a>
`;

spec.appendChild(languageSpec);
spec.appendChild(forked);
spec.appendChild(lastUpdated);
spec.appendChild(commitCount);
spec.appendChild(commitAuthor);
spec.appendChild(commitMessage);

if (pullRequestURL) {
const pullContainer = createElement("div", "spec-container");
pullContainer.classList.add("pull-container");
pullContainer.innerHTML = `
<img src="${pullIcon}" alt="pull request icon" class="pull-icon"/>
<a href=${pullRequestURL} class="go-to-pull-request">${pullRequestTitle}</a>
`;
spec.appendChild(pullContainer);
}

li.appendChild(titleContainer);
li.appendChild(forkedOrigin);
li.appendChild(spec);

return li;
}

function displayLastUpdatedDate(commitDate) {
const today = new Date().getTime();
const commitdate = new Date(commitDate).getTime();
const datediff = today - commitdate;
const lastCommited = Math.round(datediff / (24 * 60 * 60 * 1000));
return lastCommited === 0
? "Updated today"
: `Updated ${lastCommited} days ago`;
}

function specItem(innerElementContent, innerElementClass) {
const specContainer = createElement("div", "spec-container");
const innerElement = createElement("span", innerElementClass);
innerElement.innerHTML = innerElementContent;
specContainer.appendChild(innerElement);
return specContainer;
}

function substractMessage(message, messageLimit) {
const substracted = message.slice(0, messageLimit);
return message.length > messageLimit ? `${substracted} ...` : message;
}
Loading