improved website

This commit is contained in:
2024-07-07 22:16:55 -04:00
parent fae0c95aa1
commit 82ee8fddd1
11 changed files with 278 additions and 191 deletions
+128
View File
@@ -0,0 +1,128 @@
import { useState, useEffect } from 'react';
import InputBox from './InputBox';
export default function ContactForm() {
const [ error, setError ] = useState<string>("");
const [ success, setSuccess ] = useState<boolean>(false);
const [ inputState, setInputState ] = useState<{name: string, email: string, subject: string, message: string}>({
name: "",
email: "",
subject: "",
message: ""
});
const onSubmit = () => {
if (
inputState.name === ""
|| inputState.email === ""
|| inputState.subject === ""
|| inputState.message === ""
) {
setError("A required field was missing")
} else {
console.log("Form submitted")
fetch("https://api.alexav.gg/v4/contact", {
method: "post",
headers: {
'content-type': 'application/json'
},
body: JSON.stringify(inputState)
}).then((res) => {
if (res.ok) {
setSuccess(true)
} else {
setError("An error occurred while submitting your info.")
}
}).catch((e) => {
console.error(e)
setError("An error occurred while submitting your info.")
})
}
}
useEffect(() => {
setTimeout(() => {
setError("")
}, 6000)
}, [error]);
useEffect(() => {
setTimeout(() => {
setSuccess(false)
}, 6000)
})
return (
<div className="bg-white rounded-lg text-black p-2">
<h1 className="text-2xl font-bold">Contact Me</h1>
<hr className="my-2 border-black" />
<div className="space-y-2">
<InputBox
isText={true}
name="name"
placeholder="Name.."
onChange={(value: string) => {
setInputState((prevState) => {
return {
...prevState,
name: value
}
})
}}
/>
<InputBox
name="email"
isText={true}
placeholder="Email.."
onChange={(value: string) => {
setInputState((prevState) => {
return {
...prevState,
email: value
}
})
}}
/>
<InputBox
isText={true}
name="subject"
placeholder="Subject.."
onChange={(value: string) => {
setInputState((prevState) => {
return {
...prevState,
subject: value
}
})
}}
/>
<InputBox
isText={false}
name="message"
placeholder="Message.."
onChange={(value: string) => {
setInputState((prevState) => {
return {
...prevState,
message: value
}
})
}}
/>
</div>
<div className="block">
<button
type="button"
className="bg-black w-full text-white font-bold rounded-lg p-2"
onClick={onSubmit}
>
Submit
</button>
<p className="text-red-400 font-bold text-center">{error}</p>
<p className="font-bold text-center">{ success && "Success! Your message has been received"}</p>
</div>
</div>
)
}
+2 -1
View File
@@ -15,8 +15,9 @@ export default function Footer() {
}, [mailState])
return (
<div className="bg-zinc-600 text-center text-white absolute bottom-0 left-0 right-0 p-4 select-none">
<div className="bg-zinc-600 text-center text-white bottom-0 left-0 right-0 p-4 select-none">
<h1 className="font-bold">&copy; {new Date().getFullYear()}, Alex Frantz. All Rights Reserved</h1>
<p>All logos & images on this page belong to their respective owners.</p>
{
mailState ? (
+1 -5
View File
@@ -1,12 +1,8 @@
export default function Header() {
return (
<div className="text-white pt-4">
<div className="text-white pt-4 mx-auto lg:w-1/2 sm:w-full">
<h1 className="font-bold text-2xl">Alex Frantz</h1>
<p>Professional button clicker & word typer</p>
<div className="inline-block space-x-2">
<a className="font-bold hover:underline" href="/">Home</a>
<a className="font-bold hover:underline" href="experience">Experience</a>
</div>
<hr className="my-4" />
</div>
+36
View File
@@ -0,0 +1,36 @@
export default function InputBox({
name,
placeholder,
isText = true,
onChange
}: {
name: string,
isText?: boolean,
placeholder: string,
onChange: (value: string) => void
}) {
if (isText) {
return (
<div className="block">
<input
type="text"
name={name}
placeholder={placeholder}
className="bg-black rounded-lg p-2 text-white w-full"
onChange={({target}) => onChange(target.value)}
/>
</div>
)
} else {
return (
<div className="block">
<textarea
name={name}
placeholder={placeholder}
onChange={({ target }) => onChange(target.value)}
className="bg-black rounded-lg p-2 text-white w-full h-32"
/>
</div>
)
}
}
+20
View File
@@ -0,0 +1,20 @@
export default function ListBox({
title,
points
}: {
title: string,
points: string[]
}) {
return (
<div className="bg-slate-100 text-black rounded-lg p-4">
<h3 className="text-lg font-bold">{title}</h3>
<ul className="mt-2 space-y-2 text-muted-foreground">
{
points.map((v,k) => {
return <li key={k}>{v}</li>
})
}
</ul>
</div>
)
}
+19 -38
View File
@@ -1,51 +1,32 @@
export default function Project({
name,
image,
description,
urlTo,
subtitle,
bullets
urlTo
}: {
name: string,
image: string,
description: string,
urlTo?: string,
subtitle?: string,
bullets?: string[]
}) {
const mapBullets = () => {
return bullets?.map((v,k) => {
return <li className="list-inside list-disc" key={k}>{v}</li>
})
}
return (
<div className="bg-white text-black p-2">
<div className="float-left">
<p className="pl-2 text-2xl font-bold">{name}
{
urlTo ? (
<a className="text-sm hover:underline pl-2" target="_blank" href={urlTo}>
Project Website
</a>
) : (
<span className="text-base pl-2">
{subtitle}
</span>
)
<a href={urlTo} target="_blank">
<div className="bg-white text-black max-w-fit mx-auto rounded-lg overflow-hidden">
<img
src={image}
width="400"
height="300"
alt={name}
className="w-full h-48 object-cover"
/>
}
</p>
<div className="p-4 space-y-2">
<h3 className="text-xl font-bold">{name}</h3>
<p className="text-muted-foreground">
{description}
</p>
</div>
</div>
<div className="clear-left text-left pl-2.5">
<p>{description}</p>
{
bullets && (
<ul>
{mapBullets()}
</ul>
)
}
</div>
</div>
</a>
)
}