Added rough draft of contact page

This commit is contained in:
2025-01-11 01:09:00 +00:00
parent 0f51d2160b
commit 5e433248ff
7 changed files with 326 additions and 2 deletions

BIN
bun.lockb

Binary file not shown.

View File

@@ -25,7 +25,8 @@
"vite": "^5.4.11" "vite": "^5.4.11"
}, },
"dependencies": { "dependencies": {
"@tailwindcss/forms": "^0.5.10" "@tailwindcss/forms": "^0.5.10",
"lucide-svelte": "^0.471.0"
}, },
"private": true "private": true
} }

View File

@@ -0,0 +1,180 @@
<script>
import { Send, Loader2 } from "lucide-svelte";
let formData = {
name: "",
email: "",
subject: "",
message: "",
};
let errors = {};
let isSubmitting = false;
let submitted = false;
const validateForm = () => {
const newErrors = {};
if (!formData.name.trim()) newErrors.name = "Name is required";
if (!formData.email.trim()) {
newErrors.email = "Email is required";
} else if (!/\S+@\S+\.\S+/.test(formData.email)) {
newErrors.email = "Please enter a valid email";
}
if (!formData.subject.trim()) newErrors.subject = "Subject is required";
if (!formData.message.trim()) newErrors.message = "Message is required";
errors = newErrors;
return Object.keys(newErrors).length === 0;
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!validateForm()) return;
isSubmitting = true;
// Simulate API call - replace with your actual API endpoint
await new Promise((resolve) => setTimeout(resolve, 1500));
isSubmitting = false;
submitted = true;
// Reset form
formData = {
name: "",
email: "",
subject: "",
message: "",
};
};
const handleInput = (e) => {
const target = e.target;
formData[target.name] = target.value;
// Clear error when user starts typing
if (errors[target.name]) {
errors[target.name] = "";
}
};
</script>
{#if submitted}
<div class="form-container {$$props.class}">
<div class="text-center success-message">
<h2 class="text-2xl font-semibold text-green-600 mb-4">
Thank You!
</h2>
<p class="text-gray-600 mb-4">
Your message has been sent successfully.
</p>
<button
on:click={() => (submitted = false)}
class="px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700 transition-colors"
>
Send Another Message
</button>
</div>
</div>
{:else}
<div class="form-container {$$props.class}">
<h2 class="text-2xl font-semibold text-gray-800 mb-6">Contact Form</h2>
<form on:submit={handleSubmit} class="space-y-4">
<div>
<label
for="name"
class="block text-sm font-medium text-gray-700 mb-1"
>
Name
</label>
<input
type="text"
id="name"
name="name"
bind:value={formData.name}
on:input={handleInput}
class="w-full px-3 py-2 border rounded-md {errors.name
? 'error'
: 'border-gray-300'}"
/>
{#if errors.name}
<p class="mt-1 text-sm text-red-500">{errors.name}</p>
{/if}
</div>
<div>
<label
for="email"
class="block text-sm font-medium text-gray-700 mb-1"
>
Email
</label>
<input
type="email"
id="email"
name="email"
bind:value={formData.email}
on:input={handleInput}
class="w-full px-3 py-2 border rounded-md {errors.email
? 'error'
: 'border-gray-300'}"
/>
{#if errors.email}
<p class="mt-1 text-sm text-red-500">{errors.email}</p>
{/if}
</div>
<div>
<label
for="subject"
class="block text-sm font-medium text-gray-700 mb-1"
>
Subject
</label>
<input
type="text"
id="subject"
name="subject"
bind:value={formData.subject}
on:input={handleInput}
class="w-full px-3 py-2 border rounded-md {errors.subject
? 'error'
: 'border-gray-300'}"
/>
{#if errors.subject}
<p class="mt-1 text-sm text-red-500">{errors.subject}</p>
{/if}
</div>
<div>
<label
for="message"
class="block text-sm font-medium text-gray-700 mb-1"
>
Message
</label>
<textarea
id="message"
name="message"
bind:value={formData.message}
on:input={handleInput}
rows={4}
class="w-full px-3 py-2 border rounded-md {errors.message
? 'error'
: 'border-gray-300'}"
/>
{#if errors.message}
<p class="mt-1 text-sm text-red-500">{errors.message}</p>
{/if}
</div>
<button type="submit" disabled={isSubmitting} class="submit-button">
{#if isSubmitting}
<Loader2 class="animate-spin" size={20} />
<span>Sending...</span>
{:else}
<Send size={20} />
<span>Send Message</span>
{/if}
</button>
</form>
</div>
{/if}

View File

@@ -0,0 +1,64 @@
.form-container {
input,
textarea {
transition: border-color 0.2s ease, box-shadow 0.2s ease;
&:focus {
outline: none;
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
}
&.error {
border-color: #ef4444;
&:focus {
box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2);
}
}
}
.submit-button {
width: 100%;
padding: 0.5rem 1rem;
background-color: #2563eb;
color: white;
border-radius: 0.375rem;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
transition: background-color 0.2s ease;
&:hover:not(:disabled) {
background-color: #1d4ed8;
}
&:focus {
outline: none;
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
}
&:disabled {
background-color: #93c5fd;
cursor: not-allowed;
}
}
// Add subtle animation for success message
.success-message {
animation: fadeIn 0.3s ease-in-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
}

View File

@@ -1 +1,32 @@
Contact page placeholder <script>
import "./ContactPage.scss";
import NavigationHeader from "$lib/components/NavigationHeader.svelte";
import NavigationFooter from "$lib/components/NavigationFooter.svelte";
import ContactForm from "$lib/components/ContactForm.svelte";
</script>
<div class="contact-page">
<NavigationHeader />
<div class="content">
<div class="contact-form-column">
<h1>Contact Us</h1>
<h2>We look forward to hearing from you!</h2>
<p>
Interested in contributing, collaborating, or otherwise getting
in touch with the AM-D-Model team? We are always looking for
input on our project from others in the wider L-PBF research
community. Please use the form below to email our corresponding
researcher and they will reply as soon as they are able.
</p>
<ContactForm class="contact-form" />
</div>
<div class="contact-image-column">
<img
class="contact-image"
src="contact.webp"
alt="A photograph of our lovely colleague who is eager to hear from you"
/>
</div>
</div>
<NavigationFooter />
</div>

View File

@@ -0,0 +1,48 @@
.contact-page,
.contact-page * {
box-sizing: border-box;
}
.contact-page {
background: var(--cpalettecomplimentary);
width: 100vw;
min-width: 250px;
position: absolute;
display: flex;
flex-direction: column;
}
.content {
display: flex;
flex-direction: row;
width: 100%;
padding: 5% 5% 5% 5%;
}
.contact-form-column {
display: flex;
flex-direction: column;
width: 50%;
}
.contact-form {
position: relative;
padding-top: 32px;
}
.contact-image-column {
position: relative;
width: 50%;
top: 0;
bottom: 0;
padding: 32px 0 32px 48px;
}
.contact-image {
position: sticky;
border-radius: var(--contact-corner-round);
width: 100%;
top: 64px;
max-width: var(--contact-img-size);
max-height: var(--contact-img-size);
}

BIN
static/contact.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB