Merge version_2 into main
Merge version_2 into main
This commit was merged in pull request #6.
This commit is contained in:
292
src/app/appointment/page.tsx
Normal file
292
src/app/appointment/page.tsx
Normal file
@@ -0,0 +1,292 @@
|
||||
"use client";
|
||||
|
||||
import NavbarStyleApple from "@/components/navbar/NavbarStyleApple/NavbarStyleApple";
|
||||
import HeroLogoBillboardSplit from "@/components/sections/hero/HeroLogoBillboardSplit";
|
||||
import ContactForm from "@/components/form/ContactForm";
|
||||
import FooterLogoEmphasis from "@/components/sections/footer/FooterLogoEmphasis";
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import { Calendar, User, Sparkles } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function AppointmentPage() {
|
||||
const [selectedService, setSelectedService] = useState<string>("");
|
||||
const [selectedDate, setSelectedDate] = useState<string>("");
|
||||
const [formData, setFormData] = useState({
|
||||
fullName: "", email: "", phone: "", service: "", date: "", time: ""});
|
||||
|
||||
const services = [
|
||||
{ id: "makeup", name: "Professional Makeup Consultation", duration: "60 min", price: "$75" },
|
||||
{ id: "skincare", name: "Skincare Analysis & Treatment", duration: "90 min", price: "$120" },
|
||||
{ id: "makeover", name: "Complete Beauty Makeover", duration: "120 min", price: "$180" },
|
||||
{ id: "bridal", name: "Bridal Beauty Package", duration: "180 min", price: "$250" },
|
||||
];
|
||||
|
||||
const timeSlots = [
|
||||
"9:00 AM", "10:00 AM", "11:00 AM", "1:00 PM", "2:00 PM", "3:00 PM", "4:00 PM", "5:00 PM"];
|
||||
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[name]: value,
|
||||
}));
|
||||
if (name === "service") setSelectedService(value);
|
||||
if (name === "date") setSelectedDate(value);
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
console.log("Appointment booked:", formData);
|
||||
alert(`Appointment booked for ${formData.fullName} on ${formData.date} at ${formData.time}`);
|
||||
setFormData({ fullName: "", email: "", phone: "", service: "", date: "", time: "" });
|
||||
setSelectedService("");
|
||||
setSelectedDate("");
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="expand-hover"
|
||||
defaultTextAnimation="background-highlight"
|
||||
borderRadius="soft"
|
||||
contentWidth="small"
|
||||
sizing="largeSizeMediumTitles"
|
||||
background="noiseDiagonalGradient"
|
||||
cardStyle="solid"
|
||||
primaryButtonStyle="diagonal-gradient"
|
||||
secondaryButtonStyle="glass"
|
||||
headingFontWeight="bold"
|
||||
>
|
||||
<div id="nav" data-section="nav">
|
||||
<NavbarStyleApple
|
||||
brandName="Radiance Beauty"
|
||||
navItems={[
|
||||
{ name: "Shop", id: "products" },
|
||||
{ name: "About", id: "about" },
|
||||
{ name: "Testimonials", id: "testimonials" },
|
||||
{ name: "Booking", id: "/appointment" },
|
||||
{ name: "Contact", id: "contact" },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div id="hero" data-section="hero">
|
||||
<HeroLogoBillboardSplit
|
||||
logoText="BOOK YOUR APPOINTMENT"
|
||||
description="Schedule a personalized beauty consultation with our expert beauty professionals. Choose your preferred service, date, and time."
|
||||
background={{ variant: "radial-gradient" }}
|
||||
buttons={[
|
||||
{ text: "Back to Shop", href: "/" },
|
||||
]}
|
||||
layoutOrder="default"
|
||||
imageSrc="http://img.b2bpic.net/free-photo/happy-woman-holding-mascara-wand_23-2148398589.jpg?_wi=1"
|
||||
imageAlt="Radiance Beauty professional makeup artist"
|
||||
frameStyle="card"
|
||||
mediaAnimation="slide-up"
|
||||
buttonAnimation="slide-up"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="py-20 px-6 md:px-10">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<div className="grid md:grid-cols-2 gap-10">
|
||||
{/* Services Section */}
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-2 flex items-center gap-2">
|
||||
<Sparkles className="w-6 h-6" />
|
||||
Our Services
|
||||
</h2>
|
||||
<p className="text-foreground/80 mb-4">Select a service to get started</p>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
{services.map((service) => (
|
||||
<div
|
||||
key={service.id}
|
||||
onClick={() =>
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
service: service.id,
|
||||
}))
|
||||
}
|
||||
className={`p-4 rounded-lg border-2 cursor-pointer transition-all ${
|
||||
selectedService === service.id
|
||||
? "border-primary-cta bg-primary-cta/10"
|
||||
: "border-background-accent hover:border-primary-cta/50"
|
||||
}`}
|
||||
>
|
||||
<h3 className="font-semibold text-sm">{service.name}</h3>
|
||||
<p className="text-xs text-foreground/60 mt-1">
|
||||
{service.duration} • {service.price}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Booking Form Section */}
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-2 flex items-center gap-2">
|
||||
<Calendar className="w-6 h-6" />
|
||||
Your Information
|
||||
</h2>
|
||||
<p className="text-foreground/80 mb-4">Complete your booking details</p>
|
||||
</div>
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
{/* Client Name */}
|
||||
<div>
|
||||
<label htmlFor="fullName" className="block text-sm font-medium mb-2 flex items-center gap-2">
|
||||
<User className="w-4 h-4" />
|
||||
Full Name *
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="fullName"
|
||||
name="fullName"
|
||||
value={formData.fullName}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
placeholder="Enter your full name"
|
||||
className="w-full px-4 py-2 border-2 border-background-accent rounded-lg bg-background focus:outline-none focus:border-primary-cta transition-colors"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Email */}
|
||||
<div>
|
||||
<label htmlFor="email" className="block text-sm font-medium mb-2">
|
||||
Email Address *
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
value={formData.email}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
placeholder="your.email@example.com"
|
||||
className="w-full px-4 py-2 border-2 border-background-accent rounded-lg bg-background focus:outline-none focus:border-primary-cta transition-colors"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Phone */}
|
||||
<div>
|
||||
<label htmlFor="phone" className="block text-sm font-medium mb-2">
|
||||
Phone Number *
|
||||
</label>
|
||||
<input
|
||||
type="tel"
|
||||
id="phone"
|
||||
name="phone"
|
||||
value={formData.phone}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
placeholder="(555) 123-4567"
|
||||
className="w-full px-4 py-2 border-2 border-background-accent rounded-lg bg-background focus:outline-none focus:border-primary-cta transition-colors"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Date Selection */}
|
||||
<div>
|
||||
<label htmlFor="date" className="block text-sm font-medium mb-2">
|
||||
Preferred Date *
|
||||
</label>
|
||||
<input
|
||||
type="date"
|
||||
id="date"
|
||||
name="date"
|
||||
value={formData.date}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
className="w-full px-4 py-2 border-2 border-background-accent rounded-lg bg-background focus:outline-none focus:border-primary-cta transition-colors"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Time Selection */}
|
||||
<div>
|
||||
<label htmlFor="time" className="block text-sm font-medium mb-2">
|
||||
Preferred Time *
|
||||
</label>
|
||||
<select
|
||||
id="time"
|
||||
name="time"
|
||||
value={formData.time}
|
||||
onChange={handleInputChange}
|
||||
required
|
||||
className="w-full px-4 py-2 border-2 border-background-accent rounded-lg bg-background focus:outline-none focus:border-primary-cta transition-colors"
|
||||
>
|
||||
<option value="">Select a time slot</option>
|
||||
{timeSlots.map((time) => (
|
||||
<option key={time} value={time}>
|
||||
{time}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Service Confirmation */}
|
||||
{selectedService && (
|
||||
<div className="p-3 bg-primary-cta/10 border-2 border-primary-cta rounded-lg">
|
||||
<p className="text-sm font-medium">
|
||||
Selected Service:{" "}
|
||||
<span className="text-primary-cta">
|
||||
{services.find((s) => s.id === selectedService)?.name}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Submit Button */}
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full bg-primary-cta text-background font-semibold py-3 rounded-lg hover:opacity-90 transition-opacity mt-6"
|
||||
>
|
||||
Confirm Appointment
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="footer" data-section="footer">
|
||||
<FooterLogoEmphasis
|
||||
logoText="Radiance Beauty"
|
||||
columns={[
|
||||
{
|
||||
items: [
|
||||
{ label: "Shop", href: "/" },
|
||||
{ label: "Book Appointment", href: "/appointment" },
|
||||
{ label: "New Arrivals", href: "/" },
|
||||
{ label: "Collections", href: "/" },
|
||||
],
|
||||
},
|
||||
{
|
||||
items: [
|
||||
{ label: "About Us", href: "/" },
|
||||
{ label: "Our Story", href: "/" },
|
||||
{ label: "Sustainability", href: "#" },
|
||||
{ label: "Careers", href: "#" },
|
||||
],
|
||||
},
|
||||
{
|
||||
items: [
|
||||
{ label: "Help Center", href: "/" },
|
||||
{ label: "Contact Us", href: "#" },
|
||||
{ label: "Shipping Info", href: "#" },
|
||||
{ label: "Track Order", href: "#" },
|
||||
],
|
||||
},
|
||||
{
|
||||
items: [
|
||||
{ label: "Privacy Policy", href: "#" },
|
||||
{ label: "Terms of Service", href: "#" },
|
||||
{ label: "Refund Policy", href: "#" },
|
||||
{ label: "Accessibility", href: "#" },
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
1418
src/app/layout.tsx
1418
src/app/layout.tsx
File diff suppressed because it is too large
Load Diff
213
src/app/page.tsx
213
src/app/page.tsx
@@ -11,9 +11,75 @@ import SocialProofOne from "@/components/sections/socialProof/SocialProofOne";
|
||||
import FaqSplitText from "@/components/sections/faq/FaqSplitText";
|
||||
import FooterLogoEmphasis from "@/components/sections/footer/FooterLogoEmphasis";
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import { Award, CheckCircle, Crown, Heart, Shield, Sparkles } from "lucide-react";
|
||||
import { Award, CheckCircle, Crown, Heart, Shield, Sparkles, Calendar, Clock, Users } from "lucide-react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
interface TimeSlot {
|
||||
time: string;
|
||||
available: boolean;
|
||||
}
|
||||
|
||||
interface DaySlots {
|
||||
date: string;
|
||||
day: string;
|
||||
slots: TimeSlot[];
|
||||
}
|
||||
|
||||
export default function LandingPage() {
|
||||
const [calendarData, setCalendarData] = useState<DaySlots[]>([]);
|
||||
const [selectedDate, setSelectedDate] = useState<string | null>(null);
|
||||
const [selectedSlot, setSelectedSlot] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// Generate availability calendar for next 7 days
|
||||
const generateCalendar = () => {
|
||||
const days: DaySlots[] = [];
|
||||
const today = new Date();
|
||||
|
||||
for (let i = 0; i < 7; i++) {
|
||||
const date = new Date(today);
|
||||
date.setDate(date.getDate() + i);
|
||||
const dateStr = date.toISOString().split('T')[0];
|
||||
const dayName = date.toLocaleDateString('en-US', { weekday: 'short' });
|
||||
|
||||
const slots: TimeSlot[] = [];
|
||||
const hours = [9, 10, 11, 13, 14, 15, 16, 17];
|
||||
|
||||
for (const hour of hours) {
|
||||
// Simulate availability - 80% slots available
|
||||
const available = Math.random() > 0.2;
|
||||
slots.push({
|
||||
time: `${hour.toString().padStart(2, '0')}:00`,
|
||||
available,
|
||||
});
|
||||
}
|
||||
|
||||
days.push({
|
||||
date: dateStr,
|
||||
day: dayName,
|
||||
slots,
|
||||
});
|
||||
}
|
||||
|
||||
setCalendarData(days);
|
||||
if (days.length > 0) {
|
||||
setSelectedDate(days[0].date);
|
||||
}
|
||||
};
|
||||
|
||||
generateCalendar();
|
||||
}, []);
|
||||
|
||||
const getAvailableCount = (date: string): number => {
|
||||
const day = calendarData.find(d => d.date === date);
|
||||
return day ? day.slots.filter(s => s.available).length : 0;
|
||||
};
|
||||
|
||||
const getSelectedDaySlots = (): TimeSlot[] => {
|
||||
const day = calendarData.find(d => d.date === selectedDate);
|
||||
return day ? day.slots : [];
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="expand-hover"
|
||||
@@ -34,7 +100,7 @@ export default function LandingPage() {
|
||||
{ name: "Shop", id: "products" },
|
||||
{ name: "About", id: "about" },
|
||||
{ name: "Testimonials", id: "testimonials" },
|
||||
{ name: "Contact", id: "contact" },
|
||||
{ name: "Book Consultation", id: "availability" },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
@@ -206,6 +272,145 @@ export default function LandingPage() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div id="availability" data-section="availability" className="py-20">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
{/* Header */}
|
||||
<div className="mb-12 text-center">
|
||||
<div className="inline-flex items-center gap-2 mb-4 px-4 py-2 rounded-full bg-opacity-10 backdrop-blur">
|
||||
<Calendar className="w-4 h-4" />
|
||||
<span className="text-sm font-medium">Consultation Booking</span>
|
||||
</div>
|
||||
<h2 className="text-4xl md:text-5xl font-bold mb-4">Schedule Your Personal Consultation</h2>
|
||||
<p className="text-lg text-opacity-70 max-w-2xl mx-auto">Book a consultation with our beauty experts to find the perfect products and routine for your skin type.</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
{/* Calendar */}
|
||||
<div className="lg:col-span-2">
|
||||
<div className="rounded-lg border border-opacity-20 p-8 backdrop-blur">
|
||||
<h3 className="text-2xl font-bold mb-6 flex items-center gap-2">
|
||||
<Calendar className="w-6 h-6" />
|
||||
Available Dates
|
||||
</h3>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-8">
|
||||
{calendarData.map((day) => (
|
||||
<button
|
||||
key={day.date}
|
||||
onClick={() => setSelectedDate(day.date)}
|
||||
className={`p-4 rounded-lg border-2 transition-all ${
|
||||
selectedDate === day.date
|
||||
? 'border-opacity-100 bg-opacity-20'
|
||||
: 'border-opacity-20 hover:border-opacity-40'
|
||||
}`}
|
||||
>
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="text-left">
|
||||
<p className="font-semibold">{day.day}</p>
|
||||
<p className="text-sm opacity-70">{new Date(day.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}</p>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="font-bold text-lg">{getAvailableCount(day.date)}</p>
|
||||
<p className="text-xs opacity-70">slots</p>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Time Slots */}
|
||||
<div>
|
||||
<h4 className="text-xl font-bold mb-4 flex items-center gap-2">
|
||||
<Clock className="w-5 h-5" />
|
||||
Available Times
|
||||
</h4>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||
{getSelectedDaySlots().map((slot, idx) => (
|
||||
<button
|
||||
key={idx}
|
||||
onClick={() => slot.available && setSelectedSlot(slot.time)}
|
||||
disabled={!slot.available}
|
||||
className={`py-3 px-4 rounded-lg font-semibold transition-all ${
|
||||
!slot.available
|
||||
? 'opacity-30 cursor-not-allowed'
|
||||
: selectedSlot === slot.time
|
||||
? 'bg-opacity-100 text-white shadow-lg transform scale-105'
|
||||
: 'hover:bg-opacity-20'
|
||||
}`}
|
||||
>
|
||||
{slot.time}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Booking Summary */}
|
||||
<div className="lg:col-span-1">
|
||||
<div className="rounded-lg border border-opacity-20 p-8 backdrop-blur sticky top-20">
|
||||
<h3 className="text-xl font-bold mb-6 flex items-center gap-2">
|
||||
<Users className="w-5 h-5" />
|
||||
Booking Summary
|
||||
</h3>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<p className="text-sm opacity-70 mb-2">Selected Date</p>
|
||||
<p className="font-bold text-lg">
|
||||
{selectedDate
|
||||
? new Date(selectedDate).toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric' })
|
||||
: 'No date selected'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="text-sm opacity-70 mb-2">Selected Time</p>
|
||||
<p className="font-bold text-lg">
|
||||
{selectedSlot ? selectedSlot : 'No time selected'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="text-sm opacity-70 mb-2">Consultation Type</p>
|
||||
<p className="font-bold">30-Minute Personal Consultation</p>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-opacity-20 pt-4 mt-6">
|
||||
<p className="text-sm opacity-70 mb-3">What to Expect:</p>
|
||||
<ul className="space-y-2 text-sm">
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle className="w-4 h-4 flex-shrink-0 mt-0.5" />
|
||||
<span>Personalized skin analysis</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle className="w-4 h-4 flex-shrink-0 mt-0.5" />
|
||||
<span>Product recommendations</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle className="w-4 h-4 flex-shrink-0 mt-0.5" />
|
||||
<span>Custom routine guidance</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<button
|
||||
disabled={!selectedDate || !selectedSlot}
|
||||
className={`w-full py-3 px-4 rounded-lg font-bold transition-all mt-6 ${
|
||||
selectedDate && selectedSlot
|
||||
? 'hover:shadow-lg hover:scale-105'
|
||||
: 'opacity-50 cursor-not-allowed'
|
||||
}`}
|
||||
>
|
||||
Confirm Booking
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="socialproof" data-section="socialproof">
|
||||
<SocialProofOne
|
||||
title="Trusted by Beauty Experts"
|
||||
@@ -276,7 +481,7 @@ export default function LandingPage() {
|
||||
{
|
||||
items: [
|
||||
{ label: "Help Center", href: "#faq" },
|
||||
{ label: "Contact Us", href: "#" },
|
||||
{ label: "Book Consultation", href: "#availability" },
|
||||
{ label: "Shipping Info", href: "#" },
|
||||
{ label: "Track Order", href: "#" },
|
||||
],
|
||||
@@ -294,4 +499,4 @@ export default function LandingPage() {
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user