Merge version_2 into main #1
52
src/app/api/send-reservation/route.ts
Normal file
52
src/app/api/send-reservation/route.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import nodemailer from "nodemailer";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
const transporter = nodemailer.createTransport({
|
||||
service: "gmail", auth: {
|
||||
user: process.env.GMAIL_USER,
|
||||
pass: process.env.GMAIL_PASSWORD,
|
||||
},
|
||||
});
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const data = await request.json();
|
||||
|
||||
const { name, email, phone, date, time, guests, message } = data;
|
||||
|
||||
if (!name || !email || !phone || !date || !time || !guests) {
|
||||
return NextResponse.json(
|
||||
{ error: "Missing required fields" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const mailOptions = {
|
||||
from: process.env.GMAIL_USER,
|
||||
to: "prezantime1@gmail.com", subject: `New Reservation Request from ${name}`,
|
||||
html: `
|
||||
<h2>Taverna Kala Reservation Request</h2>
|
||||
<p><strong>Name:</strong> ${name}</p>
|
||||
<p><strong>Email:</strong> ${email}</p>
|
||||
<p><strong>Phone:</strong> ${phone}</p>
|
||||
<p><strong>Date:</strong> ${date}</p>
|
||||
<p><strong>Time:</strong> ${time}</p>
|
||||
<p><strong>Number of Guests:</strong> ${guests}</p>
|
||||
<p><strong>Special Requests:</strong> ${message || "None"}</p>
|
||||
`,
|
||||
};
|
||||
|
||||
await transporter.sendMail(mailOptions);
|
||||
|
||||
return NextResponse.json(
|
||||
{ message: "Reservation request sent successfully" },
|
||||
{ status: 200 }
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error sending reservation:", error);
|
||||
return NextResponse.json(
|
||||
{ error: "Failed to send reservation" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
194
src/app/page.tsx
194
src/app/page.tsx
@@ -10,8 +10,42 @@ import TestimonialCardTen from "@/components/sections/testimonial/TestimonialCar
|
||||
import ContactCTA from "@/components/sections/contact/ContactCTA";
|
||||
import FooterBase from "@/components/sections/footer/FooterBase";
|
||||
import { BookOpen, Flame, Heart, Award, Star, Calendar, Castle } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import nodemailer from "nodemailer";
|
||||
|
||||
export default function LandingPage() {
|
||||
const [reservationData, setReservationData] = useState({
|
||||
name: "", email: "", phone: "", date: "", time: "", guests: "", message: ""
|
||||
});
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [submitMessage, setSubmitMessage] = useState("");
|
||||
|
||||
const handleReservationSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setSubmitting(true);
|
||||
setSubmitMessage("");
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/send-reservation", {
|
||||
method: "POST", headers: {
|
||||
"Content-Type": "application/json"},
|
||||
body: JSON.stringify(reservationData),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
setSubmitMessage("Reservation request sent successfully! We'll confirm shortly.");
|
||||
setReservationData({
|
||||
name: "", email: "", phone: "", date: "", time: "", guests: "", message: ""
|
||||
});
|
||||
} else {
|
||||
setSubmitMessage("Failed to send reservation. Please try again.");
|
||||
}
|
||||
} catch (error) {
|
||||
setSubmitMessage("An error occurred. Please contact us directly.");
|
||||
}
|
||||
setSubmitting(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="hover-bubble"
|
||||
@@ -192,21 +226,151 @@ export default function LandingPage() {
|
||||
</div>
|
||||
|
||||
<div id="contact" data-section="contact">
|
||||
<ContactCTA
|
||||
tag="Ready to Join Us?"
|
||||
tagIcon={Calendar}
|
||||
tagAnimation="slide-up"
|
||||
title="Reserve Your Table at Taverna Kala"
|
||||
description="Secure your spot in our intimate dining room and experience authentic Albanian heritage. Whether you're planning a quiet dinner for two, a family celebration, or a group event, we're ready to welcome you with warm hospitality and exceptional cuisine."
|
||||
buttons={[
|
||||
{ text: "Book a Reservation", href: "mailto:reservations@tavernakala.al?subject=Taverna%20Kala%20Reservation%20Request" },
|
||||
{ text: "Call Us: +355 697 123 456", href: "tel:+355697123456" }
|
||||
]}
|
||||
buttonAnimation="slide-up"
|
||||
background={{ variant: "radial-gradient" }}
|
||||
useInvertedBackground={false}
|
||||
ariaLabel="Reservation contact section"
|
||||
/>
|
||||
<div className="px-6 md:px-12 py-16 md:py-24 space-y-12">
|
||||
<div className="max-w-2xl mx-auto text-center">
|
||||
<h2 className="text-4xl md:text-5xl font-bold mb-6">Reserve Your Table</h2>
|
||||
<p className="text-lg text-foreground/80 mb-10">
|
||||
Book your dining experience at Taverna Kala. Fill out the form below and we'll confirm your reservation shortly.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form
|
||||
onSubmit={handleReservationSubmit}
|
||||
className="max-w-2xl mx-auto bg-card p-8 rounded-lg shadow-lg space-y-6"
|
||||
>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Name *</label>
|
||||
<input
|
||||
type="text"
|
||||
required
|
||||
value={reservationData.name}
|
||||
onChange={(e) =>
|
||||
setReservationData({ ...reservationData, name: e.target.value })
|
||||
}
|
||||
className="w-full px-4 py-2 border border-foreground/20 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-cta"
|
||||
placeholder="Your name"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Email *</label>
|
||||
<input
|
||||
type="email"
|
||||
required
|
||||
value={reservationData.email}
|
||||
onChange={(e) =>
|
||||
setReservationData({ ...reservationData, email: e.target.value })
|
||||
}
|
||||
className="w-full px-4 py-2 border border-foreground/20 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-cta"
|
||||
placeholder="your@email.com"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Phone *</label>
|
||||
<input
|
||||
type="tel"
|
||||
required
|
||||
value={reservationData.phone}
|
||||
onChange={(e) =>
|
||||
setReservationData({ ...reservationData, phone: e.target.value })
|
||||
}
|
||||
className="w-full px-4 py-2 border border-foreground/20 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-cta"
|
||||
placeholder="+355 697 123 456"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Number of Guests *</label>
|
||||
<select
|
||||
required
|
||||
value={reservationData.guests}
|
||||
onChange={(e) =>
|
||||
setReservationData({ ...reservationData, guests: e.target.value })
|
||||
}
|
||||
className="w-full px-4 py-2 border border-foreground/20 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-cta"
|
||||
>
|
||||
<option value="">Select number of guests</option>
|
||||
{[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((num) => (
|
||||
<option key={num} value={num.toString()}>
|
||||
{num} {num === 1 ? "guest" : "guests"}
|
||||
</option>
|
||||
))}
|
||||
<option value="10+">10+ guests (group event)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Preferred Date *</label>
|
||||
<input
|
||||
type="date"
|
||||
required
|
||||
value={reservationData.date}
|
||||
onChange={(e) =>
|
||||
setReservationData({ ...reservationData, date: e.target.value })
|
||||
}
|
||||
className="w-full px-4 py-2 border border-foreground/20 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-cta"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Preferred Time *</label>
|
||||
<input
|
||||
type="time"
|
||||
required
|
||||
value={reservationData.time}
|
||||
onChange={(e) =>
|
||||
setReservationData({ ...reservationData, time: e.target.value })
|
||||
}
|
||||
className="w-full px-4 py-2 border border-foreground/20 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-cta"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Special Requests</label>
|
||||
<textarea
|
||||
value={reservationData.message}
|
||||
onChange={(e) =>
|
||||
setReservationData({ ...reservationData, message: e.target.value })
|
||||
}
|
||||
className="w-full px-4 py-2 border border-foreground/20 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-cta resize-none"
|
||||
placeholder="Any dietary restrictions, special occasions, or other requests?"
|
||||
rows={4}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={submitting}
|
||||
className="w-full bg-primary-cta text-primary-cta-text font-semibold py-3 rounded-lg hover:opacity-90 transition-opacity disabled:opacity-50"
|
||||
>
|
||||
{submitting ? "Sending..." : "Submit Reservation Request"}
|
||||
</button>
|
||||
|
||||
{submitMessage && (
|
||||
<div
|
||||
className={`p-4 rounded-lg text-center ${
|
||||
submitMessage.includes("successfully")
|
||||
? "bg-green-100 text-green-800"
|
||||
: "bg-red-100 text-red-800"
|
||||
}`}
|
||||
>
|
||||
{submitMessage}
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
|
||||
<div className="max-w-2xl mx-auto text-center text-foreground/70 text-sm">
|
||||
<p>
|
||||
Or contact us directly:<br />
|
||||
<strong>Email:</strong> reservations@tavernakala.al<br />
|
||||
<strong>Phone:</strong> +355 697 123 456
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="footer" data-section="footer">
|
||||
|
||||
Reference in New Issue
Block a user