refactor!: strip website back to basics (#3)
@ -1,3 +1,7 @@
|
||||
compressionLevel: mixed
|
||||
|
||||
enableGlobalCache: false
|
||||
|
||||
nodeLinker: node-modules
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.6.1.cjs
|
||||
yarnPath: .yarn/releases/yarn-4.5.1.cjs
|
||||
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 63 KiB |
@ -1,27 +0,0 @@
|
||||
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,14 +1,13 @@
|
||||
import { Wave } from "./icons/Wave";
|
||||
import { H1 } from "./utils/Titles";
|
||||
|
||||
export const Header = () => {
|
||||
return (
|
||||
<H1>
|
||||
<h1 className="text-2xl lg:text-3xl xl:text-4xl 2xl:text-5xl font-bold tracking-tight text-center">
|
||||
Hiya{" "}
|
||||
<div className="inline-block motion-safe:animate-waving-hand">
|
||||
<Wave />
|
||||
</div>
|
||||
, I'm Goudham
|
||||
</H1>
|
||||
</h1>
|
||||
);
|
||||
};
|
||||
|
@ -1,69 +0,0 @@
|
||||
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>
|
||||
);
|
||||
};
|
@ -1,13 +0,0 @@
|
||||
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>
|
||||
);
|
||||
};
|
@ -1,13 +0,0 @@
|
||||
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>
|
||||
);
|
||||
};
|
@ -1,13 +0,0 @@
|
||||
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>
|
||||
);
|
||||
};
|
@ -1,18 +0,0 @@
|
||||
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>
|
||||
);
|
||||
};
|
@ -1,19 +0,0 @@
|
||||
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>
|
||||
);
|
||||
};
|
@ -1,9 +0,0 @@
|
||||
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>
|
||||
);
|
||||
};
|
@ -1,30 +0,0 @@
|
||||
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>
|
||||
);
|
||||
};
|
@ -1,83 +0,0 @@
|
||||
"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>
|
||||
);
|
||||
};
|
@ -1,16 +0,0 @@
|
||||
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: 1.5 KiB After Width: | Height: | Size: 1000 B |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.1 KiB |
@ -1,36 +0,0 @@
|
||||
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,
|
||||
};
|
||||
})
|
||||
);
|
||||
};
|
@ -1,13 +0,0 @@
|
||||
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,18 +1,49 @@
|
||||
import { Header } from "./components/Header";
|
||||
import { About } from "./components/About";
|
||||
import { Projects } from "./components/Projects";
|
||||
import { Navbar } from "./components/nav/Navbar";
|
||||
import { StyledLink } from "./components/utils/StyledLink";
|
||||
import { Text } from "./components/utils/Text";
|
||||
import { GroupedText, FancyUnderline } from "./components/utils/Text";
|
||||
import { ProfilePicture } from "./components/images/ProfilePicture";
|
||||
import { Footer } from "./components/Footer";
|
||||
|
||||
export default function Home() {
|
||||
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 flex flex-col space-y-10 grow">
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="self-center">
|
||||
<ProfilePicture />
|
||||
</div>
|
||||
<div className="self-center max-w-lg lg:max-w-xl xl:max-w-2xl flex flex-col space-y-10">
|
||||
<Header />
|
||||
<About />
|
||||
<Projects />
|
||||
<GroupedText className="text-center">
|
||||
<Text>
|
||||
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>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,25 +0,0 @@
|
||||
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>;
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
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: 282 KiB After Width: | Height: | Size: 246 KiB |