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 :(" />;
}
+2 -24
View File
@@ -1,31 +1,9 @@
export default function ErrorState({
type = "error",
}: {
type: "error" | "loading" | "unavailable";
}) {
if (type === "error") {
export default function ErrorState({ label }: { label: string }) {
return (
<div className="flex flex-col bg-black text-white">
<div className="px-2 text-center">
<p>whoops, something went wrong</p>
<p>{label}</p>
</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>
);
}
}
+6 -18
View File
@@ -34,29 +34,17 @@ export default function GameComponent() {
fetchGame();
}, []);
if (game) {
return (
<a href={`https://medal.tv/u/alexav`} target="_blank">
<div className="flex flex-col bg-black border border-black text-white">
<div className="flex">
<Gamepad className="w-full h-full bg-white text-black" />
<div className="pl-2 pr-1">
<p className="w-[150px] truncate overflow-hidden whitespace-nowrap">
{game.currentlyPlaying.game}
</p>
<p>
{" "}
since{" "}
{new Date(
game.currentlyPlaying.startedPlayingAt,
).toLocaleTimeString()}
<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 justify-center items-center">
<div className="px-2">
<p className="text-sm font-semibold">
{game?.currentlyPlaying.game || "Nothing playing"}
</p>
</div>
</div>
</div>
</a>
);
} else {
return <ErrorState type="unavailable" />;
}
}
+13 -20
View File
@@ -1,10 +1,8 @@
import { useState, useEffect } from "react";
import type { LastFMMusicObject } from "../../utils/types";
import ErrorState from "../ErrorState";
export default function MusicComponent() {
const [music, setMusic] = useState<LastFMMusicObject | null>(null);
const [failedFetch, setFailedFetch] = useState<boolean>(false);
useEffect(() => {
async function fetchMusic() {
@@ -13,10 +11,9 @@ export default function MusicComponent() {
if (response.ok) {
const data = await response.json();
setMusic(data as LastFMMusicObject);
} else setFailedFetch(true);
}
} catch (e) {
console.error("Failed to fetch music!", e);
setFailedFetch(true);
}
}
@@ -26,27 +23,23 @@ export default function MusicComponent() {
fetchMusic();
}, []);
if (!failedFetch) {
if (music) {
return (
<a href="https://koito.fntz.net" target="_blank">
<div className="w-full bg-black">
<div className="text-center">
<div className="pl-2 pr-1">
<p className="text-lg">
<span className="font-semibold">Currently Playing - </span>
<span className="underline">{music.title}</span> by{" "}
<span className="underline">{music.artist}</span>
</p>
<div className="bg-white p-2 rounded-lg w-full min-h-full text-black">
<div className="flex justify-center items-center">
<img
src={
music?.images.filter((v) => v.size === "medium")[0]["url"] ||
music?.images[0].url
}
className="border-green-500 border-2"
/>
<div className="px-2">
<p className="text-sm font-semibold">{music?.title}</p>
<p className="text-xs">{music?.artist}</p>
</div>
</div>
</div>
</a>
);
} else {
return <ErrorState type="loading" />;
}
} else {
return <ErrorState type="error" />;
}
}
+2 -2
View File
@@ -67,9 +67,9 @@ export default function StatModule() {
</a>
);
} else {
return <ErrorState type="loading" />;
return <ErrorState label="loading.." />;
}
} 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 PictureCarousel from "../components/modules/PictureCarousel";
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() {
return (
@@ -43,6 +46,15 @@ export default function App() {
<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>
<div className="my-2 space-y-2">
<ProjectCard
@@ -81,7 +93,13 @@ export default function App() {
<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>
<h1 className="text-sm opacity-85 font-semibold">Fantasy</h1>
+8
View File
@@ -93,6 +93,13 @@ type MLBTeam = {
score: number;
};
interface BlogPost {
id: number;
title: string;
description: string;
slug: string;
}
export type {
LastFMMusicObject,
LastFMImageObject,
@@ -104,4 +111,5 @@ export type {
BillsScore,
SabresScore,
YankeesScore,
BlogPost,
};