UI updates

This commit is contained in:
Michael Rausch
2025-10-12 00:58:56 +13:00
parent 237668fd31
commit 1a40e6e718
4 changed files with 151 additions and 35 deletions
+120
View File
@@ -0,0 +1,120 @@
---
// Contact Form Component
---
<div id="form-message" class="hidden mb-4"></div>
<form id="contact-form" class="space-y-6 contact-form-wrapper">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="space-y-2">
<label for="name" class="block text-sm text-black/70 dark:text-white/60">
Name
</label>
<input
type="text"
id="name"
name="name"
required
class="w-full px-4 py-3 rounded-md border border-black/5 dark:border-white/5 bg-transparent text-black dark:text-white placeholder-black/30 dark:placeholder-white/30 focus:outline-none focus:border-black/20 dark:focus:border-white/20 transition-all duration-300"
placeholder="Your name"
/>
</div>
<div class="space-y-2">
<label for="email" class="block text-sm text-black/70 dark:text-white/60">
Email
</label>
<input
type="email"
id="email"
name="email"
required
class="w-full px-4 py-3 rounded-md border border-black/5 dark:border-white/5 bg-transparent text-black dark:text-white placeholder-black/30 dark:placeholder-white/30 focus:outline-none focus:border-black/20 dark:focus:border-white/20 transition-all duration-300"
placeholder="your.email@example.com"
/>
</div>
</div>
<div class="space-y-2">
<label for="message" class="block text-sm text-black/70 dark:text-white/60">
Message
</label>
<textarea
id="message"
name="message"
required
rows="5"
class="w-full px-4 py-3 rounded-md border border-black/5 dark:border-white/5 bg-transparent text-black dark:text-white placeholder-black/30 dark:placeholder-white/30 focus:outline-none focus:border-black/20 dark:focus:border-white/20 transition-all duration-300 resize-none"
placeholder="Your message..."
></textarea>
</div>
<div>
<button
type="submit"
class="px-8 py-3 rounded-md bg-black/90 dark:bg-white/90 text-white dark:text-black font-medium hover:bg-black dark:hover:bg-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black/50 dark:focus:ring-white/50 transition-all duration-300 disabled:opacity-40 disabled:cursor-not-allowed"
>
<span id="button-text">Send Message</span>
</button>
</div>
</form>
<script>
document.addEventListener('DOMContentLoaded', () => {
const form = document.getElementById('contact-form') as HTMLFormElement;
const buttonText = document.getElementById('button-text') as HTMLSpanElement;
const formMessage = document.getElementById('form-message') as HTMLDivElement;
if (form) {
form.addEventListener('submit', async (e) => {
e.preventDefault();
// Get form data
const formData = new FormData(form);
const name = formData.get('name') as string;
const email = formData.get('email') as string;
const message = formData.get('message') as string;
// Disable button and show loading state
const submitButton = form.querySelector('button[type="submit"]') as HTMLButtonElement;
submitButton.disabled = true;
buttonText.textContent = 'Sending...';
formMessage.className = 'hidden';
try {
// Send to API
const response = await fetch('https://notify.api.standard.nz/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name,
email,
message,
destination: 'michael'
}),
});
if (response.ok) {
// Success
formMessage.textContent = 'Message sent successfully! I\'ll get back to you soon.';
formMessage.className = 'text-sm px-4 py-2.5 rounded-md bg-green-50 dark:bg-green-900/20 text-green-700 dark:text-green-300 border border-green-200 dark:border-green-800/30';
form.style.display = 'none';
} else {
// Error response
throw new Error('Failed to send message');
}
} catch (error) {
// Network or other error
formMessage.textContent = 'Failed to send message. Please try again or email me directly.';
formMessage.className = 'text-sm px-4 py-2.5 rounded-md bg-red-50 dark:bg-red-900/20 text-red-700 dark:text-red-300 border border-red-200 dark:border-red-800/30';
} finally {
// Re-enable button
submitButton.disabled = false;
buttonText.textContent = 'Send Message';
}
});
}
});
</script>
-6
View File
@@ -18,12 +18,6 @@ const isHomepage = Astro.url.pathname === '/';
</Link> </Link>
)} )}
<nav class="flex gap-1"> <nav class="flex gap-1">
<Link href="/blog">
blog
</Link>
<span>
{`/`}
</span>
<Link href="/work"> <Link href="/work">
work work
</Link> </Link>
+1 -1
View File
@@ -2,7 +2,7 @@ import type { Site, Metadata, Socials } from "@types";
export const SITE: Site = { export const SITE: Site = {
NAME: "Michael Rausch", NAME: "Michael Rausch",
EMAIL: "michael@rausch.nz", EMAIL: "m@michaelraus.ch",
NUM_POSTS_ON_HOMEPAGE: 3, NUM_POSTS_ON_HOMEPAGE: 3,
NUM_WORKS_ON_HOMEPAGE: 2, NUM_WORKS_ON_HOMEPAGE: 2,
NUM_PROJECTS_ON_HOMEPAGE: 3, NUM_PROJECTS_ON_HOMEPAGE: 3,
+30 -28
View File
@@ -5,6 +5,7 @@ import PageLayout from "@layouts/PageLayout.astro";
import ArrowCard from "@components/ArrowCard.astro"; import ArrowCard from "@components/ArrowCard.astro";
import WorkCard from "@components/WorkCard.astro"; import WorkCard from "@components/WorkCard.astro";
import Link from "@components/Link.astro"; import Link from "@components/Link.astro";
import ContactForm from "@components/ContactForm.astro";
import { dateRange } from "@lib/utils"; import { dateRange } from "@lib/utils";
import { SITE, HOME, SOCIALS } from "@consts"; import { SITE, HOME, SOCIALS } from "@consts";
import EmojiScroller from "@components/EmojiScroller.astro"; import EmojiScroller from "@components/EmojiScroller.astro";
@@ -35,13 +36,13 @@ const work = await Promise.all(
<PageLayout title={HOME.TITLE} description={HOME.DESCRIPTION}> <PageLayout title={HOME.TITLE} description={HOME.DESCRIPTION}>
<Container> <Container>
<h1 class="animate font-bold text-black dark:text-white text-4xl md:text-5xl font-serif leading-tight"> <h1 class="animate font-bold text-black dark:text-white text-4xl md:text-5xl font-serif leading-tight mb-5">
Hi, I'm Michael <EmojiScroller/> Hi, I'm Michael <EmojiScroller/>
</h1> </h1>
<div class="space-y-16"> <div class="space-y-16">
<section> <section>
<article class="space-y-4"> <article class="space-y-4">
<p class="animate"> <p class="animate text-lg">
I'm a software engineer at the University of Canterbury, working on <Link href="https://uconline.ac.nz" external class="text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300">Tuihono UC | UC Online</Link>. I'm a software engineer at the University of Canterbury, working on <Link href="https://uconline.ac.nz" external class="text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300">Tuihono UC | UC Online</Link>.
I specialize in full-stack development. While my primary expertise is in software engineering, I also have a strong interest in cloud infrastructure and DevOps. Based in Christchurch, New Zealand I specialize in full-stack development. While my primary expertise is in software engineering, I also have a strong interest in cloud infrastructure and DevOps. Based in Christchurch, New Zealand
@@ -108,32 +109,33 @@ const work = await Promise.all(
</ul> </ul>
</section> </section>
<section class="animate space-y-4"> <section class="animate space-y-4">
<h4 class="font-semibold text-black dark:text-white text-xl"> <h4 class="font-semibold text-black dark:text-white text-xl">
Let's Connect Let's Connect
</h4> </h4>
<article> <article>
<p> <p>
If you want to get in touch with me about something or just to say hi, If you want to get in touch with me about something or just to say hi,
reach out on social media or send me an email. reach out on social media or send me an email.
</p> </p>
</article> </article>
<ul class="flex flex-wrap gap-2"> <ContactForm />
{SOCIALS.map(SOCIAL => ( <ul class="flex flex-wrap gap-2 pt-5">
<li class="flex gap-x-2 text-nowrap"> {SOCIALS.map(SOCIAL => (
<Link href={SOCIAL.HREF} external aria-label={`${SITE.NAME} on ${SOCIAL.NAME}`}> <li class="flex gap-x-2 text-nowrap">
{SOCIAL.NAME} <Link href={SOCIAL.HREF} external aria-label={`${SITE.NAME} on ${SOCIAL.NAME}`}>
</Link> {SOCIAL.NAME}
{"/"} </Link>
</li> {"/"}
))} </li>
<li class="line-clamp-1"> ))}
<Link href={`mailto:${SITE.EMAIL}`} aria-label={`Email ${SITE.NAME}`} confetti> <li class="line-clamp-1">
{SITE.EMAIL} <Link href={`mailto:${SITE.EMAIL}`} aria-label={`Email ${SITE.NAME}`} confetti>
</Link> {SITE.EMAIL}
</li> </Link>
</ul> </li>
</section> </ul>
</section>
</div> </div>
</Container> </Container>
</PageLayout> </PageLayout>