Pre-Implement last commit date display and change fetch logic for projects and mini projects

This commit is contained in:
Space-Banane
2026-01-23 17:33:14 +01:00
parent b8c07cf27d
commit f6513286ec

View File

@@ -1,4 +1,4 @@
import { useEffect, useState } from "react"; import { useEffect, useState, useRef } from "react";
interface MiniProject { interface MiniProject {
title: string; title: string;
@@ -11,6 +11,7 @@ interface MiniProject {
color: string; color: string;
content: string; content: string;
}; };
last_commit?: Date;
} }
interface Experience { interface Experience {
@@ -322,6 +323,11 @@ function MiniProjectCard({
<p className="text-gray-400 text-sm line-clamp-2 mb-4"> <p className="text-gray-400 text-sm line-clamp-2 mb-4">
{project.description} {project.description}
</p> </p>
{project.last_commit && (
<p className="text-gray-400 text-xs mb-2">
Last commit: {project.last_commit.toLocaleDateString()}
</p>
)}
<div className="flex items-center text-purple-400 text-sm font-medium mt-auto"> <div className="flex items-center text-purple-400 text-sm font-medium mt-auto">
Click to learn more Click to learn more
@@ -438,6 +444,11 @@ function ProjectCard({ project }: { project: Project }) {
<p className="text-gray-400 text-sm flex-grow mb-6 leading-relaxed"> <p className="text-gray-400 text-sm flex-grow mb-6 leading-relaxed">
{project.description} {project.description}
</p> </p>
{project.last_commit && (
<p className="text-gray-400 text-xs mb-2">
Last commit: {project.last_commit.toLocaleDateString()}
</p>
)}
<div className="flex gap-3 mt-auto"> <div className="flex gap-3 mt-auto">
<a <a
@@ -479,6 +490,18 @@ function ProjectCard({ project }: { project: Project }) {
); );
} }
async function getLastCommitDate(url: string) {
const response = await fetch("https://shsf-api.reversed.dev/api/exec/6/c04c873c-6b75-4733-8636-bdaa962701c5", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ url: url }),
});
const data = await response.json();
return new Date(data.date);
}
function App() { function App() {
const [status, setStatus] = useState(""); const [status, setStatus] = useState("");
const [borderStatus, setBorderStatus] = useState("border-gray-700"); const [borderStatus, setBorderStatus] = useState("border-gray-700");
@@ -493,6 +516,11 @@ function App() {
const [selectedExperience, setSelectedExperience] = const [selectedExperience, setSelectedExperience] =
useState<Experience | null>(null); useState<Experience | null>(null);
// Replace hardcoded arrays with state
const [projects, setProjects] = useState<Project[]>([]);
const [miniProjects, setMiniProjects] = useState<MiniProject[]>([]);
const [experiences, setExperiences] = useState<Experience[]>([]);
const oldUsernames = [ const oldUsernames = [
"getspaced (ingame)", "getspaced (ingame)",
"Space² (alternative)", "Space² (alternative)",
@@ -605,6 +633,24 @@ function App() {
}; };
}, []); }, []);
// Fetch data from API on mount
useEffect(() => {
fetch("https://shsf-api.reversed.dev/api/exec/4/e942538c-caa1-49d1-8953-dfab1e62f8cb/projects")
.then(res => res.json())
.then(setProjects)
.catch(() => setProjects([]));
fetch("https://shsf-api.reversed.dev/api/exec/4/e942538c-caa1-49d1-8953-dfab1e62f8cb/mini_projects")
.then(res => res.json())
.then(setMiniProjects)
.catch(() => setMiniProjects([]));
fetch("https://shsf-api.reversed.dev/api/exec/4/e942538c-caa1-49d1-8953-dfab1e62f8cb/experience")
.then(res => res.json())
.then(setExperiences)
.catch(() => setExperiences([]));
}, []);
return ( return (
<div className="relative min-h-screen bg-black text-white selection:bg-purple-500/30"> <div className="relative min-h-screen bg-black text-white selection:bg-purple-500/30">
{/* Background Grid */} {/* Background Grid */}
@@ -623,7 +669,7 @@ function App() {
<main className="relative z-10 flex flex-col items-center px-6 py-20 mx-auto max-w-5xl gap-24"> <main className="relative z-10 flex flex-col items-center px-6 py-20 mx-auto max-w-5xl gap-24">
{/* Hero Section */} {/* Hero Section */}
<section className="flex flex-col items-center text-center space-y-8 animate-fade-in-down"> <section className="flex flex-col items-center text-center space-y-8 animate-fade-in">
<div className="relative group"> <div className="relative group">
<div <div
className={`absolute -inset-1 rounded-full blur opacity-75 transition duration-500`} className={`absolute -inset-1 rounded-full blur opacity-75 transition duration-500`}
@@ -1065,6 +1111,25 @@ function App() {
</div> </div>
</footer> </footer>
</main> </main>
{/* Floating Island: Bottom Left (only on localhost) */}
{typeof window !== "undefined" &&
(window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1") && (
<div
className="fixed bottom-4 left-4 z-50 flex flex-col items-start gap-2 bg-gradient-to-br from-purple-900/80 to-black/80 border border-purple-500/30 rounded-2xl shadow-lg px-5 py-3 text-sm animate-fade-in"
style={{ minWidth: 180 }}
>
<div className="flex items-center gap-2">
<span role="img" aria-label="island">🏝</span>
<span className="font-semibold text-purple-300">Loaded</span>
</div>
<div className="flex flex-col gap-1 mt-1">
<span className="text-white/90">Projects: <span className="font-bold text-purple-400">{projects.length}</span></span>
<span className="text-white/90">Mini Projects: <span className="font-bold text-purple-400">{miniProjects.length}</span></span>
<span className="text-white/90">Profile Data: <span className="font-bold text-purple-400">1/1</span></span>
</div>
</div>
)}
</div> </div>
); );
} }
@@ -1076,416 +1141,9 @@ interface Project {
link: string; link: string;
open_source: false | { link: string }; open_source: false | { link: string };
rounded?: boolean; rounded?: boolean;
last_commit?: Date;
} }
const projects: Project[] = [
{
name: "BetterNews",
description: "A news feed where you submit the news.",
image: "https://betternews.app/assets/icon.png",
open_source: false,
link: "https://betternews.app",
},
{
name: "SHSF",
description: 'Self-hostable "Cloud Functions" on your own hardware.',
link: "https://github.com/Space-Banane/shsf",
open_source: { link: "https://github.com/Space-Banane/shsf" },
image: "https://cdn.reversed.dev/pictures/shsf/SHSF.png",
},
{
name: "Thoughtful",
description: "A minimalistic and privacy-focused idea-taking app.",
link: "https://github.com/Space-Banane/thoughtful",
open_source: { link: "https://github.com/Space-Banane/thoughtful" },
image:
"https://em-content.zobj.net/source/microsoft-3D-fluent/433/thinking-face_1f914.png",
},
{
name: "3D Print Card",
description: "Showcase Your 3D Prints Beautifully",
link: "https://card.peakprinting.top",
image: "https://card.peakprinting.top/icon.png",
open_source: { link: "https://github.com/Space-Banane/imsoprintingit" },
},
{
name: "Whatsapp-Chat-Analyzer",
description:
"Analyze your Whatsapp chats with ease. Get insights, stats and more.",
image: "https://cdn.reversed.dev/pictures/wca.png",
open_source: { link: "https://github.com/Space-Banane/whatsapp-stats" },
link: "https://whatstat.reversed.dev",
},
{
name: "Instant Replay",
description: "Instantly replay your last 30 seconds of gameplay with the help of your Voice and OBS Replay Buffers.",
link: "https://gitea.reversed.dev/space/instant-replay",
open_source: { link: "https://gitea.reversed.dev/space/instant-replay" },
}
];
const miniProjects: MiniProject[] = [
{
title: "Discord Status to Website",
description: "Display your Discord status on your personal website.",
why: "I wanted to show my Discord status here on my portfolio.",
github: "https://github.com/Space-Banane/shsf-discord-status",
note: {
color: "red",
content: "Requires SHSF to run",
},
image:
"https://github.com/Space-Banane/shsf-discord-status/blob/main/Discord%20Status%20Image.png?raw=true",
},
{
title: "Mirror GoXLR to Discord",
description: "Muting your Mic Channel mutes you on Discord.",
why: "When i mute myself on my GoXLR, my friends don't know that, now they do.",
github: "https://github.com/Space-Banane/mirror-goxlr-to-discord",
note: {
color: "yellow",
content:
"Experimental - This might interfere with some other Hotkeys you set, or Applications that listen to those Hotkeys",
},
image:
"https://github.com/Space-Banane/mirror-goxlr-to-discord/blob/main/Discord%20x%20GoXLR.png?raw=true",
},
{
title: "Octo-Activity",
description: "Show your 3D Printer activity as Discord Status.",
why: "I wanted to show off my 3D Printer activity on Discord.",
github: "https://github.com/Space-Banane/discord-octo-activity",
image:
"https://github.com/Space-Banane/discord-octo-activity/blob/main/Discord%20Octo%20Activity.png?raw=true",
},
{
title: "Fishing Rod Automation Raft",
description: "Automate fishing in Raft with this simple script.",
why: "Fishing while Shitting is kinda difficult.",
github: "https://github.com/Space-Banane/raft-fishing",
note: {
color: "yellow",
content:
"Might interfere with other Applications that use Mouse/Keyboard input. In case of trust issues, move the mouse to the bottom right corner to stop the script.",
},
image:
"https://github.com/Space-Banane/raft-fishing/blob/main/Raft%20Fishing%20Guide%20-%20cover.jpg?raw=true",
},
{
title: "Nvidia-SMI Server",
description: "A simple server to expose Nvidia-SMI data via HTTP.",
why: "I wanted to monitor my GPU usage remotely without installing additional software.",
github: "https://github.com/Space-Banane/nvidia-smi-server",
image:
"https://github.com/Space-Banane/nvidia-smi-server/blob/main/Nvidia%20-%20SMI.png?raw=true",
},
{
title: "Lego Set to STL",
description: "Convert Lego set instructions to STL files for 3D printing.",
why: "A friend of mine searched for a website that does something like this, so i made one.",
github: "https://github.com/Space-Banane/lego-to-stl",
image: "https://shx.reversed.dev/u/44nekH.png",
},
{
title: "Thoughtful Extention for Raycast",
description: "Quickly add ideas to Thoughtful from Raycast.",
why: "I use Raycast a lot, so having a quick way to add ideas to Thoughtful is super convenient.",
github: "https://gitea.reversed.dev/space/thoughtful-extention",
note: {
color: "blue",
content: "Requires Thoughtful & Raycast to use lol",
},
image: "https://shx.reversed.dev/u/bCOaZt.png"
},
{
title: "Big File Generator",
description:
"Generate large files filled with random data for testing purposes.",
why: "SMB performance test, i did NOT have a big file lying around.",
github: "https://gitea.reversed.dev/space/big-file-gen",
image:
"https://em-content.zobj.net/source/microsoft-3D-fluent/433/file-folder_1f4c1.png",
},
{
title: "Active Directory Profile Picture Management",
description: "Manage and update Active Directory profile pictures easily.",
why: "I wanted a simple way to manage profile pictures in Active Directory. (home lab stuff)",
github: "https://github.com/Space-Banane/ADPPM",
image:
"https://shx.reversed.dev/u/RSqlwN.png",
note: {
color: "red",
content:
"Work in Progress + Requires Active Directory Environment + Requires Go to be installed",
},
},
{
title: "QrCode Generator",
description: "Vue.JS based QR Code Generator with a bit of customization.",
why: "I wanted a simple and customizable QR code generator.",
github: "https://github.com/reversed-dev/qr-code-gen",
image: "https://cdn.reversed.dev/pictures/qrcode.jpeg",
},
{
title: "Supertonic TTS Wrapper API",
description: "A simple API wrapper for the Supertonic TTS model.",
why: "I guess nobody made one thats deployable via docker yet?",
github: "https://github.com/Space-Banane/supertonic-wrapper",
image:
"https://shx.reversed.dev/u/cc6j2r.png",
note: {
color: "yellow",
content:
"Experimental. PRs are welcome to improve stability and features.",
},
},
{
title: "Clipboard on the GO😉",
description: "A simple clipboard sync service written in Go,",
why: "Windows sync was unreliable as hell",
github: "https://gitea.reversed.dev/space/clipboard-on-the-go",
image: "https://shx.reversed.dev/u/fnHPKP.png",
note: {
color: "blue",
content: "Requires Go to be installed; Docs are in windows only for now. Linux is probably still supported though.",
}
},
{
title: "Thoughtful Discord Bot",
description: "A Discord bot to quickly add ideas to Thoughtful.",
why: "Amazing idea i had while eating.",
github: "https://gitea.reversed.dev/space/thoughtful-dcbot",
image: "https://shx.reversed.dev/u/lQHAyz.png",
note: {
color: "blue",
content: "Requires Thoughtful to use lol",
}
}
];
const experiences: Experience[] = [
{
name: "TypeScript",
type: "language",
description:
"My go-to language for building scalable web applications with type safety.",
image:
"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/typescript/typescript-original.svg",
learned_because: "I HATE JavaScript, this makes it minimally better",
},
{
name: "JavaScript",
type: "language",
description: "The foundation of web development.",
image:
"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/javascript/javascript-original.svg",
},
{
name: "Python",
type: "language",
description:
"Used for automation scripts, data processing, and backend services.",
image:
"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/python/python-original.svg",
},
{
name: "Go",
type: "language",
description: "For building fast and efficient backend services.",
image:
"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/go/go-original.svg",
},
{
name: "Lua",
type: "language",
description: "Scripting and embedded applications.",
image:
"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/lua/lua-original.svg",
learned_from: "Youtube & Docs",
learned_because: "Used in some Robots in a Minecraft mod called CC:Tweaked",
},
{
name: "React",
type: "platform",
description:
"Building interactive user interfaces with modern React patterns and hooks.",
image:
"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/react/react-original.svg",
},
{
name: "Vue.js",
type: "platform",
description: "Creating reactive web applications with Vue's elegant API.",
image:
"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/vuejs/vuejs-original.svg",
},
{
name: "Docker",
type: "service",
description:
"Containerizing applications for consistent deployment across environments.",
image:
"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/docker/docker-original.svg",
},
{
name: "PostgreSQL",
type: "service",
description: "Reliable relational database for complex data storage needs.",
image:
"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/postgresql/postgresql-original.svg",
},
{
name: "Redis",
type: "service",
description: "High-performance caching and data structure store.",
image:
"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/redis/redis-original.svg",
},
{
name: "MariaDB",
type: "service",
description: "Open-source relational database management system.",
image:
"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/mariadb/mariadb-original.svg",
},
{
name: "Proxy Software",
type: "service",
description:
"Configuring and managing reverse proxies for web applications.",
},
{
name: "Home Assistant",
type: "platform",
description:
"Building smart home automations and integrating various IoT devices.",
image:
"https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/home-assistant.png",
},
{
name: "ESPHome",
type: "platform",
description:
"Programming ESP32 and ESP8266 microcontrollers for custom smart home sensors.",
image: "https://esphome.io/_images/logo.svg",
},
{
name: "Troubleshooting Hardware",
type: "real life experience",
description:
"Diagnosing and fixing hardware issues, from bricked microcontrollers to server problems.",
},
{
name: "Node.js",
type: "platform",
description: "Building backend services and APIs with Express and Fastify.",
image:
"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/nodejs/nodejs-original.svg",
},
{
name: "Immich",
type: "platform",
description: "Self-hosted photo and video management solution.",
image: "https://avatars.githubusercontent.com/u/109746326?v=4",
learned_because: "I hate OneDrive",
},
{
name: "Pangolin",
type: "platform",
description: "Proxy solution for network management.",
image:
"https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/pangolin.svg",
learned_because:
"My old proxy did NOT work super well with authentication.",
},
{
name: "Nginx",
type: "platform",
description: "Proxy solution for network management. (old proxy)",
image:
"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/nginx/nginx-original.svg",
},
{
name: "Minecraft Servers",
type: "platform",
description: "Hosting and managing multiplayer game servers.",
image:
"https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/minecraft.png",
},
{
name: "Gitea",
type: "platform",
description: "Self-hosted Git service for version control.",
image:
"https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/gitea.png",
},
{
name: "Serverless Functions",
type: "platform",
description: "Building scalable cloud functions and microservices. (SHSF)",
image:
"https://cdn.reversed.dev/pictures/shsf/SHSF%20SMALL%20TRANSPARENT.png",
},
{
name: "Cloud Infrastructure",
type: "platform",
description:
"Managing and deploying applications on cloud platforms. (GCP, AWS, DigitalOcean)",
},
{
name: "Git",
type: "service",
description:
"Version control for collaborative development and open-source contributions.",
image:
"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/git/git-original.svg",
},
{
name: "ShareX",
type: "platform",
description:
"Custom uploaders and automation workflows for screenshots and file sharing.",
image:
"https://upload.wikimedia.org/wikipedia/commons/d/d1/ShareX_Logo.png",
},
{
name: "Zipline",
type: "platform",
description: "ShareX companion for self-hosted file uploads.",
image: "https://media.sys.truenas.net/apps/zipline/icons/icon.png",
},
{
name: "Tailwind CSS",
type: "platform",
description: "Utility-first CSS framework for rapid UI development.",
image:
"https://upload.wikimedia.org/wikipedia/commons/thumb/d/d5/Tailwind_CSS_Logo.svg/2560px-Tailwind_CSS_Logo.svg.png",
},
{
name: "Proxmox VE",
type: "platform",
description:
"Virtualization management platform for running VMs and containers.",
image:
"https://media.printables.com/media/prints/543748/images/4374616_04ed4585-3662-4652-a84d-04621cb9f709/thumbs/inside/1280x960/png/proxmox.webp",
},
{
name: "MongoDB",
type: "service",
description: "NoSQL database for flexible document-based data storage.",
image:
"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/mongodb/mongodb-original.svg",
learned_because: "I didn't wanna mess around with schemas for a project.",
learned_at: "09.01.2026",
},
{
name: "Insomnia",
type: "platform",
description: "REST API client for testing and debugging HTTP requests.",
image:
"https://s3.amazonaws.com/s3.roaringapps.com/assets/icons/1561251841927-Insomnia.png",
learned_because: "Postman is bloatware",
},
];
export default App; export default App;