Rebrand to Sage & Cream identity, working contact form, drop Firebase

- New sage/cream palette and stacked logo lockup (The Vine over Coffeehouse
  + Bakery) driven by currentColor; logo SVGs now colorable
- Reuse the logo component for the hero and section marks
- Rewrite site copy: real founding (Morissa Bennett, 2024), real story from
  the product range, remove invented claims and AI phrasing
- Contact form now sends over SMTP via /api/contact (nodemailer) with real
  send/error states, server-side validation, and reply-to the customer;
  delivers to CONTACT_TO. Add .env.example and a test-email script
- Fix mobile: unmount the off-screen menu (killed sideways scroll), cap logo
  width, responsive heroes and type
- Remove Firebase (config, tracked build output, placeholder pages, nix
  firebase-tools) now that hosting has moved
- Delete dead code: LoyaltyCardForm, ProductModal, LoadingSpinner, card-flip
  CSS, unused Playfair font
- Serve dev/start on port 2024
This commit is contained in:
Austin
2026-07-14 17:48:00 -05:00
parent f4ce93965b
commit 5c81b65e51
170 changed files with 980 additions and 1399 deletions
+57
View File
@@ -0,0 +1,57 @@
import { NextResponse } from 'next/server';
import nodemailer from 'nodemailer';
// Server-side only, so the SMTP credentials never reach the browser.
const { SMTP_SERVER, SMTP_PORT, SMTP_USERNAME, SMTP_TOKEN, CONTACT_TO, CONTACT_FROM } = process.env;
export async function POST(request: Request) {
if (!SMTP_SERVER || !SMTP_PORT || !SMTP_USERNAME || !SMTP_TOKEN) {
console.error('contact: SMTP env vars missing');
return NextResponse.json({ error: 'Email is not configured.' }, { status: 500 });
}
let body: { name?: string; email?: string; message?: string };
try {
body = await request.json();
} catch {
return NextResponse.json({ error: 'Invalid request.' }, { status: 400 });
}
const name = body.name?.trim();
const email = body.email?.trim();
const message = body.message?.trim();
// Trust boundary: validate before handing anything to the mailer.
if (!name || !email || !message) {
return NextResponse.json({ error: 'Please fill in every field.' }, { status: 400 });
}
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
return NextResponse.json({ error: 'That email address does not look right.' }, { status: 400 });
}
if (message.length > 5000) {
return NextResponse.json({ error: 'That message is too long.' }, { status: 400 });
}
const port = Number(SMTP_PORT);
const transporter = nodemailer.createTransport({
host: SMTP_SERVER,
port,
secure: port === 465, // 465 is implicit TLS; 587 upgrades via STARTTLS
auth: { user: SMTP_USERNAME, pass: SMTP_TOKEN },
});
try {
await transporter.sendMail({
from: CONTACT_FROM || SMTP_USERNAME,
to: CONTACT_TO || '[email protected]',
replyTo: `${name} <${email}>`, // so hitting reply answers the customer
subject: `Website enquiry from ${name}`,
text: `${message}\n\nFrom: ${name} <${email}>`,
});
} catch (err) {
console.error('contact: send failed', err);
return NextResponse.json({ error: 'Could not send the message.' }, { status: 502 });
}
return NextResponse.json({ ok: true });
}