diff --git a/assets/css/style.css b/assets/css/style.css index 771fa75..d3ae1a4 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -733,3 +733,28 @@ br { .twitter-contact > img { color: var(--white); } + +#github-full { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; +} + +.gitnamepfp { + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; +} + +#github-full a { + text-decoration: none; + font-weight: 400 !important; + color: var(--text) !important; +} + +#github-full a:hover { + text-decoration: underline; + color: var(--white) !important; +} diff --git a/assets/js/github.js b/assets/js/github.js new file mode 100644 index 0000000..0f08579 --- /dev/null +++ b/assets/js/github.js @@ -0,0 +1,76 @@ +export async function fetchGithubStats(user) { + const response = await fetch(`https://api.github.com/users/${user}`); + if (!response.ok) throw new Error(`Error fetching Github Info: ${response.statusText}`); + return await response.json(); +} + +export async function getTotalStars(username) { + let page = 1; + let totalStars = 0; + let hasMore = true; + + while (hasMore) { + const response = await fetch(`https://api.github.com/users/${username}/repos?per_page=100&page=${page}`); + const repos = await response.json(); + + if (repos.length === 0) break; + totalStars += repos.reduce((sum, repo) => sum + repo.stargazers_count, 0); + hasMore = repos.length === 100; + page++; + } + return totalStars; +} + +export async function writeGithubStats(targetId) { + const data = await fetchGithubStats("zyqunix"); + const stars = await getTotalStars("zyqunix"); + + const target = document.querySelector(targetId); + target.innerHTML = ""; + + const mainEl = document.createElement("div"); + mainEl.classList.add("gitnamepfp"); + + const pfp = document.createElement("img"); + pfp.src = data.avatar_url; + pfp.style.borderRadius = "50%"; + pfp.style.width = "96px"; + + const name = document.createElement("a"); + name.innerText = data.login; + name.href = data.html_url; + name.target = "_blank"; + name.style.fontSize = "20px"; + name.classList.add("tooltip"); + name.setAttribute("data-tooltip", data.bio); + + const pubRepos = document.createElement("a"); + pubRepos.innerText = `${data.public_repos} Public Repositories`; + pubRepos.href = `https://github.com/${data.login}?tab=repos`; + pubRepos.id = "pubrepos"; + + const followers = document.createElement("div"); + followers.innerHTML = `${data.followers} Followers & Following ${data.following}`; + + + const hireable = document.createElement("div"); + if (data.hireable === "null") { + hireable.innerText = "Not Hireable"; + } else { + hireable.innerText = "Hire Me!"; + } + + const tStars = document.createElement("div"); + tStars.innerText = `${stars} Total Stars`; + + const registered = data.created_at; + document.getElementById("gh_since").innerText = `Registed on ${registered.slice(0, 10).replace(/-/g, "/")}`; + + mainEl.appendChild(pfp); + mainEl.appendChild(name); + target.appendChild(mainEl); + target.appendChild(pubRepos); + target.appendChild(followers); + target.appendChild(hireable); + target.appendChild(tStars); +} diff --git a/assets/js/index.js b/assets/js/index.js index f039d7e..3ccb74a 100644 --- a/assets/js/index.js +++ b/assets/js/index.js @@ -1,4 +1,5 @@ import * as wakatime from "./wakatime.js"; +import * as github from "./github.js"; const timeElem = document.getElementById('time'); const timezone = 'Europe/Berlin'; @@ -241,6 +242,7 @@ function fetchWeather(location) { } wakatime.fetchWakatime("#wakapi"); +github.writeGithubStats("#github-full"); const messages = [ "Coding", diff --git a/assets/js/wakatime.js b/assets/js/wakatime.js index 3a31e89..9f9f422 100644 --- a/assets/js/wakatime.js +++ b/assets/js/wakatime.js @@ -57,7 +57,7 @@ const projectColors = [ '#E2F0CB', '#F5E6E8', '#C9D6DF' -]; +]; const categoryColors = { coding: '#A8DADC', @@ -66,274 +66,273 @@ const categoryColors = { }; export async function fetchWakaTimeStats(user, range) { - const response = await fetch(`https://wakapi.atums.world/api/v1/users/${user}/stats/${range}`); - if (!response.ok) throw new Error(`Error fetching WakaTime stats: ${response.statusText}`); - return await response.json(); + const response = await fetch(`https://wakapi.atums.world/api/v1/users/${user}/stats/${range}`); + if (!response.ok) throw new Error(`Error fetching WakaTime stats: ${response.statusText}`); + return await response.json(); } export async function prepareChartData() { - const languages = await fetchWakaTimeStats("zyqunix", "all_time"); - const sortedLanguages = [...languages.data.languages].sort((a, b) => b.percent - a.percent); + const languages = await fetchWakaTimeStats("zyqunix", "all_time"); + const sortedLanguages = [...languages.data.languages].sort((a, b) => b.percent - a.percent); - const totalSeconds = sortedLanguages.reduce((total, lang) => total + lang.total_seconds, 0); - let totalTime = ''; + const totalSeconds = sortedLanguages.reduce((total, lang) => total + lang.total_seconds, 0); + let totalTime = ''; - if (totalSeconds > 3600) { - totalTime = `${Math.floor(totalSeconds / 3600)}h ${Math.floor((totalSeconds % 3600) / 60)}m`; - } else { - totalTime = `${Math.floor(totalSeconds / 60)}m`; - } - - const limit = 10; - const topLanguages = sortedLanguages.slice(0, limit); - - if (sortedLanguages.length > limit) { - const otherSeconds = sortedLanguages.slice(limit).reduce((total, lang) => total + lang.total_seconds, 0); - const otherPercent = sortedLanguages.slice(limit).reduce((total, lang) => total + lang.percent, 0); - - let otherText = ''; - if (otherSeconds > 3600) { - otherText = `${Math.floor(otherSeconds / 3600)}h ${Math.floor((otherSeconds % 3600) / 60)}m`; + if (totalSeconds > 3600) { + totalTime = `${Math.floor(totalSeconds / 3600)}h ${Math.floor((totalSeconds % 3600) / 60)}m`; } else { - otherText = `${Math.floor(otherSeconds / 60)}m`; + totalTime = `${Math.floor(totalSeconds / 60)}m`; } - topLanguages.push({ - name: 'Other', - total_seconds: otherSeconds, - percent: otherPercent, - text: otherText, - color: '#CCCCCC', - digital: '', - hours: Math.floor(otherSeconds / 3600), - minutes: Math.floor((otherSeconds % 3600) / 60), - seconds: otherSeconds % 60 - }); - } + const limit = 10; + const topLanguages = sortedLanguages.slice(0, limit); - const segmentsWithColors = topLanguages.map(lang => ({ - ...lang, - color: lang.color || langColors[lang.name] || '#CCCCCC' - })); + if (sortedLanguages.length > limit) { + const otherSeconds = sortedLanguages.slice(limit).reduce((total, lang) => total + lang.total_seconds, 0); + const otherPercent = sortedLanguages.slice(limit).reduce((total, lang) => total + lang.percent, 0); - return { - segments: segmentsWithColors, - totalTime - }; + let otherText = ''; + if (otherSeconds > 3600) { + otherText = `${Math.floor(otherSeconds / 3600)}h ${Math.floor((otherSeconds % 3600) / 60)}m`; + } else { + otherText = `${Math.floor(otherSeconds / 60)}m`; + } + + topLanguages.push({ + name: 'Other', + total_seconds: otherSeconds, + percent: otherPercent, + text: otherText, + color: '#CCCCCC', + digital: '', + hours: Math.floor(otherSeconds / 3600), + minutes: Math.floor((otherSeconds % 3600) / 60), + seconds: otherSeconds % 60 + }); + } + + const segmentsWithColors = topLanguages.map(lang => ({ + ...lang, + color: lang.color || langColors[lang.name] || '#CCCCCC' + })); + + return { + segments: segmentsWithColors, + totalTime + }; } export async function fetchWakatime(targetId) { - const data = await fetchWakaTimeStats("zyqunix", "all_time"); + const data = await fetchWakaTimeStats("zyqunix", "all_time"); const target = document.querySelector(`${targetId}`); target.innerHTML = ""; - const langDetails = document.createElement("details"); - const langSummary = document.createElement("summary"); - langSummary.innerText = "Languages"; - langSummary.classList.add("tooltip"); - langSummary.setAttribute("data-tooltip", "Most Used Languages"); - langDetails.appendChild(langSummary); - langDetails.style.marginTop = "15px"; - target.appendChild(langDetails); + const langDetails = document.createElement("details"); + const langSummary = document.createElement("summary"); + langSummary.innerText = "Languages"; + langSummary.classList.add("tooltip"); + langSummary.setAttribute("data-tooltip", "Most Used Languages"); + langDetails.appendChild(langSummary); + langDetails.style.marginTop = "15px"; + target.appendChild(langDetails); - const topLangs = data.data.languages.slice(0, 10); - topLangs.forEach(lang => { - const el = document.createElement("div"); - el.innerText = `${lang.name}: ${lang.text}`; - el.id = lang.name.toLowerCase(); - el.classList.add("proglang"); - el.style.margin = "5px"; - el.style.padding = "10px"; - el.style.borderRadius = "5px"; - el.style.backgroundColor = langColors[lang.name]; - el.style.color = "var(--base)"; - langDetails.appendChild(el); - }); + const topLangs = data.data.languages.slice(0, 10); + topLangs.forEach(lang => { + const el = document.createElement("div"); + el.innerText = `${lang.name}: ${lang.text}`; + el.id = lang.name.toLowerCase(); + el.classList.add("proglang"); + el.style.margin = "5px"; + el.style.padding = "10px"; + el.style.borderRadius = "5px"; + el.style.backgroundColor = langColors[lang.name]; + el.style.color = "var(--base)"; + langDetails.appendChild(el); + }); - const edDetails = document.createElement("details"); - const edSummary = document.createElement("summary"); - edSummary.innerText = "Editors"; - edSummary.classList.add("tooltip"); - edSummary.setAttribute("data-tooltip", "Most Used Editors"); - edDetails.appendChild(edSummary); - edDetails.style.marginTop = "15px"; - target.appendChild(edDetails); + const edDetails = document.createElement("details"); + const edSummary = document.createElement("summary"); + edSummary.innerText = "Editors"; + edSummary.classList.add("tooltip"); + edSummary.setAttribute("data-tooltip", "Most Used Editors"); + edDetails.appendChild(edSummary); + edDetails.style.marginTop = "15px"; + target.appendChild(edDetails); - const topEditors = data.data.editors.slice(0, 5); - topEditors.forEach(editor => { - const el = document.createElement("div"); - el.innerText = `In ${editor.name} for ${editor.text} (${editor.percent}%)`; - el.style.margin = "5px"; - el.style.padding = "10px"; - el.style.borderRadius = "5px"; - el.style.color = "var(--base)"; - el.style.backgroundColor = editorColors[editor.name.toLowerCase().replace(/\s+/g, '').replace(/[^a-zA-Z]/g, '')]; - edDetails.appendChild(el); - }); + const topEditors = data.data.editors.slice(0, 5); + topEditors.forEach(editor => { + const el = document.createElement("div"); + el.innerText = `In ${editor.name} for ${editor.text} (${editor.percent}%)`; + el.style.margin = "5px"; + el.style.padding = "10px"; + el.style.borderRadius = "5px"; + el.style.color = "var(--base)"; + el.style.backgroundColor = editorColors[editor.name.toLowerCase().replace(/\s+/g, '').replace(/[^a-zA-Z]/g, '')]; + edDetails.appendChild(el); + }); - const prDetails = document.createElement("details"); - const prSummary = document.createElement("summary"); - prSummary.innerText = "Projects"; - prSummary.classList.add("tooltip"); - prSummary.setAttribute("data-tooltip", "Most Used Projects"); - prDetails.appendChild(prSummary); - prDetails.style.marginTop = "15px"; - target.appendChild(prDetails); + const prDetails = document.createElement("details"); + const prSummary = document.createElement("summary"); + prSummary.innerText = "Projects"; + prSummary.classList.add("tooltip"); + prSummary.setAttribute("data-tooltip", "Most Used Projects"); + prDetails.appendChild(prSummary); + prDetails.style.marginTop = "15px"; + target.appendChild(prDetails); - const topProjects = data.data.projects.slice(0, 10); - topProjects.forEach(project => { - const el = document.createElement("div"); - el.innerText = `Coded ${project.name} for ${project.text}`; - el.style.margin = "5px"; - el.style.padding = "10px"; - el.style.borderRadius = "5px"; - el.style.color = "var(--base)"; - el.style.backgroundColor = projectColors[Math.floor(Math.random() * projectColors.length)]; - prDetails.appendChild(el); - }); + const topProjects = data.data.projects.slice(0, 10); + topProjects.forEach(project => { + const el = document.createElement("div"); + el.innerText = `Coded ${project.name} for ${project.text}`; + el.style.margin = "5px"; + el.style.padding = "10px"; + el.style.borderRadius = "5px"; + el.style.color = "var(--base)"; + el.style.backgroundColor = projectColors[Math.floor(Math.random() * projectColors.length)]; + prDetails.appendChild(el); + }); - const osDetails = document.createElement("details"); - const osSummary = document.createElement("summary"); - osSummary.innerText = "Operating Systems"; - osSummary.classList.add("tooltip"); - osSummary.setAttribute("data-tooltip", "Most Used Operating Systems"); - osDetails.appendChild(osSummary); - osDetails.style.marginTop = "15px"; - target.appendChild(osDetails); + const osDetails = document.createElement("details"); + const osSummary = document.createElement("summary"); + osSummary.innerText = "Operating Systems"; + osSummary.classList.add("tooltip"); + osSummary.setAttribute("data-tooltip", "Most Used Operating Systems"); + osDetails.appendChild(osSummary); + osDetails.style.marginTop = "15px"; + target.appendChild(osDetails); - const topOS = data.data.operating_systems; - topOS.forEach(machine => { - const el = document.createElement('div'); - el.innerText = `Coded on ${machine.name} for ${machine.text}`; - el.style.margin = "5px"; - el.style.padding = "10px"; - el.style.borderRadius = "5px"; - el.style.color = "var(--base)"; - el.style.backgroundColor = osColors[machine.name.toLowerCase()]; - osDetails.appendChild(el); - }); + const topOS = data.data.operating_systems; + topOS.forEach(machine => { + const el = document.createElement('div'); + el.innerText = `Coded on ${machine.name} for ${machine.text}`; + el.style.margin = "5px"; + el.style.padding = "10px"; + el.style.borderRadius = "5px"; + el.style.color = "var(--base)"; + el.style.backgroundColor = osColors[machine.name.toLowerCase()]; + osDetails.appendChild(el); + }); - const caDetails = document.createElement("details"); - const caSummary = document.createElement("summary"); - caSummary.innerText = "Categories"; - caSummary.classList.add("tooltip"); - caSummary.setAttribute("data-tooltip", "Time Spent by Category"); - caDetails.appendChild(caSummary); - caDetails.style.marginTop = "15px"; - target.appendChild(caDetails); + const caDetails = document.createElement("details"); + const caSummary = document.createElement("summary"); + caSummary.innerText = "Categories"; + caSummary.classList.add("tooltip"); + caSummary.setAttribute("data-tooltip", "Time Spent by Category"); + caDetails.appendChild(caSummary); + caDetails.style.marginTop = "15px"; + target.appendChild(caDetails); - const categories = data.data.categories; - categories.forEach(category => { - const el = document.createElement('div'); - el.style.margin = "5px"; - el.style.padding = "10px"; - el.style.borderRadius = "5px"; - el.style.color = "var(--base)"; - el.style.backgroundColor = categoryColors[category.name.toLowerCase()]; - el.innerText = `Has done ${category.name} for ${category.text}`; - caDetails.appendChild(el); - }); + const categories = data.data.categories; + categories.forEach(category => { + const el = document.createElement('div'); + el.style.margin = "5px"; + el.style.padding = "10px"; + el.style.borderRadius = "5px"; + el.style.color = "var(--base)"; + el.style.backgroundColor = categoryColors[category.name.toLowerCase()]; + el.innerText = `Has done ${category.name} for ${category.text}`; + caDetails.appendChild(el); + }); - const miscDetails = document.createElement("details"); - const miscSummary = document.createElement("summary"); - miscSummary.innerText = "Miscellaneous"; - miscSummary.classList.add("tooltip"); - miscSummary.setAttribute("data-tooltip", "Miscellaneous Wakatime Info"); - miscDetails.appendChild(miscSummary); - miscDetails.style.marginTop = "15px"; - target.appendChild(miscDetails); + const miscDetails = document.createElement("details"); + const miscSummary = document.createElement("summary"); + miscSummary.innerText = "Miscellaneous"; + miscSummary.classList.add("tooltip"); + miscSummary.setAttribute("data-tooltip", "Miscellaneous Wakatime Info"); + miscDetails.appendChild(miscSummary); + miscDetails.style.marginTop = "15px"; + target.appendChild(miscDetails); - const el = document.createElement("div"); - el.innerHTML = ` + const el = document.createElement("div"); + el.innerHTML = ` Total Coding Time: ${data.data.human_readable_total}
Daily Average: ${data.data.human_readable_daily_average}
Days Since Register: ${data.data.days_including_holidays} `; - miscDetails.appendChild(el); + miscDetails.appendChild(el); - document.getElementById("stats_since").innerText = `Registered on ${data.data.start}`; + const registered = data.data.start; + document.getElementById("stats_since").innerText = `Registered on ${registered.slice(0, 10).replace(/-/g, "/")}`; - const chartDetails = document.createElement("details"); - const chartSummary = document.createElement("summary"); - chartSummary.innerText = "Chart"; - chartSummary.classList.add("tooltip"); - chartSummary.setAttribute("data-tooltip", "Miscellaneous Coding Info"); - chartDetails.appendChild(chartSummary); - chartDetails.style.marginTop = "15px"; - target.appendChild(chartDetails); + const chartDetails = document.createElement("details"); + const chartSummary = document.createElement("summary"); + chartSummary.innerText = "Chart"; + chartSummary.classList.add("tooltip"); + chartSummary.setAttribute("data-tooltip", "Miscellaneous Coding Info"); + chartDetails.appendChild(chartSummary); + chartDetails.style.marginTop = "15px"; + target.appendChild(chartDetails); - const chartData = await prepareChartData(); + const chartData = await prepareChartData(); - - const svgNS = "http://www.w3.org/2000/svg"; - const radius = 50; - const center = 60; - const strokeWidth = 20; - const circumference = 2 * Math.PI * radius; - const container = document.createElement("div"); + const svgNS = "http://www.w3.org/2000/svg"; + const radius = 50; + const center = 60; + const strokeWidth = 20; + const circumference = 2 * Math.PI * radius; - const legend = document.createElement("div"); - legend.style.display = 'flex'; - legend.style.flexDirection = 'column'; - legend.style.gap = '8px'; - legend.id = 'legend'; + const container = document.createElement("div"); - chartData.segments.forEach(segment => { - const label = document.createElement('div'); - label.style.display = 'flex'; - label.style.alignItems = 'center'; - label.style.gap = '8px'; - - const colorBox = document.createElement('span'); - colorBox.style.width = '10px'; - colorBox.style.height = '10px'; - colorBox.style.backgroundColor = segment.color; - colorBox.style.borderRadius = '3px'; - - const text = document.createElement('span'); - text.innerText = `${segment.name} (${segment.text})`; - - label.appendChild(colorBox); - label.appendChild(text); - legend.appendChild(label); - }); + const legend = document.createElement("div"); + legend.style.display = 'flex'; + legend.style.flexDirection = 'column'; + legend.style.gap = '8px'; + legend.id = 'legend'; - const chart = document.createElementNS(svgNS, "svg"); - chart.setAttribute("viewBox", "0 0 120 120"); - chart.style.width = "120px"; - chart.style.height = "120px"; + chartData.segments.forEach(segment => { + const label = document.createElement('div'); + label.style.display = 'flex'; + label.style.alignItems = 'center'; + label.style.gap = '8px'; - let cumulativePercent = 0; + const colorBox = document.createElement('span'); + colorBox.style.width = '10px'; + colorBox.style.height = '10px'; + colorBox.style.backgroundColor = segment.color; + colorBox.style.borderRadius = '3px'; - chartData.segments.forEach(segment => { - const circle = document.createElementNS(svgNS, "circle"); - circle.setAttribute("cx", center); - circle.setAttribute("cy", center); - circle.setAttribute("r", radius); - circle.setAttribute("fill", "none"); - circle.setAttribute("stroke", segment.color); - circle.setAttribute("stroke-width", strokeWidth); - circle.setAttribute("transform", `rotate(-90 ${center} ${center})`); + const text = document.createElement('span'); + text.innerText = `${segment.name} (${segment.text})`; - const segmentLength = (segment.percent / 100) * circumference; - const emptyLength = circumference - segmentLength; + label.appendChild(colorBox); + label.appendChild(text); + legend.appendChild(label); + }); - circle.setAttribute("stroke-dasharray", `${segmentLength} ${emptyLength}`); - circle.setAttribute("stroke-dashoffset", circumference * (1 - cumulativePercent / 100)); + const chart = document.createElementNS(svgNS, "svg"); + chart.setAttribute("viewBox", "0 0 120 120"); + chart.style.width = "120px"; + chart.style.height = "120px"; - cumulativePercent += segment.percent; + let cumulativePercent = 0; - chart.appendChild(circle); - }); + chartData.segments.forEach(segment => { + const circle = document.createElementNS(svgNS, "circle"); + circle.setAttribute("cx", center); + circle.setAttribute("cy", center); + circle.setAttribute("r", radius); + circle.setAttribute("fill", "none"); + circle.setAttribute("stroke", segment.color); + circle.setAttribute("stroke-width", strokeWidth); + circle.setAttribute("transform", `rotate(-90 ${center} ${center})`); - container.appendChild(legend); - container.appendChild(chart); + const segmentLength = (segment.percent / 100) * circumference; + const emptyLength = circumference - segmentLength; - chartDetails.appendChild(container); + circle.setAttribute("stroke-dasharray", `${segmentLength} ${emptyLength}`); + circle.setAttribute("stroke-dashoffset", circumference * (1 - cumulativePercent / 100)); + + cumulativePercent += segment.percent; + + chart.appendChild(circle); + }); + + container.appendChild(legend); + container.appendChild(chart); + + chartDetails.appendChild(container); } - -console.log(await fetchWakaTimeStats("zyqunix", "all_time")); diff --git a/index.html b/index.html index 8b88b0a..1790a54 100644 --- a/index.html +++ b/index.html @@ -43,6 +43,14 @@
+
+

GitHub Stats

+

Since 30 Oct 2022

+
+ +
+
+

Contact