From cdcb7e624f65b139386130c3fe76e5e59a3a6b4e Mon Sep 17 00:00:00 2001 From: Safeya-Yasien Date: Sat, 2 Aug 2025 14:31:07 +0300 Subject: [PATCH 1/5] - Moved CSS to style.css - Removed duplicate imports --- css/style.css | 215 ++++++++++++++++++++++++++++++++++++++++++++------ index.html | 198 +++++----------------------------------------- 2 files changed, 208 insertions(+), 205 deletions(-) diff --git a/css/style.css b/css/style.css index 41db137d..1584e4d6 100644 --- a/css/style.css +++ b/css/style.css @@ -1,14 +1,176 @@ +body { + background-color: rgba(250, 251, 254, 1); +} + +.IssueLabel { + height: 20px; + padding: 0.15em 6px; + font-size: 14px; + font-weight: 600; + line-height: 15px; + border-radius: 2px; + box-shadow: inset 0 -1px 0 rgba(27, 31, 35, 0.12); +} + +.GoodFirstIssue { + background-color: #7057ff; + color: #ffffff; +} + +.StarterTask { + background-color: #62dbc9; + color: #000000; +} + +.HelpWanted { + background-color: #008672; + color: #ffffff; +} +.search-body { + background-color: #ffffff; + box-shadow: 0 0 35px 0 rgba(154, 161, 171, 0.15); + border: 1px solid rgba(154, 161, 171, 0.15); +} + +.spinner { + display: none; + position: absolute; + left: 50%; + z-index: 1; + width: 120px; + height: 120px; + margin: -75px 0 0 -75px; + border: 16px solid #f3f3f3; + border-radius: 50%; + border-top: 16px solid #7057ff; + -webkit-animation: spin 2s linear infinite; + animation: spin 2s linear infinite; +} + +@-webkit-keyframes spin { + 0% { + -webkit-transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + } +} + +.btn-primary { + color: #fff; + background-color: #0073e6; + border-color: #0073e6; +} + +.btn { + display: inline-block; + font-weight: 400; + line-height: 1.25; + text-align: center; + white-space: nowrap; + vertical-align: middle; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + border: 1px solid transparent; + padding: 0.5rem 1rem; + font-size: 1rem; + border-radius: 0.25rem; +} + +.welcome { + margin-bottom: 2rem; + margin-top: 1.5rem; + font-family: "Lato", sans-serif; +} + +.hidden { + display: none; +} + +.welcome { +} + +.welcome h1 { + font-weight: 700; +} + +.lead { + margin-bottom: 0.775rem !important; +} + +.form-header { + margin-left: 15px; + margin-top: 0.75rem; + margin-bottom: 2rem; +} + +.TopLabel { + margin-top: 8px; + margin-left: 8px; +} + +.custom-select { + display: inline-block; + max-width: 100%; + height: calc(2.25rem + 2px); + padding: 0.375rem 1.75rem 0.375rem 0.75rem; + line-height: 1.25; + color: #464a4c; + vertical-align: middle; + background: #fff + url( + data:image/svg + xml;charset=utf8, + %3Csvgxmlns="http://www.w3.org/2000/svg"v…"0 0 4 5"%3E%3Cpathfill="%23333"d="M2 0L0 2h4zm0 5L0 3h4z"/%3E%3C/svg%3E + ) + no-repeat right 0.75rem center; + -webkit-background-size: 8px 10px; + background-size: 8px 10px; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 0.25rem; + -moz-appearance: none; + -webkit-appearance: none; +} + +.QuestionsSuggestions { + margin-top: 1rem; + margin-bottom: 1rem; + font-size: 0.8rem; +} + +.text-muted { + color: #636c72; + margin-bottom: 0px; + font-weight: 300; + font-size: 90%; + margin-right: 6px; +} + +.paddedLeftMargin { + margin-left: 25px; +} + +#submit { + margin-left: 25px; + width: 300px; +} + +.sortsTable:hover { + color: #7057ff; + cursor: url(hand.cur), pointer; +} .inline-block { -display: inline; + display: inline; } .table { -background: orange; -width: 90%; -text-align: left; -margin: auto; + background: orange; + width: 90%; + text-align: left; + margin: auto; } .spinner { @@ -29,31 +191,34 @@ margin: auto; animation: spin 2s linear infinite; } - @-webkit-keyframes spin { - 0% { -webkit-transform: rotate(0deg); } - 100% { -webkit-transform: rotate(360deg); } + 0% { + -webkit-transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + } } .btn-primary { - color: #fff; - background-color: #0073e6; - border-color: #0073e6; + color: #fff; + background-color: #0073e6; + border-color: #0073e6; } .btn { - display: inline-block; - font-weight: 400; - line-height: 1.25; - text-align: center; - white-space: nowrap; - vertical-align: middle; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - border: 1px solid transparent; - padding: .5rem 1rem; - font-size: 1rem; - border-radius: .25rem; + display: inline-block; + font-weight: 400; + line-height: 1.25; + text-align: center; + white-space: nowrap; + vertical-align: middle; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + border: 1px solid transparent; + padding: 0.5rem 1rem; + font-size: 1rem; + border-radius: 0.25rem; } diff --git a/index.html b/index.html index 1f6f9d4a..6aa30656 100644 --- a/index.html +++ b/index.html @@ -1,179 +1,19 @@ - - - - - - - - - - -

.com

@@ -190,7 +30,11 @@

Find Great Open Source Opportunities.

Made for new contributors to find great Open Source projects.

-

Discover Issues and Repositories with and labels.

+

Discover Issues and Repositories with + + + +

@@ -254,12 +98,10 @@

Find Great Open Source Opportunities.

- - - - + + - From 2c9204f0e0de088f68537856de2fec7923543d8e Mon Sep 17 00:00:00 2001 From: Safeya-Yasien Date: Sat, 2 Aug 2025 15:07:52 +0300 Subject: [PATCH 3/5] enhance structure --- index.html | 243 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 142 insertions(+), 101 deletions(-) diff --git a/index.html b/index.html index d0ba3c21..b3d916dd 100644 --- a/index.html +++ b/index.html @@ -1,112 +1,153 @@ - + - - + + + + - - + + - - - - - - + + + + +

+ Good First Issue.com +

- -

Good First Issue.com

-
-
-
- Questions? Suggestions? Send me a PM on -
-
-
+
+
+

+ Find Great Open Source Opportunities +

+

+ Made for new contributors to find great Open Source projects. +

+

+ Discover Issues and Repositories with: + Help Wanted + Starter Task + Good First Issue +

+ +
+
- +
+ - -
-
-
-

Find Great Open Source Opportunities.

-

Made for new contributors to find great Open Source projects.

-

- Discover Issues and Repositories with - Help Wanted - Starter Task - Good First Issue -

-
-
-
+
+
+
+
+ + +
+
+
- -
-
-
-
- -
-
- -
-
- - - - - - - - - - - - - - - - -
- -
-
-
- - - +
+
+
+ + + + + + + + + + + + + + + +
+
+ +
+ - - - + + + From 4851813fb8e4a08cacbb4864befefad5f1f65d78 Mon Sep 17 00:00:00 2001 From: Safeya-Yasien Date: Sat, 2 Aug 2025 15:08:01 +0300 Subject: [PATCH 4/5] - enhance styles --- css/style.css | 277 +++++++++++++++++++++----------------------------- 1 file changed, 118 insertions(+), 159 deletions(-) diff --git a/css/style.css b/css/style.css index 1584e4d6..810c9dd6 100644 --- a/css/style.css +++ b/css/style.css @@ -1,224 +1,183 @@ -body { - background-color: rgba(250, 251, 254, 1); -} - -.IssueLabel { - height: 20px; - padding: 0.15em 6px; - font-size: 14px; - font-weight: 600; - line-height: 15px; - border-radius: 2px; - box-shadow: inset 0 -1px 0 rgba(27, 31, 35, 0.12); +/* General Reset */ +* { + box-sizing: border-box; + margin: 0; + padding: 0; } -.GoodFirstIssue { - background-color: #7057ff; - color: #ffffff; -} - -.StarterTask { - background-color: #62dbc9; - color: #000000; +body { + font-family: "Lato", sans-serif; + background-color: #f5f7fa; + color: #333; } -.HelpWanted { - background-color: #008672; - color: #ffffff; +/* Top Label */ +.TopLabel { + font-weight: bold; + font-size: 1.5rem; + margin: 1rem; } +/* Container Layout */ .search-body { - background-color: #ffffff; - box-shadow: 0 0 35px 0 rgba(154, 161, 171, 0.15); - border: 1px solid rgba(154, 161, 171, 0.15); + background-color: #fff; + border-radius: 12px; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.07); + padding: 2rem; + margin: 2rem auto; + max-width: 1100px; } +/* Spinner Styling */ .spinner { display: none; position: absolute; left: 50%; - z-index: 1; - width: 120px; - height: 120px; - margin: -75px 0 0 -75px; - border: 16px solid #f3f3f3; + top: 50%; + z-index: 10; + transform: translate(-50%, -50%); + width: 80px; + height: 80px; + border: 8px solid #f3f3f3; + border-top: 8px solid #7057ff; border-radius: 50%; - border-top: 16px solid #7057ff; - -webkit-animation: spin 2s linear infinite; - animation: spin 2s linear infinite; + animation: spin 1s linear infinite; } -@-webkit-keyframes spin { +@keyframes spin { 0% { - -webkit-transform: rotate(0deg); + transform: rotate(0deg); } 100% { - -webkit-transform: rotate(360deg); + transform: rotate(360deg); } } -.btn-primary { - color: #fff; - background-color: #0073e6; - border-color: #0073e6; -} - -.btn { +/* Issue Label Tags */ +.IssueLabel { + padding: 0.25rem 0.75rem; + font-size: 0.85rem; + font-weight: 600; + border-radius: 4px; display: inline-block; - font-weight: 400; - line-height: 1.25; - text-align: center; - white-space: nowrap; - vertical-align: middle; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - border: 1px solid transparent; - padding: 0.5rem 1rem; - font-size: 1rem; - border-radius: 0.25rem; -} - -.welcome { - margin-bottom: 2rem; - margin-top: 1.5rem; - font-family: "Lato", sans-serif; -} - -.hidden { - display: none; + margin-right: 0.5rem; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); } -.welcome { +.GoodFirstIssue { + background-color: #7057ff; + color: #fff; } -.welcome h1 { - font-weight: 700; +.StarterTask { + background-color: #62dbc9; + color: #000; } -.lead { - margin-bottom: 0.775rem !important; +.HelpWanted { + background-color: #008672; + color: #fff; } +/* Language Form */ .form-header { - margin-left: 15px; - margin-top: 0.75rem; - margin-bottom: 2rem; + margin-bottom: 1.5rem; } -.TopLabel { - margin-top: 8px; - margin-left: 8px; +.LanguageOptions { + min-width: 220px; + border-radius: 6px; + padding: 0.5rem; + border: 1px solid #ccc; } -.custom-select { - display: inline-block; - max-width: 100%; - height: calc(2.25rem + 2px); - padding: 0.375rem 1.75rem 0.375rem 0.75rem; - line-height: 1.25; - color: #464a4c; - vertical-align: middle; - background: #fff - url( - data:image/svg + xml;charset=utf8, - %3Csvgxmlns="http://www.w3.org/2000/svg"v…"0 0 4 5"%3E%3Cpathfill="%23333"d="M2 0L0 2h4zm0 5L0 3h4z"/%3E%3C/svg%3E - ) - no-repeat right 0.75rem center; - -webkit-background-size: 8px 10px; - background-size: 8px 10px; - border: 1px solid rgba(0, 0, 0, 0.15); - border-radius: 0.25rem; - -moz-appearance: none; - -webkit-appearance: none; +/* Table Styling */ +.table-responsive { + overflow-x: auto; } -.QuestionsSuggestions { +.table { + width: 100%; margin-top: 1rem; - margin-bottom: 1rem; - font-size: 0.8rem; -} - -.text-muted { - color: #636c72; - margin-bottom: 0px; - font-weight: 300; - font-size: 90%; - margin-right: 6px; + border-collapse: collapse; + border-radius: 8px; + overflow: hidden; + background-color: #fff; } -.paddedLeftMargin { - margin-left: 25px; +.table th, +.table td { + padding: 1rem; + text-align: left; + border-bottom: 1px solid #f0f0f0; } -#submit { - margin-left: 25px; - width: 300px; +.table thead { + background-color: #f8f9fa; + font-weight: 600; } -.sortsTable:hover { +.table th.sortsTable:hover { color: #7057ff; - cursor: url(hand.cur), pointer; + cursor: pointer; } -.inline-block { - display: inline; +/* Utility Classes */ +.hidden { + display: none !important; } -.table { - background: orange; - width: 90%; - text-align: left; - margin: auto; +.QuestionsSuggestions { + font-size: 0.875rem; + color: #666; + margin: 1.5rem 0; + text-align: center; } -.spinner { - display: none; - position: absolute; - left: 50%; - top: 50%; - z-index: 1; - width: 150px; - height: 150px; - margin: -75px 0 0 -75px; - border: 16px solid #f3f3f3; - border-radius: 50%; - border-top: 16px solid #3498db; - width: 120px; - height: 120px; - -webkit-animation: spin 2s linear infinite; - animation: spin 2s linear infinite; +.QuestionsSuggestions a { + color: #0073e6; + text-decoration: none; } -@-webkit-keyframes spin { - 0% { - -webkit-transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(360deg); - } +.QuestionsSuggestions a:hover { + text-decoration: underline; } +/* Buttons */ .btn-primary { - color: #fff; background-color: #0073e6; border-color: #0073e6; + color: #fff; +} + +.btn-primary:hover { + background-color: #005bb5; + border-color: #005bb5; } .btn { - display: inline-block; - font-weight: 400; - line-height: 1.25; - text-align: center; - white-space: nowrap; - vertical-align: middle; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - border: 1px solid transparent; padding: 0.5rem 1rem; font-size: 1rem; - border-radius: 0.25rem; + border-radius: 0.375rem; +} + +/* Responsive Enhancements */ +@media (max-width: 768px) { + .form-header { + text-align: center; + } + + .LanguageOptions { + width: 100%; + } + + .TopLabel { + font-size: 1.25rem; + text-align: center; + } + + .table th, + .table td { + padding: 0.75rem; + } } From b938bf7cd75d0b17aba44be38819982d4e50e3c9 Mon Sep 17 00:00:00 2001 From: Safeya-Yasien Date: Sat, 2 Aug 2025 15:08:21 +0300 Subject: [PATCH 5/5] move js to separate file --- js/main.js | 430 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 430 insertions(+) create mode 100644 js/main.js diff --git a/js/main.js b/js/main.js new file mode 100644 index 00000000..9476587c --- /dev/null +++ b/js/main.js @@ -0,0 +1,430 @@ +var searchIssuesQuery = ` +query SearchIssues($query:String!, $after: String) { + search(query:$query, type:ISSUE, first: 100, after: $after) { + edges { + node { + ... on Issue { + repository { + owner { + login + }, + updatedAt, + url, + updatedAt, + name, + stargazers { + totalCount + }, + }, + url, + createdAt, + } + } + } + + pageInfo { + endCursor + hasNextPage + } + } +} + +`; + +class RepositoryContainer { + constructor(issue) { + this.owner = issue.repository.owner.login; + this.name = issue.repository.name; + this.starcount = issue.repository.stargazers.totalCount; + this.issues = [issue]; + this.url = issue.repository.url; + this.updatedAt = issue.repository.updatedAt; + } + + addIssue(issue) { + this.issues.push(issue); + } +} + +function sortByGoodFirstIssues(repos) { + function compare(a, b) { + return returnComp( + a.issues.length, + b.issues.length, + a.starcount, + b.starcount + ); + } + + repos.sort(compare); + return repos; +} + +function returnComp(a, b, tiebreaker1, tiebreaker2) { + if (a > b) { + return -1; + } else if (b > a) { + return 1; + } else if (tiebreaker1 > tiebreaker2) { + return -1; + } else if (tiebreaker2 > tiebreaker1) { + return 1; + } + + return 0; +} + +function sortByStars(repos) { + function compare(a, b) { + return returnComp( + a.starcount, + b.starcount, + a.issues.length, + b.issues.length + ); + } + + repos.sort(compare); + return repos; +} + +function sortBy(value, repositories) { + switch (value) { + case "Good First Issue": + return sortByGoodFirstIssues(repositories); + break; + + case "Stars": + return sortByStars(repositories); + break; + } +} + +function addSlashes(string) { + var slash = '"'; + return slash + string + slash; +} + +function makeIssuesQuery(language, label, endCursor) { + return { + query: + "label:" + + addSlashes(label) + + " language:" + + language + + " " + + "state:open", + after: endCursor, + }; +} + +function getHTMLParameters() { + // get language + var langOptions = $(".LanguageOptions")[0]; + var selectedLanguage = langOptions[langOptions.selectedIndex].value; + console.log(selectedLanguage); + // get labels + + return { + language: selectedLanguage, + }; +} + +function daysBetweenDates(date1, date2) { + var oneDay = 24 * 60 * 60 * 1000; // hours*minutes*seconds*milliseconds + var diffDays = Math.round((date2 - date1) / oneDay); + return diffDays; +} + +function afterDateLimit(date, todaysDate, monthLimit) { + var dayLimit = monthLimit * 30; + var daysBtwn = daysBetweenDates(date, todaysDate); + return daysBtwn >= dayLimit; +} + +function isUndefined(object) { + return typeof object === "undefined"; +} + +function createContainers(issues, repoDict, repoNameDict) { + issues.forEach((i) => { + if (isUndefined(i)) { + return; + } + if (isUndefined(i.repository)) { + return; + } + var name = i.repository.name; + + if (isUndefined(repoNameDict[name])) { + repoNameDict[name] = name; + var container = new RepositoryContainer(i); + repoDict[name] = container; + } else { + repoDict[name].addIssue(i); + } + }); + + var arrayvalues = new Array(); + + for (var key in repoDict) { + arrayvalues.push(repoDict[key]); + } + + return arrayvalues; +} + +function createTable(issues) { + console.log("***** create table called ****"); + var repoNameDict = {}; + var repoDict = {}; + var body = document.getElementsByTagName("body")[0]; + var allRepositories = createContainers(issues, repoDict, repoNameDict); + + sortByGoodFirstIssues(allRepositories); + makeTableFromRepos(allRepositories); + return allRepositories; +} + +function makeTableFromRepos(repositories) { + makeTableElement(); + repositories.forEach((i) => { + var row = insertIntoTableElement( + i.owner, + i.name, + i.url, + i.issues.length, + i.starcount, + i.updatedAt + ); + $("table tbody")[0].appendChild(row); + }); +} + +function removeRows(table) { + $("table tbody tr").remove(); +} + +function makeTableElement() { + // create elements and a + var table = $("table")[0]; + table.classList.remove("hidden"); + removeRows(table); +} + +function appendCell(row, text) { + var cell = document.createElement("td"); + var celltext = document.createTextNode(text); + cell.appendChild(celltext); + row.appendChild(cell); +} + +function formatNameCell(repoOwnerLogin, repoName, url, row) { + var cell = document.createElement("td"); + var span = document.createElement("span"); + var a = document.createElement("a"); + var loginText = document.createTextNode(repoOwnerLogin + " / "); + var repoNameText = document.createTextNode(repoName); + + a.appendChild(loginText); + $(a).attr("href", url); + $(a).attr("target", "_blank"); + a.classList.add("ownerLoginText"); + a.appendChild(repoNameText); + + cell.appendChild(a); + row.appendChild(cell); +} + +function formatUpdatedAtDate(updatedAt) { + var date = new Date(updatedAt); + var monthNames = [ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", + ]; + + var day = date.getDate(); + var monthIndex = date.getMonth(); + var year = date.getFullYear(); + + return monthNames[monthIndex] + " " + day + " " + year; +} + +function appendStarCell(row, stars) { + var cell = document.createElement("td"); + var starSVG = starSVGIcon(); + var p1 = "

"; + var p2 = "

"; + var space = " "; + $(cell).html(p1 + starSVG + space + stars + p2); + row.appendChild(cell); +} + +function insertIntoTableElement( + repoOwnerLogin, + repoName, + url, + issueCount, + stars, + updatedAt +) { + var row = document.createElement("tr"); + formatNameCell(repoOwnerLogin, repoName, url, row); + appendCell(row, issueCount); + appendStarCell(row, stars); + appendCell(row, formatUpdatedAtDate(updatedAt)); + return row; +} + +function throwLoading(spinner) { + spinner.css("display", "block"); + console.log("throwing loading spinner"); +} + +function stopLoadingSpinner(spinner, time) { + function stopLoading() { + spinner.css("display", "none"); + } + + console.log(time); + var now = new Date().getTime(); + console.log(now); + var interval = new Date().getTime() - time; + console.log(interval); + spinner.css("display", "none"); +} + +function starSVGIcon() { + return ''; +} + +function onSubmitButton() { + var parameters = getHTMLParameters(); + if (parameters.language == "") { + return; + } + + var time = new Date().getTime(); + var spinner = $(".spinner"); + throwLoading(spinner); + var fetchCount = 0; + var labels = ["easy contribution", "good first issue", "starter-task"]; + var todaysDate = new Date(); + var issuesFetched = []; + var uniqueRepositories = []; + var labelIndex = 0; + + function setupSortMech(repos) { + $(".sortsTable").click(function () { + console.log("seen "); + var value = $(this).attr("value"); + var sortedRepos = sortBy(value, repos); + makeTableFromRepos(sortedRepos); + }); + } + + function finishedFetchingRepos() { + console.log("finished " + issuesFetched.length); + stopLoadingSpinner(spinner, new Date().getTime()); + console.log("finished " + issuesFetched.length); + var repos = createTable(issuesFetched); + setupSortMech(repos); + } + + function makeQuery(endCursor) { + var query = makeIssuesQuery( + parameters.language, + labels[labelIndex], + endCursor + ); + fetchQuery(searchIssuesQuery, query, handleData); + } + + function fetchNextPage(fetchCount, pageInfo) { + // { "query": "label:\"good first issue\" ", "after": "Y3Vyc29yOjg="} + fetchCount += 1; + makeQuery(pageInfo.endCursor); + } + + function fetchNextLabel() { + labelIndex += 1; + if (labelIndex >= labels.length) { + finishedFetchingRepos(); + } else { + makeQuery(); + } + } + + function handleData(data) { + if (data == null || data.data == null) { + fetchNextLabel(); + return; + } + + var issues = data.data.search.edges.map((edge) => { + return edge.node; + }); + console.log(issues.length); + issuesFetched = issuesFetched.concat(issues); + console.log(issuesFetched.length); + var pageInfo = data.data.search.pageInfo; + + var i = 0; + + for (i = issues.length; i >= 0; i--) { + if (isUndefined(issues[i])) { + issues.splice(i, 1); + } + } + + if (issues.length == 0) { + fetchNextLabel(); + } else if ( + pageInfo.hasNextPage && + !afterDateLimit( + new Date(issues[issues.length - 1].createdAt), + todaysDate, + 4 + ) + ) { + fetchNextPage(fetchCount, pageInfo); + } else { + fetchNextLabel(); + } + } + + makeQuery(); +} + +function fetchQuery(query, variables, callback) { + //https://graphql.org/graphql-js/graphql-clients/ + + var body = JSON.stringify({ + query, + variables: variables, + }); + + fetch("https://api.github.com/graphql", { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + Authorization: "Bearer" + " " + token, + }, + body: body, + }) + .then((r) => r.json()) + .then((data) => { + callback(data); + }); +}