Add contact form
This commit is contained in:
parent
107ee8794d
commit
3866e8ade0
7
src/app/api/test/route.ts
Normal file
7
src/app/api/test/route.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { NextResponse } from "next/server";
|
||||||
|
|
||||||
|
export function GET (req: Request) {
|
||||||
|
console.log(req.headers.get("x-forwarded-for") ?? 'damn');
|
||||||
|
|
||||||
|
return NextResponse.json({ status: "ok" });
|
||||||
|
}
|
||||||
@ -22,7 +22,7 @@
|
|||||||
animation-timing-function: linear(0.2, 0.8, 1);
|
animation-timing-function: linear(0.2, 0.8, 1);
|
||||||
}
|
}
|
||||||
50% {
|
50% {
|
||||||
transform: translate(0px, -180px) scale(1.18, 0.9);
|
transform: translate(0px, -120px) scale(1.18, 0.9);
|
||||||
animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
|
animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
96
src/components/form/contact-form.tsx
Normal file
96
src/components/form/contact-form.tsx
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { ChangeEvent, useRef, useState } from "react";
|
||||||
|
import { CheckboxInput, Input, Submit } from "./form";
|
||||||
|
import { FloatingLabel } from "../floating-label";
|
||||||
|
|
||||||
|
export default function ContactForm() {
|
||||||
|
const [anon, setAnon] = useState(false);
|
||||||
|
const formRef = useRef<HTMLFormElement | null>(null);
|
||||||
|
const [status, setStatus] = useState<'success' | 'loading' | 'failed' | 'idle'>('idle');
|
||||||
|
const [errorMsg, setErrorMsg] = useState("");
|
||||||
|
|
||||||
|
const statusMsg = {
|
||||||
|
success: 'Sended!',
|
||||||
|
loading: 'Sending...',
|
||||||
|
failed: 'Failed :(',
|
||||||
|
idle: 'Send!'
|
||||||
|
}
|
||||||
|
|
||||||
|
const send = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setStatus('idle');
|
||||||
|
setErrorMsg('');
|
||||||
|
|
||||||
|
if (status == 'loading' || !formRef.current) return;
|
||||||
|
|
||||||
|
const formData = new FormData(formRef.current);
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
anon,
|
||||||
|
name: formData.get('name')?.toString(),
|
||||||
|
email: formData.get('email')?.toString(),
|
||||||
|
message: formData.get('message')!.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((data.name && data.email) && (data.name.length < 1 || data.email.length < 1) && !data.anon) {
|
||||||
|
setErrorMsg("Name and email are required. If you prefer not to provide them, check the box to send anonymously.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.message.length < 1) {
|
||||||
|
setErrorMsg("What do you want to tell me???");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setStatus('loading');
|
||||||
|
//process
|
||||||
|
|
||||||
|
await new Promise(res => setTimeout(res, 2000));
|
||||||
|
setStatus('success');
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form action="POST" className="space-y-4" ref={formRef} onSubmit={send}>
|
||||||
|
<FloatingLabel placeholder="Leave an anonymous message, but I won't answer it">
|
||||||
|
<CheckboxInput
|
||||||
|
name="anon"
|
||||||
|
placeholder="Send anonymously🤫"
|
||||||
|
onChange={setAnon}
|
||||||
|
/>
|
||||||
|
</FloatingLabel>
|
||||||
|
{!anon && <>
|
||||||
|
<label className="block" htmlFor="contact-name">
|
||||||
|
<div className="mb-2">Name</div>
|
||||||
|
<Input
|
||||||
|
id="contact-name"
|
||||||
|
className="w-full"
|
||||||
|
name="name"
|
||||||
|
type="text"
|
||||||
|
placeholder="Any name you want me to know"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label className="block" htmlFor="contact-email">
|
||||||
|
<div className="mb-2">Email</div>
|
||||||
|
<Input
|
||||||
|
id="contact-email"
|
||||||
|
className="w-full"
|
||||||
|
name="email"
|
||||||
|
type="email"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</>}
|
||||||
|
<label className="block" htmlFor="contact-msg">
|
||||||
|
<div className="mb-2">Message</div>
|
||||||
|
<textarea
|
||||||
|
name="message"
|
||||||
|
id="contact-msg"
|
||||||
|
className="max-h-96 h-32 w-full p-3 bg-background border border-primary"
|
||||||
|
placeholder="Tell me something cool, or ask question"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<div className="text-primary">{errorMsg}</div>
|
||||||
|
<Submit className="w-full">{statusMsg[status]}</Submit>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
49
src/components/form/form.tsx
Normal file
49
src/components/form/form.tsx
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import clsx from "clsx";
|
||||||
|
import { Icon } from "@iconify/react";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
export function Input ({ className, type, ...props }: React.ComponentProps<'input'>) {
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type={type}
|
||||||
|
className={clsx("block py-3 px-4 bg-background text-white border border-primary", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Submit ({ className, type, ...props }: React.ComponentProps<'button'>) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
type={'submit'}
|
||||||
|
className={clsx("block py-3 px-4 bg-primary text-background", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CheckboxInputProps {
|
||||||
|
name: string,
|
||||||
|
placeholder: string,
|
||||||
|
onChange: (value: boolean) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CheckboxInput ({ placeholder, name, onChange }: CheckboxInputProps) {
|
||||||
|
const [checked, setCheck] = useState(false);
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
setCheck(c => !c);
|
||||||
|
onChange(!checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="">
|
||||||
|
<button type="button" id={`checkbox-${name}`} className="inline-block bg-none border border-primary w-4 h-4" onClick={onClick}>
|
||||||
|
{checked && <Icon icon="lucide:check" className="text-primary" />}
|
||||||
|
</button>
|
||||||
|
<label htmlFor={`checkbox-${name}`} className="ps-2 inline-block">{placeholder}</label>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -2,11 +2,11 @@ import Image from "next/image"
|
|||||||
import { FloatingLabel } from "./floating-label"
|
import { FloatingLabel } from "./floating-label"
|
||||||
|
|
||||||
export const LandingImage = () => (
|
export const LandingImage = () => (
|
||||||
<FloatingLabel placeholder="Coral Glasses ❤️. Alert: NOT MY WORK! See the end of the page">
|
<FloatingLabel placeholder="ENA ❤️. Alert: NOT MY WORK!">
|
||||||
<Image
|
<Image
|
||||||
className="mb-8 mx-auto"
|
className="mb-8 mx-auto h-auto"
|
||||||
alt="Coral <3"
|
alt="Coral <3"
|
||||||
src="https://media1.tenor.com/m/RIP2rxKM_FgAAAAC/ena-ena-dream-bbq.gif"
|
src="https://media.tenor.com/1BCeG1aTiBAAAAAd/temptation-stairway-ena.gif"
|
||||||
width={280}
|
width={280}
|
||||||
height={280}
|
height={280}
|
||||||
unoptimized
|
unoptimized
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import Image from "next/image"
|
import Image from "next/image"
|
||||||
import Link from "@/components/link";
|
import Link from "@/components/link";
|
||||||
import { FloatingLabel } from "@/components/floating-label";
|
import { FloatingLabel } from "@/components/floating-label";
|
||||||
|
import ContactForm from "@/components/form/contact-form";
|
||||||
|
|
||||||
Welcome!
|
Welcome!
|
||||||
|
|
||||||
@ -8,6 +9,8 @@ This is our cozy little corner of the internet where we run services and website
|
|||||||
|
|
||||||
We've got tools, resources, game server and other stuff that just works and doesn't burn our wallet, maybe.
|
We've got tools, resources, game server and other stuff that just works and doesn't burn our wallet, maybe.
|
||||||
|
|
||||||
|
I love putting silly favorite characters on this site
|
||||||
|
|
||||||
## About Me
|
## About Me
|
||||||
|
|
||||||
Nomi Nonszy (also known as Nonszy, Nomi Nonsense, whatever).
|
Nomi Nonszy (also known as Nonszy, Nomi Nonsense, whatever).
|
||||||
@ -15,9 +18,9 @@ I write code, do some art, and make sure our server doesn't catch on fire lol.
|
|||||||
|
|
||||||
I build sick web apps with modern stacks. Big fan of open-source stuff.
|
I build sick web apps with modern stacks. Big fan of open-source stuff.
|
||||||
Hate something that make things overengineered and boilerplate, but still use them anyway.
|
Hate something that make things overengineered and boilerplate, but still use them anyway.
|
||||||
I only play indie games, and cooking up cursed mods just for fun.
|
Love with indie games, and cooking up cursed mods just for fun.
|
||||||
|
|
||||||
Multifandom with Psychopomp, Deltarune, and ENA! I'm currently dedicated to the ENA SERIES!! Especially <Link href="https://enajoelg.fandom.com/wiki/Coral_Glasses" target="_blank">Coral Glasses</Link> my beloved wife ❤️
|
Multifandom with Psychopomp, Deltarune, and ENA! I'm currently dedicated to the ENA SERIES!!
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
className="mb-12 mx-auto"
|
className="mb-12 mx-auto"
|
||||||
@ -87,6 +90,7 @@ and published under the ENA Team. She speaks Korean in the game and is voiced by
|
|||||||
|
|
||||||
The game is part of the surreal and artistically distinct ENA universe, which expands upon his earlier animated web series of the same name.
|
The game is part of the surreal and artistically distinct ENA universe, which expands upon his earlier animated web series of the same name.
|
||||||
|
|
||||||
She's supposed to handle business stuff, but she's sweating, faxing out of her hairline, and spiraling into mild panic every five minutes
|
|
||||||
|
|
||||||
She's cute, anxious, awkward, weird, beautiful and i swear, she's literally me at work sweating through every conversation ashdjakwoiqhkaslchmaujqk
|
## Tell me something
|
||||||
|
|
||||||
|
<ContactForm />
|
||||||
@ -7,7 +7,7 @@ const events: EventsDate[] = [
|
|||||||
{
|
{
|
||||||
name: 'christmas',
|
name: 'christmas',
|
||||||
start: [12, 20],
|
start: [12, 20],
|
||||||
end: [12, 30]
|
end: [12, 32]
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user