Compare commits
No commits in common. '107c60e592139ae19c9fa44f028d890188a22037' and '16cc67c4152c390b21fd017f0ca3eb7799ebb2a6' have entirely different histories.
107c60e592
...
16cc67c415
@ -1,7 +1,3 @@
|
|||||||
compressionLevel: mixed
|
|
||||||
|
|
||||||
enableGlobalCache: false
|
|
||||||
|
|
||||||
nodeLinker: node-modules
|
nodeLinker: node-modules
|
||||||
|
|
||||||
yarnPath: .yarn/releases/yarn-4.5.1.cjs
|
yarnPath: .yarn/releases/yarn-3.6.1.cjs
|
||||||
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 49 KiB |
@ -0,0 +1,27 @@
|
|||||||
|
import { FancyUnderline, GroupedText, Text } from "./utils/Text";
|
||||||
|
|
||||||
|
export const About = () => {
|
||||||
|
return (
|
||||||
|
<GroupedText className="text-center">
|
||||||
|
<Text>
|
||||||
|
I am a{" "}
|
||||||
|
<FancyUnderline className="font-semibold" decoration="decoration-green">
|
||||||
|
software engineer
|
||||||
|
</FancyUnderline>{" "}
|
||||||
|
at the BBC and a{" "}
|
||||||
|
<FancyUnderline className="font-semibold" decoration="decoration-red">student</FancyUnderline>,
|
||||||
|
currently pursuing a BSc in Software Engineering at the University of
|
||||||
|
Glasgow.
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
You'll usually find me nerding out about technology and playing
|
||||||
|
video games. I also really enjoy contributing to open source projects,
|
||||||
|
mainly{" "}
|
||||||
|
<FancyUnderline className="font-semibold" decoration="decoration-peach">
|
||||||
|
Catppuccin
|
||||||
|
</FancyUnderline>
|
||||||
|
.
|
||||||
|
</Text>
|
||||||
|
</GroupedText>
|
||||||
|
);
|
||||||
|
};
|
@ -1,13 +1,14 @@
|
|||||||
import { Wave } from "./icons/Wave";
|
import { Wave } from "./icons/Wave";
|
||||||
|
import { H1 } from "./utils/Titles";
|
||||||
|
|
||||||
export const Header = () => {
|
export const Header = () => {
|
||||||
return (
|
return (
|
||||||
<h1 className="text-2xl lg:text-3xl xl:text-4xl 2xl:text-5xl font-bold tracking-tight text-center">
|
<H1>
|
||||||
Hiya{" "}
|
Hiya{" "}
|
||||||
<div className="inline-block motion-safe:animate-waving-hand">
|
<div className="inline-block motion-safe:animate-waving-hand">
|
||||||
<Wave />
|
<Wave />
|
||||||
</div>
|
</div>
|
||||||
, I'm Goudham
|
, I'm Goudham
|
||||||
</h1>
|
</H1>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
import { fetchUserRepositories } from "@/app/lib/api";
|
||||||
|
import { Star } from "./icons/Star";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { H2 } from "./utils/Titles";
|
||||||
|
import { ExternalLink } from "./icons/ExternalLink";
|
||||||
|
import { Archived } from "./icons/Archived";
|
||||||
|
|
||||||
|
export const Projects = async () => {
|
||||||
|
const projects = await fetchUserRepositories().then((res) =>
|
||||||
|
res
|
||||||
|
.filter(
|
||||||
|
(data) =>
|
||||||
|
!data.fork &&
|
||||||
|
data.name !== "uwuifyy" &&
|
||||||
|
data.name !== "Enso-Bot" &&
|
||||||
|
!data.archived
|
||||||
|
)
|
||||||
|
.sort((a, b) => (b.stargazers_count ?? 0) - (a.stargazers_count ?? 0))
|
||||||
|
.slice(0, 6)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="flex flex-col items-center space-y-3 px-5">
|
||||||
|
<H2>Favourite Projects</H2>
|
||||||
|
<div className="rounded-lg text-md lg:text-lg xl:text-xl">
|
||||||
|
<div className="flex flex-col space-y-4">
|
||||||
|
{projects.map((project) => (
|
||||||
|
<Project key={project.name} project={project} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Project = ({ project }: { project: RepositoryData }) => {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
href={project.html_url}
|
||||||
|
className="bg-base w-full rounded-xl outline-2 outline-mantle focus:ring-2 focus:ring-blue ring-offset-0 shadow-lg hover:scale-105 motion-safe:duration-300 p-3"
|
||||||
|
>
|
||||||
|
<div className="flex flex-col space-y-1 h-full justify-between">
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold hover:underline">
|
||||||
|
{project.name} 
|
||||||
|
<ExternalLink />
|
||||||
|
</span>
|
||||||
|
{project.description && (
|
||||||
|
<div className="text-sm lg:text-md xl:text-lg text-subtext1">
|
||||||
|
{project.description}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="self-end flex flex-row gap-x-1">
|
||||||
|
{project.archived && (
|
||||||
|
<p className="text-md font-medium bg-mantle rounded-xl px-3">
|
||||||
|
<Archived />
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
<p className="text-md font-medium bg-mantle rounded-xl px-3">
|
||||||
|
{project.stargazers_count} <Star />
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,13 @@
|
|||||||
|
export const Archived = () => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
className="h-5 w-5 xl:h-6 xl:w-6 inline align-text-bottom fill-yellow"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="32"
|
||||||
|
height="32"
|
||||||
|
viewBox="0 0 256 256"
|
||||||
|
>
|
||||||
|
<path d="M224,48H32A16,16,0,0,0,16,64V88a16,16,0,0,0,16,16v88a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V104a16,16,0,0,0,16-16V64A16,16,0,0,0,224,48ZM208,192H48V104H208ZM224,88H32V64H224V88ZM96,136a8,8,0,0,1,8-8h48a8,8,0,0,1,0,16H104A8,8,0,0,1,96,136Z"></path>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,13 @@
|
|||||||
|
export const Close = () => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
className="h-8 w-8 fill-overlay0 group-hover:bg-subtext1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="32"
|
||||||
|
height="32"
|
||||||
|
viewBox="0 0 256 256"
|
||||||
|
>
|
||||||
|
<path d="M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z"></path>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,13 @@
|
|||||||
|
export const ExternalLink = () => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
className="h-4 w-4 inline-block align-text-top fill-text opacity-50"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="32"
|
||||||
|
height="32"
|
||||||
|
viewBox="0 0 256 256"
|
||||||
|
>
|
||||||
|
<path d="M224,104a8,8,0,0,1-16,0V59.32l-66.33,66.34a8,8,0,0,1-11.32-11.32L196.68,48H152a8,8,0,0,1,0-16h64a8,8,0,0,1,8,8Zm-40,24a8,8,0,0,0-8,8v72H48V80h72a8,8,0,0,0,0-16H48A16,16,0,0,0,32,80V208a16,16,0,0,0,16,16H176a16,16,0,0,0,16-16V136A8,8,0,0,0,184,128Z"></path>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,18 @@
|
|||||||
|
export const Heart = () => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
className="h-5 w-5 lg:h-6 lg:w-6 xl:h-7 xl:w-7 inline-block align-text-bottom"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 256 256"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M128,216S24,160,24,94A54,54,0,0,1,78,40c22.59,0,41.94,12.31,50,32,8.06-19.69,27.41-32,50-32a54,54,0,0,1,54,54C232,160,128,216,128,216Z"
|
||||||
|
fill="red"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="0"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,19 @@
|
|||||||
|
export const Star = () => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
className="h-5 w-5 xl:h-6 xl:w-6 inline align-text-bottom"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 256 256"
|
||||||
|
>
|
||||||
|
<rect width="256" height="256" fill="none" />
|
||||||
|
<path
|
||||||
|
d="M135.34,28.9l23.23,55.36a8,8,0,0,0,6.67,4.88l59.46,5.14a8,8,0,0,1,4.54,14.07L184.13,147.7a8.08,8.08,0,0,0-2.54,7.89l13.52,58.54a8,8,0,0,1-11.89,8.69l-51.1-31a7.93,7.93,0,0,0-8.24,0l-51.1,31a8,8,0,0,1-11.89-8.69l13.52-58.54a8.08,8.08,0,0,0-2.54-7.89L26.76,108.35A8,8,0,0,1,31.3,94.28l59.46-5.14a8,8,0,0,0,6.67-4.88L120.66,28.9A8,8,0,0,1,135.34,28.9Z"
|
||||||
|
fill="orange"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="0"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,9 @@
|
|||||||
|
export const Hamburger = () => {
|
||||||
|
return (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<span className="block w-8 h-0.5 bg-overlay0 group-hover:bg-subtext1"></span>
|
||||||
|
<span className="block w-8 h-0.5 bg-overlay0 group-hover:bg-subtext1"></span>
|
||||||
|
<span className="block w-8 h-0.5 bg-overlay0 group-hover:bg-subtext1"></span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,30 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export const Links = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<LinkWithAnimatedUnderline href="/" text="< Home />" />
|
||||||
|
<LinkWithAnimatedUnderline href="/projects" text="< Projects />" />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const LinkWithAnimatedUnderline = ({
|
||||||
|
href,
|
||||||
|
text,
|
||||||
|
}: {
|
||||||
|
href: string;
|
||||||
|
text: string;
|
||||||
|
}) => {
|
||||||
|
const animatedUnderline =
|
||||||
|
"md:block md:max-w-0 md:group-hover:max-w-full motion-safe:md:transition-all md:h-0.5 md:bg-blue motion-safe:md:duration-300";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link href={href} className="motion-safe:md:transition motion-safe:md:duration-300 group focus:text-blue">
|
||||||
|
<span className="group-hover:text-blue motion-safe:md:transition-all motion-safe:md:duration-300">
|
||||||
|
{text}
|
||||||
|
</span>
|
||||||
|
<span className={`${animatedUnderline}`}></span>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,83 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Dispatch, SetStateAction, useRef, useState } from "react";
|
||||||
|
import { ProfilePicture } from "../images/ProfilePicture";
|
||||||
|
import { Hamburger } from "./Hamburger";
|
||||||
|
import { Links } from "./Links";
|
||||||
|
import { Close } from "../icons/Close";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { Dialog } from "@headlessui/react";
|
||||||
|
import { usePathname } from "next/navigation";
|
||||||
|
|
||||||
|
export const Navbar = () => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<nav className="self-center w-full max-w-xl lg:max-w-3xl xl:max-w-6xl">
|
||||||
|
<div className="p-4 flex flex-row items-center justify-between text-md lg:text-lg xl:text-xl">
|
||||||
|
<ProfilePicture />
|
||||||
|
<div className="hidden md:flex flex-row gap-x-4 font-bold tracking-tight">
|
||||||
|
<Links />
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className="md:hidden px-4 py-2 hover:bg-crust dark:hover:bg-base rounded-lg"
|
||||||
|
aria-label="Open Menu"
|
||||||
|
onClick={() => setIsOpen((prev) => !prev)}
|
||||||
|
>
|
||||||
|
<Hamburger />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<MobileMenu isOpen={isOpen} setIsOpen={setIsOpen} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MobileMenu = ({
|
||||||
|
isOpen,
|
||||||
|
setIsOpen,
|
||||||
|
}: {
|
||||||
|
isOpen: boolean;
|
||||||
|
setIsOpen: Dispatch<SetStateAction<boolean>>;
|
||||||
|
}) => {
|
||||||
|
const focusedRef = useRef(null);
|
||||||
|
const path = usePathname();
|
||||||
|
const checkPath = (href: string) => {
|
||||||
|
if (href === path) {
|
||||||
|
setIsOpen(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
className={
|
||||||
|
isOpen
|
||||||
|
? "block w-full h-screen absolute overflow-hidden inset-0 bg-base dark:bg-crust z-10 flex-col items-center"
|
||||||
|
: "hidden"
|
||||||
|
}
|
||||||
|
open={isOpen}
|
||||||
|
onClose={() => setIsOpen(false)}
|
||||||
|
>
|
||||||
|
<div className="max-w-xl mx-auto">
|
||||||
|
<div className="p-4 w-full flex flex-row items-center justify-between">
|
||||||
|
<ProfilePicture />
|
||||||
|
<button
|
||||||
|
className="px-4 py-1 hover:bg-crust dark:hover:bg-base rounded-lg focus:bg-crust dark:focus:bg-base"
|
||||||
|
onClick={() => setIsOpen((prev) => !prev)}
|
||||||
|
ref={focusedRef}
|
||||||
|
>
|
||||||
|
<Close />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<ul className="flex flex-col space-y-6 items-center text-2xl font-semibold justify-center">
|
||||||
|
<Link href="/" onClick={() => checkPath("/")}>
|
||||||
|
< Home />
|
||||||
|
</Link>
|
||||||
|
<Link href="/projects" onClick={() => checkPath("/projects")}>
|
||||||
|
< Projects />
|
||||||
|
</Link>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,16 @@
|
|||||||
|
export const H1 = ({ children }: { children: React.ReactNode | string }) => {
|
||||||
|
return (
|
||||||
|
<h1 className="text-3xl lg:text-4xl xl:text-5xl font-bold tracking-tight text-center">
|
||||||
|
{children}
|
||||||
|
</h1>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const H2 = ({ children }: { children: React.ReactNode | string }) => {
|
||||||
|
return (
|
||||||
|
<h2 className="text-2xl lg:text-3xl xl:text-4xl font-bold">
|
||||||
|
{children}
|
||||||
|
</h2>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 1000 B After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.0 KiB |
@ -0,0 +1,36 @@
|
|||||||
|
import { Octokit } from "octokit";
|
||||||
|
|
||||||
|
if (!process.env.REPOS_READ_ONLY) {
|
||||||
|
throw new Error("GITHUB TOKEN 'REPOS_READ_ONLY' is missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
const octokit = new Octokit({
|
||||||
|
auth: process.env.REPOS_READ_ONLY,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const fetchUserRepositories = async (): Promise<RepositoryData[]> => {
|
||||||
|
return await octokit.paginate(
|
||||||
|
octokit.rest.repos.listForAuthenticatedUser,
|
||||||
|
{
|
||||||
|
per_page: 100,
|
||||||
|
visibility: "public",
|
||||||
|
affiliation: "owner",
|
||||||
|
},
|
||||||
|
(response) =>
|
||||||
|
response.data.map((data) => {
|
||||||
|
return {
|
||||||
|
name: data.name,
|
||||||
|
description: data.description,
|
||||||
|
html_url: data.html_url,
|
||||||
|
size: data.size,
|
||||||
|
archived: data.archived,
|
||||||
|
fork: data.fork,
|
||||||
|
stargazers_count: data.stargazers_count,
|
||||||
|
open_issues_count: data.open_issues,
|
||||||
|
created_at: data.created_at,
|
||||||
|
updated_at: data.updated_at,
|
||||||
|
topics: data.topics,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,13 @@
|
|||||||
|
type RepositoryData = {
|
||||||
|
name: string;
|
||||||
|
description: string | null;
|
||||||
|
html_url: string;
|
||||||
|
size?: number;
|
||||||
|
archived?: boolean;
|
||||||
|
fork: boolean;
|
||||||
|
stargazers_count?: number;
|
||||||
|
open_issues_count?: number;
|
||||||
|
created_at: string | null | undefined;
|
||||||
|
updated_at: string | null | undefined;
|
||||||
|
topics?: string[];
|
||||||
|
};
|
@ -1,49 +1,18 @@
|
|||||||
import { Header } from "./components/Header";
|
import { Header } from "./components/Header";
|
||||||
import { StyledLink } from "./components/utils/StyledLink";
|
import { About } from "./components/About";
|
||||||
import { Text } from "./components/utils/Text";
|
import { Projects } from "./components/Projects";
|
||||||
import { GroupedText, FancyUnderline } from "./components/utils/Text";
|
import { Navbar } from "./components/nav/Navbar";
|
||||||
import { ProfilePicture } from "./components/images/ProfilePicture";
|
|
||||||
import { Footer } from "./components/Footer";
|
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-4">
|
/* Inherits from the RootLayout */
|
||||||
<div className="self-center">
|
<div className="flex flex-col">
|
||||||
<ProfilePicture />
|
<Navbar />
|
||||||
</div>
|
<div className="self-center max-w-md lg:max-w-lg xl:max-w-xl flex flex-col space-y-10 grow">
|
||||||
<div className="self-center max-w-lg lg:max-w-xl xl:max-w-2xl flex flex-col space-y-10">
|
|
||||||
<Header />
|
<Header />
|
||||||
<GroupedText className="text-center">
|
<About />
|
||||||
<Text>
|
<Projects />
|
||||||
I am a{" "}
|
|
||||||
<FancyUnderline className="font-semibold" decoration="decoration-peach">
|
|
||||||
Software Engineer at the BBC
|
|
||||||
</FancyUnderline>.{" "}
|
|
||||||
I obtained my degree while working full-time, you can learn
|
|
||||||
more about that by visiting{" \""}
|
|
||||||
<StyledLink
|
|
||||||
href={"https://www.gla.ac.uk/schools/computing/undergraduate/graduateapprenticeships/"}>
|
|
||||||
Graduate Apprenticeships - University of Glasgow
|
|
||||||
</StyledLink>."
|
|
||||||
</Text>
|
|
||||||
<Text>
|
|
||||||
You'll usually find me nerding out about technology and playing
|
|
||||||
video games. Contributing to open-source is a big passion of mine and
|
|
||||||
I'm grateful to be a{" "}
|
|
||||||
<FancyUnderline className="font-semibold" decoration="decoration-peach">
|
|
||||||
Core Maintainer for Catppuccin
|
|
||||||
</FancyUnderline>
|
|
||||||
. When I'm not staring at a screen, I love taking my Canon EOS R50 out for a spin and capturing my friends and the world around me.
|
|
||||||
</Text>
|
|
||||||
<Text>
|
|
||||||
Eventually, I'd like to turn this website into a place where I can share my thoughts, projects, and photographs with everyone. Hopefully sometime soon<span className="text-blue font-bold">™</span>
|
|
||||||
</Text>
|
|
||||||
<Text>
|
|
||||||
<span className="text-red font-bold">~~~</span>
|
|
||||||
</Text>
|
|
||||||
</GroupedText>
|
|
||||||
</div>
|
</div>
|
||||||
<Footer />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
import { Metadata } from "next";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Projects by Goudham Suresh",
|
||||||
|
description:
|
||||||
|
"I am a software engineer by day, and an open source enthusiast by night. Welcome to my corner of the internet where I write about my experiences, projects, and more.",
|
||||||
|
openGraph: {
|
||||||
|
url: process.env.NEXT_PUBLIC_IS_PREVIEW
|
||||||
|
? "https://preview.goudham.com/projects"
|
||||||
|
: "https://goudham.com/projects",
|
||||||
|
title: `Projects by Goudham Suresh${
|
||||||
|
process.env.NEXT_PUBLIC_IS_PREVIEW ? " (Preview)" : ""
|
||||||
|
}`,
|
||||||
|
description:
|
||||||
|
"I am a software engineer by day, and an open source enthusiast by night. Welcome to my corner of the internet where I write about my experiences, projects, and more.",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ProjectsLayout({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
return <section>{children}</section>;
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
import { fetchUserRepositories } from "../lib/api";
|
||||||
|
import { Project } from "../components/Projects";
|
||||||
|
import { Text } from "../components/utils/Text";
|
||||||
|
import { H1 } from "../components/utils/Titles";
|
||||||
|
import { Navbar } from "../components/nav/Navbar";
|
||||||
|
|
||||||
|
export default async function Projects() {
|
||||||
|
const projects = await fetchUserRepositories().then((res) =>
|
||||||
|
res
|
||||||
|
.filter((data) => !data.fork)
|
||||||
|
.sort((a, b) => (b.stargazers_count ?? 0) - (a.stargazers_count ?? 0))
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
/* Inherits from the RootLayout */
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<Navbar />
|
||||||
|
<div className="self-center max-w-md lg:max-w-lg xl:max-w-xl 2xl:max-w-4xl flex flex-col space-y-10 grow">
|
||||||
|
<H1>All Projects</H1>
|
||||||
|
<div className="flex flex-col mx-3 gap-3 text-center">
|
||||||
|
<Text>
|
||||||
|
Here's a list of all my public projects, they are sorted by
|
||||||
|
most starred. Note that some projects are still under development
|
||||||
|
and/or unfinished, but I'd like to get around to finishing them
|
||||||
|
at some point in the future!
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
I promise I'll implement sorting options soon
|
||||||
|
<span className="text-blue font-bold">™</span>
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1 2xl:grid-cols-2 gap-y-4 gap-x-6 px-5">
|
||||||
|
{projects.map((project) => (
|
||||||
|
<div
|
||||||
|
key={project.name}
|
||||||
|
className="rounded-lg text-md lg:text-lg xl:text-xl flex justify-center"
|
||||||
|
>
|
||||||
|
<Project key={project.name} project={project} />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
Before Width: | Height: | Size: 246 KiB After Width: | Height: | Size: 282 KiB |