Files
my-portfolio/app/routes/_index.tsx

603 lines
21 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { MetaFunction } from "@remix-run/node";
import { useEffect, useState } from "react";
import { json } from "@remix-run/node";
import { useLoaderData, Link } from "@remix-run/react";
export const meta: MetaFunction = () => {
return [
{ title: "Paul W." },
{ name: "description", content: "This is my Profile!" },
{
name: "keywords",
content: "Paul W, Paul W Portfolio, Paul W Profile, Paul W Remix",
},
];
};
interface BlogPost {
id: string;
title: string;
excerpt: string;
date: string;
access: string; // Where its located in the folder
tags: string[];
hide_date?: boolean;
}
export async function loader() {
const posts: BlogPost[] = [
{
id: "1",
title: "My First Post",
excerpt: "Yeah, as it says, this is my first post.",
date: "2024-01-23",
access: "myfirstpost",
tags: ["Hello, World!"],
hide_date: true,
},
// Add more posts here
];
return json({ posts });
}
export default function Index() {
const { posts } = useLoaderData<typeof loader>();
const [status, setStatus] = useState("");
const [border_status, setBorderStatus] = useState("border-gray-700");
useEffect(() => {
if (status === "online") {
setBorderStatus("border-green-500");
} else if (status === "offline") {
setBorderStatus("border-red-500");
} else if (status === "dnd") {
setBorderStatus("border-red-600");
} else if (status === "idle") {
setBorderStatus("border-yellow-500");
} else {
setBorderStatus("border-gray-700");
}
}, [status]);
useEffect(() => {
fetch(
"https://shsf-api.cottonfieldworkers.shop/api/exec/6/c084ec4a-1b20-491e-ab2e-67c5fa8881e6"
)
.then((res) => res.json())
.then((data) => {
setStatus(data.status);
})
.catch((err) => {
console.error(err);
setStatus("offline");
});
}, []);
return (
<div className="relative flex min-h-screen flex-col items-center justify-center p-8 overflow-hidden">
<div className="absolute inset-0 z-0">
<div className="h-full w-full bg-black">
<div
className="absolute inset-0"
style={{
backgroundImage:
"linear-gradient(#ffffff15 1px, transparent 1px), linear-gradient(90deg, #ffffff15 1px, transparent 1px)",
backgroundSize: "20px 20px",
}}
/>
</div>
</div>
<div className="relative z-10 flex flex-col items-center gap-8 mt-12 max-w-6xl w-full">
{/* Hero */}
<div className="text-center space-y-6">
<div className="space-y-2">
<h1
className="text-5xl md:text-6xl font-bold bg-gradient-to-r from-blue-400 to-purple-500 bg-clip-text text-transparent"
style={{ lineHeight: "normal" }}
>
Hey, I'm Paul
</h1>
<h2 className="text-3xl md:text-4xl text-gray-400">
also known as{" "}
<span className="font-bold text-gray-200">Space</span> online
</h2>
</div>
<div
className={`w-48 h-48 mx-auto overflow-hidden rounded-full border-4 transition-colors duration-300 ${border_status}`}
>
<img
src="https://cdn.reversed.dev/pictures/cat.png"
alt="Paul W"
className="w-full h-full object-cover"
/>
</div>
<div className="space-y-3">
<span className="px-4 py-2 text-lg bg-blue-900/50 text-blue-100 rounded-full inline-block backdrop-blur-sm">
Developer & Gamer
</span>
<p className="text-xl text-gray-300">Self-proclaimed Dev</p>
</div>
</div>
{/* About Section */}
<section className="w-full">
<h2 className="text-3xl font-bold mb-8 text-center text-gray-100 bg-gradient-to-r from-blue-500 to-purple-500 bg-clip-text text-transparent">
About Me
</h2>
<div
className="p-8 rounded-xl border border-gray-700 bg-white/5 backdrop-blur-lg
flex flex-col md:flex-row gap-8 items-center"
>
<div className="flex-1 space-y-6">
<p className="text-gray-300 text-lg leading-relaxed">
Hey! I'm <span className="underline font-bold">Paul</span>, a
passionate Developer from{" "}
<span className="bg-gradient-to-r from-gray-800 via-red-500 to-yellow-400 font-bold bg-clip-text text-transparent">
Germany
</span>{" "}
🇩🇪.
<span className="block mt-4">
I specialize in JavaScript and Node.js development, crafting
digital experiences that make a difference. When I'm not
coding, you'll find me gaming or scrolling through my
Instagram feed.
</span>
</p>
<div className="bg-white/5 p-6 rounded-lg backdrop-blur-sm">
<h3 className="text-xl font-bold text-gray-100 mb-4 bg-gradient-to-r from-blue-400 to-purple-400 bg-clip-text text-transparent">
Fun Facts About Me
</h3>
<div className="grid grid-cols-2 gap-4">
<div className="flex items-center gap-3 text-gray-300 hover:text-blue-400 transition-colors">
<span className="text-2xl">🚲</span>
<span>Avid Cyclist</span>
</div>
<div className="flex items-center gap-3 text-gray-300 hover:text-blue-400 transition-colors">
<span className="text-2xl">🎮</span>
<span>Gaming Enthusiast</span>
</div>
<div className="flex items-center gap-3 text-gray-300 hover:text-blue-400 transition-colors">
<span className="text-2xl">🛠</span>
<span>Builder at Heart</span>
</div>
</div>
</div>
</div>
</div>
</section>
{/* My Blog Posts */}
<section className="w-full">
<h2
className="text-3xl font-bold mb-2 text-center text-gray-100 bg-gradient-to-r from-blue-500 to-purple-500 bg-clip-text text-transparent"
style={{ lineHeight: "normal" }}
>
Blog Posts
</h2>
<p className="text-gray-300 text-lg mb-4 text-center">
Man i love this posting thing, can't you tell?
</p>
<div
className="p-8 rounded-xl border border-gray-700 bg-white/5 backdrop-blur-lg
flex flex-col md:flex-row gap-8 items-center"
>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8 text-center">
{posts.map((post) => (
<article
key={post.id}
className="p-6 rounded-xl border border-gray-700 bg-gray-800
transform transition-all duration-300 hover:scale-105 hover:shadow-xl
backdrop-blur-sm hover:bg-opacity-90"
>
<div className="flex flex-wrap gap-2 mb-3">
{!post.hide_date && (
<time className="text-sm text-gray-500">
{new Date(post.date).toLocaleDateString()}
</time>
)}
{post.tags.map((tag) => (
<span
key={tag}
className="px-3 py-1 text-sm bg-gray-900 text-gray-100 rounded-full"
>
{tag}
</span>
))}
</div>
<h3 className="text-xl font-bold mb-3 text-gray-100">
{post.title}
</h3>
<p className=" text-gray-400 text-base mb-4">
{post.excerpt}
</p>
<Link
to={`/blog/${post.access}`}
className="inline-flex items-center px-6 py-2 bg-gradient-to-r from-blue-500 to-purple-500
text-white rounded-full font-medium transition-all duration-300
hover:from-blue-600 hover:to-purple-600 hover:shadow-lg"
>
Read More
<svg
className="w-4 h-4 ml-2"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M14 5l7 7m0 0l-7 7m7-7H3"
/>
</svg>
</Link>
</article>
))}
</div>
</div>
</section>
{/* My Dawg */}
<section className="w-full">
<h2
className="text-3xl font-bold mb-8 text-center text-gray-100 bg-gradient-to-r from-blue-500 to-purple-500 bg-clip-text text-transparent"
style={{ lineHeight: "normal" }}
>
My Dawg
</h2>
<div className="p-4 sm:p-8 rounded-xl border border-gray-700 bg-white/5 backdrop-blur-lg flex flex-col gap-8 items-center">
<div className="flex-1 space-y-6 w-full">
<img
src="https://cdn.reversed.dev/pictures/20250103_121234.jpg"
alt="Charly Image"
className="w-full h-64 sm:h-96 object-cover rounded-lg shadow-lg"
/>
<p className="text-gray-300 text-base sm:text-lg leading-relaxed">
This is Charly🙏 This lil guy is about{" "}
<span className="italic">13</span> Years old. Very mixed.
</p>
<div className="bg-white/5 p-4 sm:p-6 rounded-lg backdrop-blur-sm">
<h3 className="text-xl font-bold text-gray-100 mb-4 bg-gradient-to-r from-blue-400 to-purple-400 bg-clip-text text-transparent">
Charly
</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 bg-gray-800 p-4 rounded-lg">
<div className="flex items-center gap-3 text-gray-300 hover:text-blue-400 transition-colors">
<span className="text-2xl">🗣️</span>
<span>the guy is a little deaf</span>
</div>
<div className="flex items-center gap-3 text-gray-300 hover:text-blue-400 transition-colors">
<span className="text-2xl">🚶</span>
<span>Runs faster than me</span>
</div>
<div className="flex items-center gap-3 text-gray-300 hover:text-blue-400 transition-colors">
<span className="text-2xl">🐶</span>
<span>Barks</span>
</div>
<div className="flex items-center gap-3 text-gray-300 hover:text-blue-400 transition-colors">
<span className="text-2xl">🐕</span>
<span>Is a Dog (probably)</span>
</div>
</div>
</div>
</div>
</div>
</section>
{/* Projects Section */}
<section className="w-full">
<h2 className="text-3xl font-bold mb-8 text-gray-100 bg-gradient-to-r from-blue-500 to-purple-500 bg-clip-text text-transparent">
Projects
</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8 text-center">
{projects.map((project, index) => (
<div
key={index}
className="p-6 rounded-xl border border-gray-700 bg-gray-800
transform transition-all duration-300 hover:scale-105 hover:shadow-xl
backdrop-blur-sm hover:bg-opacity-90"
>
{project.image && (
<div className="mb-6 items-center justify-center flex flex-col">
<img
src={project.image}
alt={project.name}
className="h-40 w-40 object-cover rounded-full shadow-lg
outline outline-gray-600 outline-2 hover:outline-blue-500
transition-all duration-300"
/>
</div>
)}
<h3 className="text-xl font-bold mb-3 text-gray-100">
{project.name}
</h3>
<p className=" text-gray-400 text-base mb-4">
{project.description}
</p>
<div className="flex flex-wrap justify-center gap-3 mt-4">
<a
href={
project.link +
"?utm_source=portfolio&utm_medium=referral&ref=space"
}
target="_blank"
className="inline-flex items-center px-4 py-2 bg-gradient-to-r from-blue-500 to-purple-500
text-white rounded-lg font-medium transition-all duration-300
hover:from-blue-600 hover:to-purple-600 hover:shadow-lg hover:scale-105"
>
<span>Visit Project</span>
<svg
className="w-4 h-4 ml-2"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
/>
</svg>
</a>
{project.open_source && (
<a
href={project.open_source.link}
target="_blank"
className="inline-flex items-center px-4 py-2 bg-gray-800
text-gray-200 rounded-lg font-medium transition-all duration-300
hover:bg-gray-700 hover:shadow-lg hover:scale-105 border border-gray-600"
>
<svg
className="w-4 h-4 mr-2"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
</svg>
<span>Source Code</span>
</a>
)}
{project.blog_post && (
<a
href={project.blog_post}
className="inline-flex items-center px-4 py-2 bg-gray-800
text-gray-200 rounded-lg font-medium transition-all duration-300
hover:bg-gray-700 hover:shadow-lg hover:scale-105 border border-gray-600"
>
<svg
className="w-4 h-4 mr-2"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"
/>
</svg>
<span>Read Blog</span>
</a>
)}
</div>
</div>
))}
</div>
</section>
{/* Contact */}
<section className="w-full">
<h2 className="text-3xl font-bold mb-8 text-center text-gray-100 bg-gradient-to-r from-blue-500 to-purple-500 bg-clip-text text-transparent">
Contact
</h2>
<div
className="p-6 rounded-xl border border-gray-700 bg-white/5 backdrop-blur-lg
transform transition-all duration-300 hover:scale-105 hover:shadow-xl"
>
<p className="text-gray-300 text-lg text-center">
You can contact me via Email or on Discord. I'm always open for a
chat or a <span className="line-through">coffee</span> tea🫖
</p>
<div className="flex flex-col sm:flex-row gap-3 justify-center mt-6">
<a
href="mailto:paul.w@betternews.app"
className="px-6 py-2 bg-gradient-to-r from-blue-500 to-purple-500
text-white rounded-full font-medium transition-all duration-300
hover:from-blue-600 hover:to-purple-600 hover:shadow-lg"
>
Email
</a>
<a
href="https://discord.com/users/456443941169004545"
target="_blank"
className="px-6 py-2 bg-gradient-to-r from-blue-500 to-purple-500
text-white rounded-full font-medium transition-all duration-300
hover:from-blue-600 hover:to-purple-600 hover:shadow-lg"
>
Discord
</a>
</div>
</div>
</section>
{/* My "Skills" */}
<section className="w-full">
<h2 className="text-3xl font-bold mb-8 text-center text-gray-100 bg-gradient-to-r from-blue-500 to-purple-500 bg-clip-text text-transparent">
Skills
</h2>
<p className="text-gray-300 text-lg mb-4 text-center">
Skills? Whats that? Never heard of it🤔
</p>
<div
className="p-6 rounded-xl border border-gray-700 bg-white/5 backdrop-blur-lg
hover:shadow-xl"
>
<ul className="space-y-4 text-center">
{skills.map((skill, index) => (
<li
key={index}
className="flex items-center gap-4 text-gray-300 text-lg transition-all duration-300 hover:text-blue-400"
>
{skill.image && (
<img
src={skill.image}
alt={skill.name}
className="h-12 w-12 object-cover rounded-full shadow-lg"
/>
)}
<span>
{skill.name} - {skill.description}
</span>
</li>
))}
</ul>
</div>
</section>
{/* What's Next Section */}
<section className="w-full">
<h2 className="text-3xl font-bold mb-8 text-center text-gray-100 bg-gradient-to-r from-blue-500 to-purple-500 bg-clip-text text-transparent">
What's Next?
</h2>
<div
className="p-6 rounded-xl border border-gray-700 bg-white/5 backdrop-blur-lg
transform transition-all duration-300 hover:scale-105 hover:shadow-xl"
>
<ul className="space-y-4 text-center">
<li className="text-gray-300 text-lg transition-all duration-300 hover:text-blue-400">
• Learning more ways to build Apps
</li>
<li className="text-gray-300 text-lg transition-all duration-300 hover:text-blue-400">
• Building more fun little open-source Projects
</li>
<li className="text-gray-300 text-lg transition-all duration-300 hover:text-blue-400">
• Working on existing Projects
</li>
</ul>
</div>
</section>
</div>
</div>
);
}
const skills: {
name: string;
description: string;
image?: string;
}[] = [
{
name: "JavaScript",
description:
"I'm a JavaScript Developer with a lot of experience in JS. I love to build things with JS. Js my beloved",
image: "https://cdn.reversed.dev/pictures/languages/js_logo.png",
},
{
name: "Vue.JS",
description:
"I have not much experience with Vue.js, but i its fun to play around with.",
image: "https://cdn.reversed.dev/pictures/languages/vue.png",
},
{
name: "Node.js",
description:
"Node.js is my go-to Framework for Backend Development. I love to build things with Node.js.",
image: "https://cdn.reversed.dev/pictures/languages/nodejs.jpg",
},
{
name: "TailwindCSS",
description: "TailwindCSS is my go-to CSS Framework. TailwindCss 🙏🙏🙏",
image: "https://cdn.reversed.dev/pictures/languages/tailwind.png",
},
{
name: "TypeScript",
description:
"TypeScript is my go-to Language for larger Projects with type-safety. Typescript 🔥",
image: "https://cdn.reversed.dev/pictures/languages/ts.png",
},
{
name: "Docker",
description: "Docker is the best Containerization Tool imo. ",
image: "https://cdn.reversed.dev/pictures/languages/docker.png",
},
{
name: "HTML",
description: "HTML is the language of the Web. I dont need to say more.",
image:
"https://imagedelivery.net/5MYSbk45M80qAwecrlKzdQ/51703b85-ef3f-4d45-fae0-c39d4c733900/preview",
},
];
const projects: {
name: string;
description: string;
image?: string;
open_source: { link: string } | false;
link: string;
blog_post?: string;
}[] = [
{
name: "BetterNews",
description:
"A news aggregator app, custom built. No ads, no tracking, just news.",
image: "https://betternews.app/assets/icon.png",
open_source: false,
link: "https://betternews.app",
},
{
name: "Reversed.Dev",
description:
"An upcomming 'Dev Studio' with a lot of cool Projects. Stay tuned!",
image: "https://cdn.reversed.dev/pictures/icon.png",
open_source: false,
link: "https://reversed.dev",
},
{
name: "Luna",
description: "Luna is an All-In-One Discord Bot with a lot of AI features.",
image: "https://cdn.reversed.dev/pictures/luna/lunasmol.jpg",
open_source: false,
link: "https://luna.reversed.dev",
},
{
name: "Whatsapp-Chat-Analyzer",
description:
"A simple Chat Analyzer for WhatsApp Chats. All private, all yours.",
image: "https://cdn.reversed.dev/pictures/wca.png",
open_source: { link: "https://github.com/Space-Banane/whatsapp-stats" },
link: "https://whatstat.reversed.dev",
},
{
name: "Free QrCode Generator",
description:
"A simple QR Code Generator for free. No tracking, no ads, just QR Codes. Just like we love em.",
image: "https://cdn.reversed.dev/pictures/qrcode.jpeg",
open_source: { link: "https://github.com/reversed-dev/qr-code-gen" },
link: "https://qr.reversed.dev",
},
{
name: "Tyler-The-Creator Countdown",
description:
"A simple Countdown for the next Tyler-The-Creator Album 'Chromokopia'. Just for fun, and because i can.",
link: "https://tyler.reversed.dev",
open_source: {
link: "https://github.com/Space-Banane/tylerthecreatorcounter",
},
image: "https://i.scdn.co/image/ab67616d00001e02124e9249fada4ff3c3a0739c",
},
{
name: "Open Web UI - Memory Importer",
description:
"A simple Memory Importer for the Open Web UI. Just a fun little project.",
link: "https://github.com/Space-Banane/openweb-memory-importer",
open_source: {
link: "https://github.com/Space-Banane/openweb-memory-importer",
},
},
];