feat: wire contact form to Resend email notifications
- Add Server Action (actions.ts) sending to info@ and anastasia@axilaccountants.co.uk - Add ContactForm client component with useActionState, loading and success states - Replace static HTML form (action="#") with live ContactForm component - Add @eslint/eslintrc as direct devDependency (fix ESLint resolution on Node.js 25) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
932b7143fe
commit
338b47d4c1
5 changed files with 302 additions and 117 deletions
|
|
@ -24,9 +24,11 @@
|
|||
"payload": "^3.77.0",
|
||||
"react": "19.2.3",
|
||||
"react-dom": "19.2.3",
|
||||
"resend": "^6.9.2",
|
||||
"sharp": "^0.34.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.3.3",
|
||||
"@swc-node/register": "^1.11.1",
|
||||
"@swc/core": "^1.15.11",
|
||||
"@tailwindcss/postcss": "^4",
|
||||
|
|
|
|||
51
pnpm-lock.yaml
generated
51
pnpm-lock.yaml
generated
|
|
@ -47,10 +47,16 @@ importers:
|
|||
react-dom:
|
||||
specifier: 19.2.3
|
||||
version: 19.2.3(react@19.2.3)
|
||||
resend:
|
||||
specifier: ^6.9.2
|
||||
version: 6.9.2
|
||||
sharp:
|
||||
specifier: ^0.34.5
|
||||
version: 0.34.5
|
||||
devDependencies:
|
||||
'@eslint/eslintrc':
|
||||
specifier: ^3.3.3
|
||||
version: 3.3.3
|
||||
'@swc-node/register':
|
||||
specifier: ^1.11.1
|
||||
version: 1.11.1(@swc/core@1.15.11)(@swc/types@0.1.25)(typescript@5.9.3)
|
||||
|
|
@ -1298,6 +1304,9 @@ packages:
|
|||
'@rushstack/eslint-patch@1.16.1':
|
||||
resolution: {integrity: sha512-TvZbIpeKqGQQ7X0zSCvPH9riMSFQFSggnfBjFZ1mEoILW+UuXCKwOoPcgjMwiUtRqFZ8jWhPJc4um14vC6I4ag==}
|
||||
|
||||
'@stablelib/base64@1.0.1':
|
||||
resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==}
|
||||
|
||||
'@swc-node/core@1.14.1':
|
||||
resolution: {integrity: sha512-jrt5GUaZUU6cmMS+WTJEvGvaB6j1YNKPHPzC2PUi2BjaFbtxURHj6641Az6xN7b665hNniAIdvjxWcRml5yCnw==}
|
||||
engines: {node: '>= 10'}
|
||||
|
|
@ -2384,6 +2393,9 @@ packages:
|
|||
fast-safe-stringify@2.1.1:
|
||||
resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==}
|
||||
|
||||
fast-sha256@1.3.0:
|
||||
resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==}
|
||||
|
||||
fast-uri@3.1.0:
|
||||
resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==}
|
||||
|
||||
|
|
@ -3335,6 +3347,9 @@ packages:
|
|||
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
postal-mime@2.7.3:
|
||||
resolution: {integrity: sha512-MjhXadAJaWgYzevi46+3kLak8y6gbg0ku14O1gO/LNOuay8dO+1PtcSGvAdgDR0DoIsSaiIA8y/Ddw6MnrO0Tw==}
|
||||
|
||||
postcss@8.4.31:
|
||||
resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
|
@ -3543,6 +3558,15 @@ packages:
|
|||
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
resend@6.9.2:
|
||||
resolution: {integrity: sha512-uIM6CQ08tS+hTCRuKBFbOBvHIGaEhqZe8s4FOgqsVXSbQLAhmNWpmUhG3UAtRnmcwTWFUqnHa/+Vux8YGPyDBA==}
|
||||
engines: {node: '>=20'}
|
||||
peerDependencies:
|
||||
'@react-email/render': '*'
|
||||
peerDependenciesMeta:
|
||||
'@react-email/render':
|
||||
optional: true
|
||||
|
||||
resolve-from@4.0.0:
|
||||
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
||||
engines: {node: '>=4'}
|
||||
|
|
@ -3701,6 +3725,9 @@ packages:
|
|||
stable-hash@0.0.5:
|
||||
resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==}
|
||||
|
||||
standardwebhooks@1.0.0:
|
||||
resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==}
|
||||
|
||||
state-local@1.0.7:
|
||||
resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==}
|
||||
|
||||
|
|
@ -3794,6 +3821,9 @@ packages:
|
|||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
svix@1.84.1:
|
||||
resolution: {integrity: sha512-K8DPPSZaW/XqXiz1kEyzSHYgmGLnhB43nQCMeKjWGCUpLIpAMMM8kx3rVVOSm6Bo6EHyK1RQLPT4R06skM/MlQ==}
|
||||
|
||||
tabbable@6.4.0:
|
||||
resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==}
|
||||
|
||||
|
|
@ -5166,6 +5196,8 @@ snapshots:
|
|||
|
||||
'@rushstack/eslint-patch@1.16.1': {}
|
||||
|
||||
'@stablelib/base64@1.0.1': {}
|
||||
|
||||
'@swc-node/core@1.14.1(@swc/core@1.15.11)(@swc/types@0.1.25)':
|
||||
dependencies:
|
||||
'@swc/core': 1.15.11
|
||||
|
|
@ -6342,6 +6374,8 @@ snapshots:
|
|||
|
||||
fast-safe-stringify@2.1.1: {}
|
||||
|
||||
fast-sha256@1.3.0: {}
|
||||
|
||||
fast-uri@3.1.0: {}
|
||||
|
||||
fastq@1.20.1:
|
||||
|
|
@ -7448,6 +7482,8 @@ snapshots:
|
|||
|
||||
possible-typed-array-names@1.1.0: {}
|
||||
|
||||
postal-mime@2.7.3: {}
|
||||
|
||||
postcss@8.4.31:
|
||||
dependencies:
|
||||
nanoid: 3.3.11
|
||||
|
|
@ -7605,6 +7641,11 @@ snapshots:
|
|||
|
||||
require-from-string@2.0.2: {}
|
||||
|
||||
resend@6.9.2:
|
||||
dependencies:
|
||||
postal-mime: 2.7.3
|
||||
svix: 1.84.1
|
||||
|
||||
resolve-from@4.0.0: {}
|
||||
|
||||
resolve-pkg-maps@1.0.0: {}
|
||||
|
|
@ -7800,6 +7841,11 @@ snapshots:
|
|||
|
||||
stable-hash@0.0.5: {}
|
||||
|
||||
standardwebhooks@1.0.0:
|
||||
dependencies:
|
||||
'@stablelib/base64': 1.0.1
|
||||
fast-sha256: 1.3.0
|
||||
|
||||
state-local@1.0.7: {}
|
||||
|
||||
stop-iteration-iterator@1.1.0:
|
||||
|
|
@ -7905,6 +7951,11 @@ snapshots:
|
|||
|
||||
supports-preserve-symlinks-flag@1.0.0: {}
|
||||
|
||||
svix@1.84.1:
|
||||
dependencies:
|
||||
standardwebhooks: 1.0.0
|
||||
uuid: 10.0.0
|
||||
|
||||
tabbable@6.4.0: {}
|
||||
|
||||
tailwindcss@4.2.0: {}
|
||||
|
|
|
|||
151
src/app/contact/ContactForm.tsx
Normal file
151
src/app/contact/ContactForm.tsx
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
'use client';
|
||||
|
||||
import { useActionState } from 'react';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { submitContactForm } from './actions';
|
||||
|
||||
export function ContactForm() {
|
||||
const [state, action, isPending] = useActionState(submitContactForm, null);
|
||||
|
||||
if (state?.success) {
|
||||
return (
|
||||
<div className="rounded-card flex flex-col items-center justify-center gap-4 border border-black/8 bg-white p-10 text-center">
|
||||
<div className="bg-emerald-mist flex size-14 items-center justify-center rounded-full">
|
||||
<svg
|
||||
width="28"
|
||||
height="28"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="text-emerald"
|
||||
>
|
||||
<path d="M20 6 9 17l-5-5" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-display text-charcoal mb-1 text-xl font-bold">Message sent!</h3>
|
||||
<p className="text-muted text-sm">
|
||||
We'll get back to you within 2 hours on business days.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<form id="contact-form" className="space-y-5" action={action}>
|
||||
<div className="grid grid-cols-1 gap-5 sm:grid-cols-2">
|
||||
<div>
|
||||
<label htmlFor="first-name" className="text-charcoal mb-1.5 block text-sm font-medium">
|
||||
First name
|
||||
</label>
|
||||
<input
|
||||
id="first-name"
|
||||
name="firstName"
|
||||
type="text"
|
||||
required
|
||||
placeholder="James"
|
||||
className="rounded-card bg-bg text-charcoal placeholder:text-muted focus:border-emerald focus:ring-emerald/20 w-full border border-black/12 px-4 py-3 text-sm transition-colors focus:ring-2 focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="last-name" className="text-charcoal mb-1.5 block text-sm font-medium">
|
||||
Last name
|
||||
</label>
|
||||
<input
|
||||
id="last-name"
|
||||
name="lastName"
|
||||
type="text"
|
||||
required
|
||||
placeholder="Wilson"
|
||||
className="rounded-card bg-bg text-charcoal placeholder:text-muted focus:border-emerald focus:ring-emerald/20 w-full border border-black/12 px-4 py-3 text-sm transition-colors focus:ring-2 focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="email" className="text-charcoal mb-1.5 block text-sm font-medium">
|
||||
Email address
|
||||
</label>
|
||||
<input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
required
|
||||
placeholder="james@yourbusiness.co.uk"
|
||||
className="rounded-card bg-bg text-charcoal placeholder:text-muted focus:border-emerald focus:ring-emerald/20 w-full border border-black/12 px-4 py-3 text-sm transition-colors focus:ring-2 focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="phone" className="text-charcoal mb-1.5 block text-sm font-medium">
|
||||
Phone number <span className="text-muted">(optional)</span>
|
||||
</label>
|
||||
<input
|
||||
id="phone"
|
||||
name="phone"
|
||||
type="tel"
|
||||
placeholder="07700 900000"
|
||||
className="rounded-card bg-bg text-charcoal placeholder:text-muted focus:border-emerald focus:ring-emerald/20 w-full border border-black/12 px-4 py-3 text-sm transition-colors focus:ring-2 focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="service" className="text-charcoal mb-1.5 block text-sm font-medium">
|
||||
I'm interested in
|
||||
</label>
|
||||
<select
|
||||
id="service"
|
||||
name="service"
|
||||
className="rounded-card bg-bg text-charcoal focus:border-emerald focus:ring-emerald/20 w-full border border-black/12 px-4 py-3 text-sm transition-colors focus:ring-2 focus:outline-none"
|
||||
>
|
||||
<option value="">Select a service...</option>
|
||||
<option value="bookkeeping">Bookkeeping</option>
|
||||
<option value="tax-returns">Tax Returns</option>
|
||||
<option value="payroll">Payroll</option>
|
||||
<option value="vat-returns">VAT Returns</option>
|
||||
<option value="all">Full accounting package</option>
|
||||
<option value="courses">Courses</option>
|
||||
<option value="other">Other / Not sure</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="message" className="text-charcoal mb-1.5 block text-sm font-medium">
|
||||
Message
|
||||
</label>
|
||||
<textarea
|
||||
id="message"
|
||||
name="message"
|
||||
rows={4}
|
||||
required
|
||||
placeholder="Tell us a bit about your business and what you're looking for..."
|
||||
className="rounded-card bg-bg text-charcoal placeholder:text-muted focus:border-emerald focus:ring-emerald/20 w-full resize-none border border-black/12 px-4 py-3 text-sm transition-colors focus:ring-2 focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{state?.error && (
|
||||
<p className="rounded-card border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">
|
||||
{state.error}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
size="lg"
|
||||
className="w-full justify-center"
|
||||
trailingArrow
|
||||
disabled={isPending}
|
||||
>
|
||||
{isPending ? 'Sending...' : 'Send message'}
|
||||
</Button>
|
||||
|
||||
<p className="text-muted text-center text-xs">
|
||||
We typically respond within 2 hours on business days. No spam, ever.
|
||||
</p>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
96
src/app/contact/actions.ts
Normal file
96
src/app/contact/actions.ts
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
'use server';
|
||||
|
||||
import { Resend } from 'resend';
|
||||
|
||||
const resend = new Resend(process.env.RESEND_API_KEY);
|
||||
|
||||
const RECIPIENTS = ['info@axilaccountants.co.uk', 'anastasia@axilaccountants.co.uk'];
|
||||
|
||||
const SERVICE_LABELS: Record<string, string> = {
|
||||
bookkeeping: 'Bookkeeping',
|
||||
'tax-returns': 'Tax Returns',
|
||||
payroll: 'Payroll',
|
||||
'vat-returns': 'VAT Returns',
|
||||
all: 'Full accounting package',
|
||||
courses: 'Courses',
|
||||
other: 'Other / Not sure',
|
||||
};
|
||||
|
||||
export async function submitContactForm(
|
||||
_prev: { success: boolean; error?: string } | null,
|
||||
formData: FormData,
|
||||
): Promise<{ success: boolean; error?: string }> {
|
||||
const firstName = (formData.get('firstName') as string)?.trim();
|
||||
const lastName = (formData.get('lastName') as string)?.trim();
|
||||
const email = (formData.get('email') as string)?.trim();
|
||||
const phone = (formData.get('phone') as string)?.trim();
|
||||
const service = (formData.get('service') as string)?.trim();
|
||||
const message = (formData.get('message') as string)?.trim();
|
||||
|
||||
if (!firstName || !lastName || !email || !message) {
|
||||
return { success: false, error: 'Please fill in all required fields.' };
|
||||
}
|
||||
|
||||
const serviceLabel = service ? (SERVICE_LABELS[service] ?? service) : 'Not specified';
|
||||
|
||||
const html = `
|
||||
<div style="font-family: sans-serif; max-width: 600px; margin: 0 auto; color: #162520;">
|
||||
<div style="background: #3CC68A; padding: 24px 32px; border-radius: 8px 8px 0 0;">
|
||||
<h1 style="color: white; margin: 0; font-size: 20px;">New enquiry — Axil Accountants</h1>
|
||||
</div>
|
||||
<div style="background: #F5FEFA; padding: 32px; border: 1px solid #E8F8F1; border-top: none; border-radius: 0 0 8px 8px;">
|
||||
<table style="width: 100%; border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 10px 0; border-bottom: 1px solid #E8F8F1; width: 140px; color: #6B7280; font-size: 13px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em;">Name</td>
|
||||
<td style="padding: 10px 0; border-bottom: 1px solid #E8F8F1; font-size: 15px;">${firstName} ${lastName}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 10px 0; border-bottom: 1px solid #E8F8F1; color: #6B7280; font-size: 13px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em;">Email</td>
|
||||
<td style="padding: 10px 0; border-bottom: 1px solid #E8F8F1; font-size: 15px;"><a href="mailto:${email}" style="color: #3CC68A;">${email}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 10px 0; border-bottom: 1px solid #E8F8F1; color: #6B7280; font-size: 13px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em;">Phone</td>
|
||||
<td style="padding: 10px 0; border-bottom: 1px solid #E8F8F1; font-size: 15px;">${phone || '—'}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 10px 0; border-bottom: 1px solid #E8F8F1; color: #6B7280; font-size: 13px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em;">Service</td>
|
||||
<td style="padding: 10px 0; border-bottom: 1px solid #E8F8F1; font-size: 15px;">${serviceLabel}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 10px 0; vertical-align: top; color: #6B7280; font-size: 13px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em;">Message</td>
|
||||
<td style="padding: 10px 0; font-size: 15px; line-height: 1.6; white-space: pre-wrap;">${message}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="margin-top: 24px; padding-top: 16px; border-top: 1px solid #E8F8F1;">
|
||||
<a href="mailto:${email}?subject=Re: Your enquiry to Axil Accountants" style="display: inline-block; background: #3CC68A; color: white; padding: 12px 24px; border-radius: 6px; text-decoration: none; font-size: 14px; font-weight: 600;">Reply to ${firstName}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
try {
|
||||
const { error } = await resend.emails.send({
|
||||
from: 'Axil Website <noreply@axilaccountants.co.uk>',
|
||||
to: RECIPIENTS,
|
||||
replyTo: email,
|
||||
subject: `New enquiry from ${firstName} ${lastName} — ${serviceLabel}`,
|
||||
html,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
console.error('[contact form] Resend error:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: 'Failed to send message. Please try again or email us directly.',
|
||||
};
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
} catch (err) {
|
||||
console.error('[contact form] Unexpected error:', err);
|
||||
return {
|
||||
success: false,
|
||||
error: 'Failed to send message. Please try again or email us directly.',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import { Footer } from '@/components/layout/Footer';
|
|||
import { Button } from '@/components/ui/Button';
|
||||
import { FadeIn } from '@/components/ui/FadeIn';
|
||||
import { SpotlightCard } from '@/components/ui/SpotlightCard';
|
||||
import { ContactForm } from './ContactForm';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Contact — Axil Accountants',
|
||||
|
|
@ -128,123 +129,7 @@ export default function ContactPage() {
|
|||
<h2 className="font-display text-charcoal mb-6 text-2xl font-bold">
|
||||
Send us a message
|
||||
</h2>
|
||||
<form id="contact-form" className="space-y-5" action="#" method="POST">
|
||||
<div className="grid grid-cols-1 gap-5 sm:grid-cols-2">
|
||||
<div>
|
||||
<label
|
||||
htmlFor="first-name"
|
||||
className="text-charcoal mb-1.5 block text-sm font-medium"
|
||||
>
|
||||
First name
|
||||
</label>
|
||||
<input
|
||||
id="first-name"
|
||||
name="firstName"
|
||||
type="text"
|
||||
required
|
||||
placeholder="James"
|
||||
className="rounded-card bg-bg text-charcoal placeholder:text-muted focus:border-emerald focus:ring-emerald/20 w-full border border-black/12 px-4 py-3 text-sm transition-colors focus:ring-2 focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
htmlFor="last-name"
|
||||
className="text-charcoal mb-1.5 block text-sm font-medium"
|
||||
>
|
||||
Last name
|
||||
</label>
|
||||
<input
|
||||
id="last-name"
|
||||
name="lastName"
|
||||
type="text"
|
||||
required
|
||||
placeholder="Wilson"
|
||||
className="rounded-card bg-bg text-charcoal placeholder:text-muted focus:border-emerald focus:ring-emerald/20 w-full border border-black/12 px-4 py-3 text-sm transition-colors focus:ring-2 focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="text-charcoal mb-1.5 block text-sm font-medium"
|
||||
>
|
||||
Email address
|
||||
</label>
|
||||
<input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
required
|
||||
placeholder="james@yourbusiness.co.uk"
|
||||
className="rounded-card bg-bg text-charcoal placeholder:text-muted focus:border-emerald focus:ring-emerald/20 w-full border border-black/12 px-4 py-3 text-sm transition-colors focus:ring-2 focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label
|
||||
htmlFor="phone"
|
||||
className="text-charcoal mb-1.5 block text-sm font-medium"
|
||||
>
|
||||
Phone number <span className="text-muted">(optional)</span>
|
||||
</label>
|
||||
<input
|
||||
id="phone"
|
||||
name="phone"
|
||||
type="tel"
|
||||
placeholder="07700 900000"
|
||||
className="rounded-card bg-bg text-charcoal placeholder:text-muted focus:border-emerald focus:ring-emerald/20 w-full border border-black/12 px-4 py-3 text-sm transition-colors focus:ring-2 focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label
|
||||
htmlFor="service"
|
||||
className="text-charcoal mb-1.5 block text-sm font-medium"
|
||||
>
|
||||
I'm interested in
|
||||
</label>
|
||||
<select
|
||||
id="service"
|
||||
name="service"
|
||||
className="rounded-card bg-bg text-charcoal focus:border-emerald focus:ring-emerald/20 w-full border border-black/12 px-4 py-3 text-sm transition-colors focus:ring-2 focus:outline-none"
|
||||
>
|
||||
<option value="">Select a service...</option>
|
||||
<option value="bookkeeping">Bookkeeping</option>
|
||||
<option value="tax-returns">Tax Returns</option>
|
||||
<option value="payroll">Payroll</option>
|
||||
<option value="vat-returns">VAT Returns</option>
|
||||
<option value="all">Full accounting package</option>
|
||||
<option value="courses">Courses</option>
|
||||
<option value="other">Other / Not sure</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label
|
||||
htmlFor="message"
|
||||
className="text-charcoal mb-1.5 block text-sm font-medium"
|
||||
>
|
||||
Message
|
||||
</label>
|
||||
<textarea
|
||||
id="message"
|
||||
name="message"
|
||||
rows={4}
|
||||
required
|
||||
placeholder="Tell us a bit about your business and what you're looking for..."
|
||||
className="rounded-card bg-bg text-charcoal placeholder:text-muted focus:border-emerald focus:ring-emerald/20 w-full resize-none border border-black/12 px-4 py-3 text-sm transition-colors focus:ring-2 focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button type="submit" size="lg" className="w-full justify-center" trailingArrow>
|
||||
Send message
|
||||
</Button>
|
||||
|
||||
<p className="text-muted text-center text-xs">
|
||||
We typically respond within 2 hours on business days. No spam, ever.
|
||||
</p>
|
||||
</form>
|
||||
<ContactForm />
|
||||
</SpotlightCard>
|
||||
</FadeIn>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue