add blog post, projects
Build and Publish Docker Image / build (push) Successful in 12s
Build and Publish Docker Image / deploy (push) Successful in 1s

This commit is contained in:
2026-09-07 16:29:18 -04:00
parent ac58aaffc8
commit 96a8b655f0
7 changed files with 99 additions and 81 deletions
+33
View File
@@ -0,0 +1,33 @@
import ErrorState from "./ErrorState";
import { useState, useEffect } from "react";
import type { BlogPost } from "../utils/types";
export default function BlogRow() {
const [post, setPost] = useState<BlogPost | null>(null);
useEffect(() => {
fetch("https://blog.alexav.gg/api/posts", {
method: "GET",
}).then(async (res) => {
if (res.ok) {
try {
const data = await res.json();
setPost(data[0]);
} catch (e) {
console.error("An error occurred fetching blog posts!", e);
}
} else console.error("Failed to fetch blog posts!");
});
}, []);
if (post) {
return (
<div>
<a href="https://blog.alexav.gg">
<h1>{post?.title}</h1>
<p>{post?.description}</p>
</a>
</div>
);
} else return <ErrorState label="nothing posted yet :(" />;
}
+7 -29
View File
@@ -1,31 +1,9 @@
export default function ErrorState({ export default function ErrorState({ label }: { label: string }) {
type = "error", return (
}: { <div className="flex flex-col bg-black text-white">
type: "error" | "loading" | "unavailable"; <div className="px-2 text-center">
}) { <p>{label}</p>
if (type === "error") {
return (
<div className="flex flex-col bg-black text-white">
<div className="px-2 text-center">
<p>whoops, something went wrong</p>
</div>
</div> </div>
); </div>
} else if (type === "loading") { );
return (
<div className="flex flex-col bg-black text-white">
<div className="px-2 text-center">
<h1 className="font-semibold">loading..</h1>
</div>
</div>
);
} else if (type === "unavailable") {
return (
<div className="flex flex-col bg-black text-white">
<div className="px-2 text-center">
<p>no game launched recently</p>
</div>
</div>
);
}
} }
+11 -23
View File
@@ -34,29 +34,17 @@ export default function GameComponent() {
fetchGame(); fetchGame();
}, []); }, []);
if (game) { return (
return ( <a href="https://medal.tv/u/alexav" target="_blank">
<a href={`https://medal.tv/u/alexav`} target="_blank"> <div className="bg-white p-2 rounded-lg w-full min-h-full flex justify-center text-black">
<div className="flex flex-col bg-black border border-black text-white"> <div className="flex justify-center items-center">
<div className="flex"> <div className="px-2">
<Gamepad className="w-full h-full bg-white text-black" /> <p className="text-sm font-semibold">
<div className="pl-2 pr-1"> {game?.currentlyPlaying.game || "Nothing playing"}
<p className="w-[150px] truncate overflow-hidden whitespace-nowrap"> </p>
{game.currentlyPlaying.game}
</p>
<p>
{" "}
since{" "}
{new Date(
game.currentlyPlaying.startedPlayingAt,
).toLocaleTimeString()}
</p>
</div>
</div> </div>
</div> </div>
</a> </div>
); </a>
} else { );
return <ErrorState type="unavailable" />;
}
} }
+19 -26
View File
@@ -1,10 +1,8 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import type { LastFMMusicObject } from "../../utils/types"; import type { LastFMMusicObject } from "../../utils/types";
import ErrorState from "../ErrorState";
export default function MusicComponent() { export default function MusicComponent() {
const [music, setMusic] = useState<LastFMMusicObject | null>(null); const [music, setMusic] = useState<LastFMMusicObject | null>(null);
const [failedFetch, setFailedFetch] = useState<boolean>(false);
useEffect(() => { useEffect(() => {
async function fetchMusic() { async function fetchMusic() {
@@ -13,10 +11,9 @@ export default function MusicComponent() {
if (response.ok) { if (response.ok) {
const data = await response.json(); const data = await response.json();
setMusic(data as LastFMMusicObject); setMusic(data as LastFMMusicObject);
} else setFailedFetch(true); }
} catch (e) { } catch (e) {
console.error("Failed to fetch music!", e); console.error("Failed to fetch music!", e);
setFailedFetch(true);
} }
} }
@@ -26,27 +23,23 @@ export default function MusicComponent() {
fetchMusic(); fetchMusic();
}, []); }, []);
if (!failedFetch) { return (
if (music) { <a href="https://koito.fntz.net" target="_blank">
return ( <div className="bg-white p-2 rounded-lg w-full min-h-full text-black">
<a href="https://koito.fntz.net" target="_blank"> <div className="flex justify-center items-center">
<div className="w-full bg-black"> <img
<div className="text-center"> src={
<div className="pl-2 pr-1"> music?.images.filter((v) => v.size === "medium")[0]["url"] ||
<p className="text-lg"> music?.images[0].url
<span className="font-semibold">Currently Playing - </span> }
<span className="underline">{music.title}</span> by{" "} className="border-green-500 border-2"
<span className="underline">{music.artist}</span> />
</p> <div className="px-2">
</div> <p className="text-sm font-semibold">{music?.title}</p>
</div> <p className="text-xs">{music?.artist}</p>
</div> </div>
</a> </div>
); </div>
} else { </a>
return <ErrorState type="loading" />; );
}
} else {
return <ErrorState type="error" />;
}
} }
+2 -2
View File
@@ -67,9 +67,9 @@ export default function StatModule() {
</a> </a>
); );
} else { } else {
return <ErrorState type="loading" />; return <ErrorState label="loading.." />;
} }
} else { } else {
return <ErrorState type="error" />; return <ErrorState label="something went wrong! :(" />;
} }
} }
+19 -1
View File
@@ -3,6 +3,9 @@ import ProjectCard from "../components/ProjectCard";
import FantasyStatus from "../components/FantasyStatus"; import FantasyStatus from "../components/FantasyStatus";
import PictureCarousel from "../components/modules/PictureCarousel"; import PictureCarousel from "../components/modules/PictureCarousel";
import Footer from "../components/Footer"; import Footer from "../components/Footer";
import BlogRow from "../components/BlogRow";
import GameComponent from "../components/info/GameComponent";
import MusicComponent from "../components/info/MusicComponent";
export default function App() { export default function App() {
return ( return (
@@ -43,6 +46,15 @@ export default function App() {
<hr className="my-4 border-zinc-800" /> <hr className="my-4 border-zinc-800" />
<p className="opacity-85 text-sm text-left pb-2 underline">
<a href="https://blog.alexav.gg" target="_blank">
LATEST BLOG POST
</a>
</p>
<BlogRow />
<hr className="my-4 border-zinc-800" />
<p className="opacity-85 text-sm text-left pb-2">THINGS I'VE BUILT</p> <p className="opacity-85 text-sm text-left pb-2">THINGS I'VE BUILT</p>
<div className="my-2 space-y-2"> <div className="my-2 space-y-2">
<ProjectCard <ProjectCard
@@ -81,7 +93,13 @@ export default function App() {
<hr className="my-4 border-zinc-800" /> <hr className="my-4 border-zinc-800" />
<p className="opacity-85 text-sm text-left pb-2">FUN STUFF</p> <p className="opacity-85 text-sm text-left pb-2">MY ACTIVITIES</p>
<div className="grid sm:grid-cols-2 grid-cols-1 gap-2">
<GameComponent />
<MusicComponent />
</div>
<div className="my-2 space-y-6"> <div className="my-2 space-y-6">
<div> <div>
<h1 className="text-sm opacity-85 font-semibold">Fantasy</h1> <h1 className="text-sm opacity-85 font-semibold">Fantasy</h1>
+8
View File
@@ -93,6 +93,13 @@ type MLBTeam = {
score: number; score: number;
}; };
interface BlogPost {
id: number;
title: string;
description: string;
slug: string;
}
export type { export type {
LastFMMusicObject, LastFMMusicObject,
LastFMImageObject, LastFMImageObject,
@@ -104,4 +111,5 @@ export type {
BillsScore, BillsScore,
SabresScore, SabresScore,
YankeesScore, YankeesScore,
BlogPost,
}; };