Skip to content

Commit

Permalink
added realtime works data card using github api
Browse files Browse the repository at this point in the history
  • Loading branch information
darsan-in committed Jul 4, 2024
1 parent b7921e2 commit eaa9d85
Show file tree
Hide file tree
Showing 13 changed files with 652 additions and 73 deletions.
2 changes: 0 additions & 2 deletions app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
"use client";

export default () => {
return (
<main>
Expand Down
47 changes: 45 additions & 2 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,62 @@
"use client";

import { ShortInto, name, navigation, shortMessage, summary } from "meta";
import { getMostUsedLanguages, makeRepoGroups } from "lib/github";
import {
ShortInto,
githubMetaKey,
groupedMetaKey,
loadGithubMeta,
mostUsedLanguagesKey,
name,
navigation,
shortMessage,
summary,
} from "meta";
import { useEffect } from "react";
import Skills from "./src/components/skills";
import { getRandomColor } from "./src/components/utils";
import Works from "./src/components/works";
import style from "./src/styles/style.module.scss";

export default function HomePage() {
export default async function HomePage() {
useEffect(() => {
const skillIcons = document.querySelectorAll(`.${style.skillIcon}`);

skillIcons.forEach((skillIcon: any) => {
skillIcon.addEventListener("mouseover", () => {
const randomColor = `${getRandomColor()}`;
skillIcon.style.fill = randomColor;
skillIcon.style.color = randomColor;
});
});

const hasMeta = localStorage.getItem(githubMetaKey);
if (!!hasMeta) return;

const dumpMeta = async () => {
const githubMeta = await loadGithubMeta();
localStorage.setItem(githubMetaKey, JSON.stringify(githubMeta));
};

alert("loading again");

dumpMeta()
.then(() => {
//dump language
localStorage.setItem(
mostUsedLanguagesKey,
JSON.stringify(getMostUsedLanguages()),
);

//grouping
localStorage.setItem(
groupedMetaKey,
JSON.stringify(makeRepoGroups("language")),
);
})
.catch((err) => {
console.log(err);
});
}, []);

return (
Expand Down Expand Up @@ -107,6 +148,8 @@ export default function HomePage() {
</div>
</div>
</section>

<Works />
</>
);
}
11 changes: 0 additions & 11 deletions app/src/components/utils.ts

This file was deleted.

95 changes: 95 additions & 0 deletions app/src/components/utils.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { get } from "https";
import { useState } from "react";

export function getRandomColor(): string {
let color: string = "";
const min: number = 50;
const max: number = 255;

do {
color = `rgb(${Math.floor(Math.random() * (max - min) + min)},
${Math.floor(Math.random() * (max - min) + min)},
${Math.floor(Math.random() * (max - min) + min)})`;
} while (color === "rgb(0, 0, 0)");

return color;
}

export function convertDate(dateString: string): string {
const date = new Date(dateString);

let monthAbbreviations = [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
];

const day = date.getUTCDate();
const month = monthAbbreviations[date.getUTCMonth()];
const year = date.getUTCFullYear();

return `${day} ${month} ${year}`;
}

export async function getLatestVersion(releasesUrl: string) {
await new Promise((resolve, reject) => {
if (!releasesUrl) {
reject("Error: Unreleased project");
}

const endpoint = releasesUrl.slice(0, -5) + "/latest";
const parsedUrl = new URL(endpoint);

const options = {
hostname: parsedUrl.hostname,
path: parsedUrl.pathname,
headers: {
"User-Agent": "node.js",
Accept: "application/json",
},
};

get(options, (res) => {
let data: string = "";

res.on("data", (chunk) => {
data += chunk;
});

if (res.statusCode === 200) {
resolve(JSON.parse(data).tag_name ?? false);
} else {
reject("Error: " + res.statusCode);
}
}).on("error", (err) => {
reject(err);
});
});
}

export function LatestVersion({ releasesUrl }: { releasesUrl: string }) {
const [state, setState] = useState<string | boolean>(false);

getLatestVersion(releasesUrl)
.then((version: any) => {
if (version) setState(version);
})
.catch((err) => {
console.log(err);
});

return (
<p className="text-blue-500">
{state ? `Version: ${state}` : "Unreleased"}
</p>
);
}
88 changes: 88 additions & 0 deletions app/src/components/work-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { GithubRepoMeta } from "lib/options";
import { FaGithub } from "react-icons/fa6";
import { RxExternalLink } from "react-icons/rx";
import { convertDate } from "./utils";

export default ({ projects }: { projects: GithubRepoMeta[] }) => (
<section className="py-5">
<div className="max-w-screen-xl mx-auto px-4 md:px-8">
<ul className="mt-0 grid gap-8 sm:grid-cols-2 lg:grid-cols-3">
{projects.map((project, idx) => (
<li
className="border rounded-lg"
key={idx}>
<div className="flex items-start justify-between p-4">
<div className="space-y-2">
<h4 className="text-gray-800 font-bold text-blue-700">
{project.name.toUpperCase()}
</h4>
<p className="text-gray-600 text-sm">
{project.description}
</p>
</div>
<div className="flex gap-2">
<a
href={project.htmlUrl + "#readme"}
target="_"
className="text-black text-sm border rounded-lg p-1 duration-150 hover:bg-gray-100 hover:text-indigo-600">
<FaGithub
size={20}
className="fill-current"
/>
</a>

{project.homepage ? (
<a
href={project.homepage ?? ""}
target="_"
className="text-black text-sm border rounded-lg p-1 duration-150 hover:bg-gray-100 hover:text-indigo-600">
<RxExternalLink
size={20}
className="fill-current"
/>
</a>
) : (
""
)}
</div>
</div>
<section className="py-5 px-4 border-t text-sm">
<div className="flex gap-x-2 justify-between">
<ul>
<li className="font-medium">
Created:{" "}
<span className="font-normal text-gray-500">
{convertDate(project.createdAt)}
</span>
</li>
<li className="font-medium">
Updated:{" "}
<span className="font-normal text-gray-500">
{convertDate(project.updatedAt)}
</span>
</li>
{project.openIssuesCount ? (
<li className="font-medium">
Request:{" "}
<span className="font-normal text-gray-500">
{project.openIssuesCount}
</span>
</li>
) : (
""
)}
</ul>
<ul>
<p className="text-blue-500">
©️ {project.license.spdxId}
</p>
{/* <LatestVersion releasesUrl={project.releasesUrl ?? ""} /> */}
</ul>
</div>
</section>
</li>
))}
</ul>
</div>
</section>
);
82 changes: 82 additions & 0 deletions app/src/components/works.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import * as Tabs from "@radix-ui/react-tabs";

import { GithubRepoMeta } from "lib/options";
import { groupedMetaKey } from "meta";
import { useEffect, useState } from "react";
import WorkCard from "./work-card";

export default () => {
const [tabItems, setTabItems] = useState<
Record<string, GithubRepoMeta[]>
>({});

const [selectedTab, setSelectedTab] = useState<string>("Loading");

useEffect(() => {
const groupedMeta = JSON.parse(
localStorage.getItem(groupedMetaKey) ?? "{}",
);

setTabItems(groupedMeta);
setSelectedTab(Object.keys(groupedMeta)[0]);
}, []);

return (
<>
<Tabs.Root
className="max-w-screen-xl mt-2 mx-auto px-4 md:px-8 flex-col"
value={selectedTab}
onValueChange={(val) => setSelectedTab(val)}>
<Tabs.List
className="hidden gap-x-3 py-1 overflow-x-auto px-px text-sm sm:flex place-content-center"
aria-label="Project Experience">
{Object.keys(tabItems).map((language, idx) => (
<Tabs.Trigger
key={idx}
className="data-[state=active]:bg-gray-100 data-[state=active]:text-gray-700 data-[state=active]:shadow-sm outline-gray-800 py-1.5 px-3 rounded-lg duration-150 text-gray-500 hover:text-gray-700 hover:bg-gray-100 active:bg-gray-100 font-medium"
value={language}>
{language}
</Tabs.Trigger>
))}
</Tabs.List>

{/* mobile area */}
<div className="relative text-gray-500 sm:hidden">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
className="pointer-events-none w-5 h-5 absolute right-2 inset-y-0 my-auto">
<path
fillRule="evenodd"
d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z"
clipRule="evenodd"
/>
</svg>
<select
value={selectedTab}
className="py-2 px-3 w-full bg-transparent appearance-none outline-none border rounded-lg shadow-sm focus:border-gray-800 text-sm"
onChange={(e) => setSelectedTab(e.target.value)}>
{Object.keys(tabItems).map((language, idx) => (
<option
key={idx}
tabIndex={idx}>
{language}
</option>
))}
</select>
</div>
{/* mobile area ended */}

{Object.keys(tabItems).map((language, idx) => (
<Tabs.Content
key={idx}
className="py-6"
value={language}>
<WorkCard projects={tabItems[language]} />
</Tabs.Content>
))}
</Tabs.Root>
</>
);
};
Loading

0 comments on commit eaa9d85

Please sign in to comment.