This commit is contained in:
Alex Frantz
2024-04-18 02:32:20 -04:00
parent 4b23ee7444
commit 2a63fd119c
21 changed files with 358 additions and 592 deletions
-15
View File
@@ -1,15 +0,0 @@
import Head from '@/components/Head'
import Link from 'next/link'
export default function NotFoundPage() {
return (
<main className="bg-black flex min-h-screen">
<Head />
<div className="m-auto text-white text-center">
<h1 className="text-center text-2xl font-bold">Well, this is awkward..</h1>
<p>There is nothing here. An endless void, if you will.</p>
<p>How about we <span className="underline hover:text-gray-500"><Link href="/">head back?</Link></span> Or you could check out <span className="underline hover:text-gray-500"><Link target="_blank" href="https://www.youtube.com/watch?v=dQw4w9WgXcQ">this cool video I found.</Link></span></p>
</div>
</main>
)
}
+1 -1
View File
@@ -6,7 +6,7 @@ export default function Document() {
<Head>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link href="https://fonts.googleapis.com/css2?family=Poppins&display=swap" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=VT323&display=swap" rel="stylesheet" />
</Head>
<body>
<Main />
+16
View File
@@ -0,0 +1,16 @@
import RootLayout from "@/components/RootLayout";
import Footer from "@/components/footer";
import Header from "@/components/header";
export default function AboutPage() {
return (
<RootLayout>
<Header isNotHomePage={true} />
<div className="text-center text-green-500">
<h1 className="font-bold text-2xl">About this website</h1>
<p className="max-w-10">This is just a hobby project I thought was cool!<br /> I plan to play with it a bit to grasp more CSS, but so far, pretty cool right?</p>
</div>
<Footer />
</RootLayout>
)
}
-121
View File
@@ -1,121 +0,0 @@
// react modules
import {useState} from 'react'
import type {FormEvent} from 'react';
// trpc api
import { api } from "@/utils/api";
// components
import RootLayout from '@/components/RootLayout';
import InputOption from '@/components/InputOption';
export default function CreatePage() {
// ui states
const [message, setMessage] = useState<string | null>(null)
// trpc hooks
const linksMutation = api.links.create.useMutation()
function onLinkCreateSubmit(event: FormEvent<HTMLFormElement>) {
const form = event.target as HTMLFormElement
const urlRegex = new RegExp("^((https):\/\/)?(www.)?[a-z0-9]+\.[a-z]+(\/[a-zA-Z0-9#]+\/?)*$")
const titleElement = form.elements.namedItem('title') as HTMLInputElement;
const urlElement = form.elements.namedItem('url') as HTMLInputElement;
const orderElement = form.elements.namedItem("orderNumber") as HTMLInputElement;
const urlMatches = urlElement.value.match(urlRegex)
if (!titleElement.value || !urlElement.value || (orderElement.value && isNaN(Number(orderElement.value)))) {
event.preventDefault();
setMessage("One or more values were missing!")
setTimeout(() => setMessage(null), 5000)
} else if (urlMatches != null && !urlMatches[1]) {
event.preventDefault();
setMessage("An invalid URL was provided!")
setTimeout(() => setMessage(null), 5000)
} else {
const object = {
name: titleElement.value,
url: urlElement.value,
orderNumber: Number(orderElement.value) || 0
}
linksMutation.mutate(object)
}
}
function onLinkUpdateSubmit(event: FormEvent<HTMLFormElement>) {
const form = event.target as HTMLFormElement
const urlRegex = new RegExp("^((https):\/\/)?(www.)?[a-z0-9]+\.[a-z]+(\/[a-zA-Z0-9#]+\/?)*$")
const idElement = form.elements.namedItem("id") as HTMLInputElement;
const titleElement = form.elements.namedItem('title') as HTMLInputElement;
const urlElement = form.elements.namedItem('url') as HTMLInputElement;
const orderElement = form.elements.namedItem("orderNumber") as HTMLInputElement;
const urlMatches = urlElement.value.match(urlRegex)
if (!idElement.value || isNaN(Number(idElement.value)) || (orderElement.value && isNaN(Number(orderElement.value)))) {
event.preventDefault();
setMessage("One or more values were missing!")
setTimeout(() => setMessage(null), 5000)
} else if (urlMatches != null && !urlMatches[1]) {
event.preventDefault();
setMessage("An invalid URL was provided!")
setTimeout(() => setMessage(null), 5000)
} else {
const object = {
id: Number(idElement.value),
name: titleElement.value,
url: urlElement.value,
orderNumber: Number(orderElement.value) || 0
}
linksMutation.mutate(object)
}
}
return (
<RootLayout protectedPage={true}>
<div className="space-y-4">
<div className="w-96 p-2 mx-auto bg-gray-500 text-center text-white">
<h1 className="font-bold text-2xl">Create Link</h1>
<div className="pt-2 pb-2">
<hr />
</div>
<form onSubmit={onLinkCreateSubmit}>
<InputOption inputName="title" label="Title" />
<InputOption inputName="orderNumber" label="Order Number" />
<InputOption inputName="url" label="URL" />
<button className="bg-gray-800 text-white rounded-lg p-2" type="submit">Submit</button>
</form>
<p>{message}</p>
</div>
<div className="w-96 p-2 mx-auto bg-gray-500 text-center text-white">
<h1 className="font-bold text-2xl">Update Link</h1>
<div className="pt-2 pb-2">
<hr />
</div>
<form onSubmit={onLinkUpdateSubmit}>
<InputOption inputName="id" label="Link ID" />
<InputOption inputName="title" label="Title" />
<InputOption inputName="orderNumber" label="Order Number" />
<InputOption inputName="url" label="URL" />
<button className="bg-gray-800 text-white rounded-lg p-2" type="submit">Submit</button>
</form>
<p>{message}</p>
</div>
</div>
</RootLayout>
)
}
+83 -53
View File
@@ -1,61 +1,91 @@
import RootLayout from "@/components/RootLayout";
import MusicCard from "@/components/MusicCard";
import Link from 'next/link';
import { Cli } from "grommet-icons";
import RootLayout from '@/components/RootLayout';
import Header from '@/components/header';
import Footer from '@/components/footer';
import { useState, useEffect } from 'react';
export default function Home() {
const [doAnimation, setDoAnimation] = useState<boolean>(false);
const [pastLogo, setPastLogo] = useState<boolean>(false);
const [text, setText] = useState<string[]>([]);
const generateRandomTextString = () => {
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
const length = 66;
let result = "";
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * characters.length)) + " "
}
return result;
}
useEffect(() => {
if (location.href.includes("?noAnimation")) {
const logoElement = document.getElementById("pipboy");
const textElement = document.getElementById("textwall");
const mainElement = document.getElementById("mainelement");
if (logoElement && textElement && mainElement) {
logoElement.style.display = "none";
textElement.style.display = "none";
mainElement.style.display = "block";
}
} else {
setDoAnimation(true)
}
})
useEffect(() => {
if (doAnimation) {
const element = document.getElementById("pipboy")
if (element) {
setTimeout(() => {
element.style.display = "none";
setPastLogo(true)
}, 4000)
}
}
}, [doAnimation])
useEffect(() => {
const textElement = document.getElementById("textwall");
const mainElement = document.getElementById("mainelement");
if (pastLogo && doAnimation) {
const interval = setInterval(() => {
setText((prevState) => {
return [...prevState, generateRandomTextString()]
})
}, 50)
setTimeout(() => {
clearInterval(interval)
if (textElement && mainElement) {
textElement.style.display = "none"
mainElement.style.display = "block"
}
}, 5000)
}
}, [pastLogo, doAnimation])
return (
<RootLayout>
<div className="text-white">
<div className="text-center">
<h1 className="text-4xl font-bold">Alex Frantz</h1>
<p>QA Engineer, Zach Bryan Enthusiast</p>
</div>
<hr className="h-px my-2 bg-gray-200 border-0 w-1/2 mx-auto dark:bg-gray-700" />
<div className="my-4">
<div className="bg-terminalback lg:max-w-xl md:max-w-xl sm:max-w-full m-auto">
<div className="bg-terminalbar">
<div className="pt-2 pl-2">
<div className="bg-terminalback w-60 rounded-t-md">
<div className="p-2 pl-4 inline-block">
<Cli className="pr-2" />
<p className="inline">
Command Prompt
</p>
<p className="float-right ml-6">
<Link target="_blank" href="https://www.youtube.com/watch?v=dQw4w9WgXcQ">
X
</Link>
</p>
</div>
</div>
</div>
</div>
{/* content of cmd */}
<div className="p-2 font-mono">
<p>Michaelsoft Binbows [Version 10.0.23420.6969]</p>
<p>(c) Pixelpeek Development. No Rights Reserved.</p>
<br />
<p>C:\Users\CoolGuy{'>'}whoami</p>
<p>Full Name: <span className="font-bold">Alex Frantz</span></p>
<p>Based in: <span className="font-bold">Western New York</span></p>
<p>Working at: <span className="font-bold hover:text-medal underline"><Link target="_blank" href="https://medal.tv">Medal.tv</Link></span> as a <span className="font-bold">QA Engineer</span></p>
<p>Skills: <span className="font-bold">Java(Type)Script</span>, <span className="font-bold">Python</span>, <span className="font-bold">Go</span></p>
<p>Learning: <span className="font-bold">Network Engineering</span>, <span className="font-bold">Game Dev</span>, <span className="font-bold">Frontend</span>, <span className="font-bold">Backend</span></p>
<p>Website: <span className="font-bold underline hover:text-gray-500"><Link target="_blank" href="https://www.youtube.com/watch?v=dQw4w9WgXcQ">alexavfrantz.com</Link></span></p>
<br />
<p>C:\Users\CoolGuy{'>'}</p>
</div>
</div>
</div>
<hr className="h-px my-2 bg-gray-200 border-0 w-1/2 mx-auto dark:bg-gray-700" />
<div className="my-4">
<MusicCard />
{/* "logo", hidden after 4 seconds */}
<h1 className="text-green-500 text-9xl font-bold italic" id="pipboy">Pip-Boy</h1>
{/* text wall, hidden until pastLogo */}
<p className="text-green-500 text-2xl max-w-10 flex-shrink-0" id="textwall">{text}</p>
{/* and the content :) */}
<div className="text-green-500 flicker max-w-1/2 hidden" id="mainelement">
<Header isNotHomePage={false} />
<div className="text-left">
<p className="text-md">
<span className="font-bold text-2xl">Hello traveller!</span>
</p>
<p>Welcome to my website! Feel free to explore & learn more about me :)</p>
</div>
<Footer />
</div>
</RootLayout>
)
+31 -54
View File
@@ -1,61 +1,38 @@
import SocialCard from '@/components/SocialCard'
import Head from '@/components/Head';
import { api } from "@/utils/api";
import { useSession } from 'next-auth/react';
import Image from 'next/image';
import Link from 'next/link';
export default function Links() {
const {status} = useSession();
// trpc hooks
const { data: linkData } = api.links.getAll.useQuery()
const deleteMutation = api.links.delete.useMutation()
import RootLayout from "@/components/RootLayout";
import Footer from "@/components/footer";
import Header from "@/components/header";
import Link from "next/link";
export default function LinksPage() {
return (
<main className="bg-black flex flex-col justify-center items-center min-h-screen">
<Head />
<Link href="/">
<h1 className="text-white text-sm underline hover:text-gray-500 pb-2">&lt;- Back Home</h1>
</Link>
<div className="bg-zinc-900 p-10 rounded-sm">
<div className="pb-2 flex flex-col justify-center items-center">
<Image className="rounded-lg" width="90" height="90" alt="Profile Picture" src="https://storage.thatalex.dev/content/pfp.png" />
<div className="text-white text-center pt-2">
<h1>Alex</h1>
<p>I&apos;m Alex, a QA Engineer by day and developer by night</p>
<hr className="h-px my-2 bg-gray-200 border-0 dark:bg-gray-700" />
</div>
</div>
<RootLayout>
<Header isNotHomePage={true} />
<div className="text-green-500 text-center">
<h1 className="font-bold text-3xl">My Links!</h1>
<div className="space-y-2">
{
linkData && linkData.length > 0 ?
linkData.sort((a,b) => {
if (a.orderNumber < b.orderNumber) {
return -1
} else if (a.orderNumber > b.orderNumber) {
return 1
}
return 0
}).map((v,k) => {
return <SocialCard
name={v.name}
key={k}
id={v.id}
url={v.url}
deleteCallback={() => {
deleteMutation.mutate(v.id);
}}
isAuthenticated={ status === "authenticated" }
/>
})
:
<h1 className="text-center text-white font-bold">Uh oh, there are no links here!</h1>
}
<Link href="https://twitter.com/avvex__" target="_blank">
<div className="hover:bg-green-500 hover:text-black">
<h1 className="text-2xl mx-32">Twitter</h1>
</div>
</Link>
<Link href="https://mast.thatalex.dev/@alex" target="_blank">
<div className="hover:bg-green-500 hover:text-black">
<h1 className="text-2xl mx-32">Mastodon</h1>
</div>
</Link>
<Link href="https://linkedin.com/in/thatalex" target="_blank">
<div className="hover:bg-green-500 hover:text-black">
<h1 className="text-2xl mx-32">LinkedIn</h1>
</div>
</Link>
<Link href="https://medal.tv/users/215577" target="_blank">
<div className="hover:bg-green-500 hover:text-black">
<h1 className="text-2xl mx-32">Medal.tv</h1>
</div>
</Link>
</div>
</div>
</main>
<Footer />
</RootLayout>
)
}
+33 -63
View File
@@ -1,69 +1,39 @@
import ProjectCard from "@/components/ProjectsCard"
import RootLayout from "@/components/RootLayout"
import RootLayout from "@/components/RootLayout";
import Footer from "@/components/footer";
import Header from "@/components/header";
import Link from "next/link";
export default function Projects() {
export default function ProjectsPage() {
return (
<RootLayout>
<div className="text-white">
<div className="mb-4">
<h1 className="font-bold text-2xl text-center">Projects</h1>
</div>
<div className="space-y-2 w-full flex flex-col justify-center items-center">
<ProjectCard
name="SplitStat"
description="SplitStat was a Discord bot that allowed you to see your stats & other player stats from the Splitgate game. It would also allow you to see the results of an in-game match or what matches a player had played recently."
urlTo="https://github.com/azpha/splitstat"
technologies={
[
"JavaScript",
"Linux"
]
}
/>
<ProjectCard
name="medaltv-wrapper"
description="medaltv-wrapper is a Node.js wrapper for the Medal.tv Public API, allowing you to fetch clips from the platform and use the data in your code."
urlTo="https://github.com/azpha/medaltv-wrapper"
technologies={
[
"JavaScript"
]
}
/>
<ProjectCard
name="mcsrvstat-wrapper"
description="mcsrvstat-wrapper is a Node.js wrapper for MCSRVSTAT API, allowing you to fetch the status of Minecraft Java & Bedrock servers by running 1 method."
urlTo="https://github.com/azpha/mcsrvstat-js"
technologies={
[
"JavaScript"
]
}
/>
<ProjectCard
name="PManager"
description="PManager is a simple Express server that allows you to restart/stop/start your PM2 scripts easily remotely for uses like GitHub Actions."
urlTo="https://github.com/azpha/pmanager"
technologies={
[
"JavaScript"
]
}
/>
<ProjectCard
name="Watchlist"
description="Watchlist is a NextJS website built on the T3 Stack simply tracking my watch history, displaying stuff like my recently watched TV and Movies."
urlTo="https://github.com/azpha/tv-watchlist"
technologies={
[
"Typescript",
"NextJS",
"tRPC"
]
}
/>
</div>
<Header isNotHomePage={true} />
<div className="space-y-2 text-center">
<Link href="https://github.com/azpha/splitstat" target="_blank">
<div className="hover:bg-green-500 hover:text-black text-green-500">
<h1 className=" text-2xl mx-32">SplitStat</h1>
</div>
</Link>
<Link href="https://github.com/azpha/medaltv-wrapper" target="_blank">
<div className="hover:bg-green-500 hover:text-black text-green-500">
<h1 className=" text-2xl mx-32">Medal.tv Wrapper</h1>
</div>
</Link>
<Link href="https://github.com/azpha/mcsrvstat-js" target="_blank">
<div className="hover:bg-green-500 hover:text-black text-green-500">
<h1 className=" text-2xl mx-32">MCSRVSTAT Wrapper</h1>
</div>
</Link>
<Link href="https://github.com/azpha/pmanager" target="_blank">
<div className="hover:bg-green-500 hover:text-black text-green-500">
<h1 className=" text-2xl mx-32">PManager</h1>
</div>
</Link>
<Link href="https://github.com/azpha/tv-watchlist" target="_blank">
<div className="hover:bg-green-500 hover:text-black text-green-500">
<h1 className=" text-2xl mx-32">Watchlist</h1>
</div>
</Link>
<Footer />
</div>
</RootLayout>
)