Merge branch 'fix/landing-page-issues'

This commit is contained in:
iamkiddy 2025-11-22 20:24:08 +00:00
commit 1e829de544
10 changed files with 155 additions and 160 deletions

View File

@ -21,21 +21,27 @@ export default function Login() {
{/* Background Image */} {/* Background Image */}
<div className="absolute inset-0 z-0"> <div className="absolute inset-0 z-0">
<Image <Image
src="/doctors.png" src="/woman.jpg"
alt="Medical professionals" alt="Therapy and counseling session with African American clients"
fill fill
className="object-cover object-center" className="object-cover object-center"
priority priority
sizes="100vw" sizes="100vw"
/> />
{/* Overlay for better readability */} {/* Overlay for better readability */}
<div className="absolute inset-0 bg-black/20"></div> <div className="absolute inset-0 bg-black/50"></div>
</div> </div>
{/* Branding - Top Left */} {/* Branding - Top Left */}
<div className="absolute top-8 left-8 flex items-center gap-3 z-30"> <div className="absolute top-8 left-8 flex items-center gap-2 z-30">
<Heart className="w-6 h-6 text-white" fill="white" /> <Link href="/" className="flex items-center gap-2">
<span className="text-white text-xl font-semibold">Attune Heart Therapy</span> <div className="bg-gradient-to-r from-rose-500 to-pink-600 p-2 rounded-xl">
<Heart className="h-5 w-5 text-white fill-white" />
</div>
<span className={`font-bold text-lg drop-shadow-lg ${isDark ? 'text-rose-400' : 'text-rose-500'}`}>
Attune Heart Therapy
</span>
</Link>
</div> </div>

View File

@ -78,9 +78,8 @@ export default function BookNowPage() {
lastName: "", lastName: "",
email: "", email: "",
phone: "", phone: "",
appointmentType: "", preferredDays: [] as string[],
preferredDate: "", preferredTimes: [] as string[],
preferredTime: "",
message: "", message: "",
}); });
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@ -105,17 +104,41 @@ export default function BookNowPage() {
setError(null); setError(null);
try { try {
// Convert time to 24-hour format for ISO string if (formData.preferredDays.length === 0) {
const time24 = formData.preferredTime.includes("PM") setError("Please select at least one available day.");
? formData.preferredTime.replace("PM", "").trim().split(":").map((v, i) => setLoading(false);
i === 0 ? (parseInt(v) === 12 ? 12 : parseInt(v) + 12) : v return;
).join(":") }
: formData.preferredTime.replace("AM", "").trim().split(":").map((v, i) =>
i === 0 ? (parseInt(v) === 12 ? "00" : v.padStart(2, "0")) : v if (formData.preferredTimes.length === 0) {
).join(":"); setError("Please select at least one preferred time.");
setLoading(false);
return;
}
// For now, we'll use the first selected day and first selected time
// This can be adjusted based on your backend requirements
const firstDay = formData.preferredDays[0];
const firstTime = formData.preferredTimes[0];
const timeMap: { [key: string]: string } = {
morning: "09:00",
lunchtime: "12:00",
afternoon: "14:00",
};
const time24 = timeMap[firstTime] || "09:00";
// Get next occurrence of the first selected day
const today = new Date();
const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
const targetDayIndex = days.indexOf(firstDay);
let daysUntilTarget = (targetDayIndex - today.getDay() + 7) % 7;
if (daysUntilTarget === 0) daysUntilTarget = 7; // Next week if today
const targetDate = new Date(today);
targetDate.setDate(today.getDate() + daysUntilTarget);
const dateString = targetDate.toISOString().split("T")[0];
// Combine date and time into scheduled_at (ISO format) // Combine date and time into scheduled_at (ISO format)
const dateTimeString = `${formData.preferredDate}T${time24}:00Z`; const dateTimeString = `${dateString}T${time24}:00Z`;
// Prepare request payload // Prepare request payload
const payload = { const payload = {
@ -123,9 +146,10 @@ export default function BookNowPage() {
last_name: formData.lastName, last_name: formData.lastName,
email: formData.email, email: formData.email,
phone: formData.phone, phone: formData.phone,
appointment_type: formData.appointmentType,
scheduled_at: dateTimeString, scheduled_at: dateTimeString,
duration: 60, // Default to 60 minutes duration: 60, // Default to 60 minutes
preferred_days: formData.preferredDays,
preferred_times: formData.preferredTimes,
notes: formData.message || "", notes: formData.message || "",
}; };
@ -199,6 +223,24 @@ export default function BookNowPage() {
setFormData((prev) => ({ ...prev, [field]: value })); setFormData((prev) => ({ ...prev, [field]: value }));
}; };
const handleDayToggle = (day: string) => {
setFormData((prev) => {
const days = prev.preferredDays.includes(day)
? prev.preferredDays.filter((d) => d !== day)
: [...prev.preferredDays, day];
return { ...prev, preferredDays: days };
});
};
const handleTimeToggle = (time: string) => {
setFormData((prev) => {
const times = prev.preferredTimes.includes(time)
? prev.preferredTimes.filter((t) => t !== time)
: [...prev.preferredTimes, time];
return { ...prev, preferredTimes: times };
});
};
const formatDateTime = (dateString: string) => { const formatDateTime = (dateString: string) => {
const date = new Date(dateString); const date = new Date(dateString);
return date.toLocaleString("en-US", { return date.toLocaleString("en-US", {
@ -219,8 +261,8 @@ export default function BookNowPage() {
<div className={`hidden lg:block fixed top-0 left-0 h-screen w-1/2 overflow-hidden z-10 bg-gradient-to-br ${isDark ? 'from-gray-900 via-gray-800 to-gray-900' : 'from-rose-100 via-pink-50 to-orange-50'}`}> <div className={`hidden lg:block fixed top-0 left-0 h-screen w-1/2 overflow-hidden z-10 bg-gradient-to-br ${isDark ? 'from-gray-900 via-gray-800 to-gray-900' : 'from-rose-100 via-pink-50 to-orange-50'}`}>
<div className="absolute inset-0"> <div className="absolute inset-0">
<Image <Image
src="/doctors.png" src="/session.jpg"
alt="Therapy session" alt="Therapy session with diverse clients"
fill fill
className="object-cover" className="object-cover"
priority priority
@ -367,9 +409,8 @@ export default function BookNowPage() {
lastName: "", lastName: "",
email: "", email: "",
phone: "", phone: "",
appointmentType: "", preferredDays: [],
preferredDate: "", preferredTimes: [],
preferredTime: "",
message: "", message: "",
}); });
}} }}
@ -490,92 +531,75 @@ export default function BookNowPage() {
Appointment Details Appointment Details
</h2> </h2>
<div className="space-y-2"> <div className="space-y-4">
<label
htmlFor="appointmentType"
className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-gray-700'}`}
>
Appointment Type *
</label>
<Select
value={formData.appointmentType}
onValueChange={(value) =>
handleChange("appointmentType", value)
}
required
>
<SelectTrigger id="appointmentType" className={`h-11 ${isDark ? 'bg-gray-700 border-gray-600 text-white' : 'bg-white border-gray-300 text-gray-900'}`}>
<SelectValue placeholder="Select appointment type" />
</SelectTrigger>
<SelectContent className={isDark ? 'bg-gray-700 border-gray-600' : 'bg-white border-gray-300'}>
<SelectItem value="initial-consultation">
Initial Consultation
</SelectItem>
<SelectItem value="individual-therapy">
Individual Therapy
</SelectItem>
<SelectItem value="family-therapy">Family Therapy</SelectItem>
<SelectItem value="couples-therapy">
Couples Therapy
</SelectItem>
<SelectItem value="group-therapy">Group Therapy</SelectItem>
<SelectItem value="follow-up">Follow-up Session</SelectItem>
</SelectContent>
</Select>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div className="space-y-2"> <div className="space-y-2">
<label <label
htmlFor="preferredDate"
className={`text-sm font-medium flex items-center gap-2 ${isDark ? 'text-gray-300' : 'text-gray-700'}`} className={`text-sm font-medium flex items-center gap-2 ${isDark ? 'text-gray-300' : 'text-gray-700'}`}
> >
<Calendar className={`w-4 h-4 ${isDark ? 'text-gray-400' : 'text-gray-500'}`} /> <Calendar className={`w-4 h-4 ${isDark ? 'text-gray-400' : 'text-gray-500'}`} />
Preferred Date * Available Days *
</label> </label>
<Input <div className="flex flex-wrap gap-3">
id="preferredDate" {['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'].map((day) => (
type="date" <label
value={formData.preferredDate} key={day}
onChange={(e) => className={`flex items-center gap-2 cursor-pointer px-4 py-2 rounded-lg border transition-all ${
handleChange("preferredDate", e.target.value) formData.preferredDays.includes(day)
} ? isDark
required ? 'bg-rose-600 border-rose-500 text-white'
min={new Date().toISOString().split("T")[0]} : 'bg-rose-500 border-rose-500 text-white'
className={`h-11 ${isDark ? 'bg-gray-700 border-gray-600 text-white' : 'bg-white border-gray-300 text-gray-900'}`} : isDark
/> ? 'bg-gray-700 border-gray-600 text-gray-300 hover:border-rose-500'
: 'bg-white border-gray-300 text-gray-700 hover:border-rose-500'
}`}
>
<input
type="checkbox"
checked={formData.preferredDays.includes(day)}
onChange={() => handleDayToggle(day)}
className="sr-only"
/>
<span className="text-sm font-medium">{day}</span>
</label>
))}
</div>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<label <label
htmlFor="preferredTime"
className={`text-sm font-medium flex items-center gap-2 ${isDark ? 'text-gray-300' : 'text-gray-700'}`} className={`text-sm font-medium flex items-center gap-2 ${isDark ? 'text-gray-300' : 'text-gray-700'}`}
> >
<Clock className={`w-4 h-4 ${isDark ? 'text-gray-400' : 'text-gray-500'}`} /> <Clock className={`w-4 h-4 ${isDark ? 'text-gray-400' : 'text-gray-500'}`} />
Preferred Time * Preferred Time *
</label> </label>
<Select <div className="flex flex-wrap gap-3">
value={formData.preferredTime} {[
onValueChange={(value) => { value: 'morning', label: 'Morning' },
handleChange("preferredTime", value) { value: 'lunchtime', label: 'Lunchtime' },
} { value: 'afternoon', label: 'Afternoon' }
required ].map((time) => (
> <label
<SelectTrigger id="preferredTime" className={`h-11 ${isDark ? 'bg-gray-700 border-gray-600 text-white' : 'bg-white border-gray-300 text-gray-900'}`}> key={time.value}
<SelectValue placeholder="Select time" /> className={`flex items-center gap-2 cursor-pointer px-4 py-2 rounded-lg border transition-all ${
</SelectTrigger> formData.preferredTimes.includes(time.value)
<SelectContent className={isDark ? 'bg-gray-700 border-gray-600' : 'bg-white border-gray-300'}> ? isDark
<SelectItem value="9:00 AM">9:00 AM</SelectItem> ? 'bg-rose-600 border-rose-500 text-white'
<SelectItem value="10:00 AM">10:00 AM</SelectItem> : 'bg-rose-500 border-rose-500 text-white'
<SelectItem value="11:00 AM">11:00 AM</SelectItem> : isDark
<SelectItem value="12:00 PM">12:00 PM</SelectItem> ? 'bg-gray-700 border-gray-600 text-gray-300 hover:border-rose-500'
<SelectItem value="1:00 PM">1:00 PM</SelectItem> : 'bg-white border-gray-300 text-gray-700 hover:border-rose-500'
<SelectItem value="2:00 PM">2:00 PM</SelectItem> }`}
<SelectItem value="3:00 PM">3:00 PM</SelectItem> >
<SelectItem value="4:00 PM">4:00 PM</SelectItem> <input
<SelectItem value="5:00 PM">5:00 PM</SelectItem> type="checkbox"
</SelectContent> checked={formData.preferredTimes.includes(time.value)}
</Select> onChange={() => handleTimeToggle(time.value)}
className="sr-only"
/>
<span className="text-sm font-medium">{time.label}</span>
</label>
))}
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -5,6 +5,7 @@ import { useInView } from "framer-motion";
import { useRef } from "react"; import { useRef } from "react";
import { Users, UserCheck, Globe } from "lucide-react"; import { Users, UserCheck, Globe } from "lucide-react";
import { useAppTheme } from "@/components/ThemeProvider"; import { useAppTheme } from "@/components/ThemeProvider";
import Image from "next/image";
export function ClientFocus() { export function ClientFocus() {
const ref = useRef(null); const ref = useRef(null);
@ -94,7 +95,7 @@ export function ClientFocus() {
animate={isInView ? { opacity: 1, y: 0 } : {}} animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.8, delay: 0.2 }} transition={{ duration: 0.8, delay: 0.2 }}
> >
Client Focus Who We Serve
</motion.h2> </motion.h2>
</motion.div> </motion.div>
@ -143,7 +144,7 @@ export function ClientFocus() {
<p className="text-muted-foreground">Individuals</p> <p className="text-muted-foreground">Individuals</p>
</motion.div> </motion.div>
{/* Ethnicity */} {/* Communities */}
<motion.div <motion.div
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}} animate={isInView ? { opacity: 1, y: 0 } : {}}
@ -151,12 +152,28 @@ export function ClientFocus() {
className="bg-card/50 backdrop-blur-sm rounded-2xl p-6 border border-border/50 hover:border-rose-500/50 hover:shadow-lg hover:shadow-rose-500/10 hover:scale-105 transition-all duration-300" className="bg-card/50 backdrop-blur-sm rounded-2xl p-6 border border-border/50 hover:border-rose-500/50 hover:shadow-lg hover:shadow-rose-500/10 hover:scale-105 transition-all duration-300"
> >
<div className="flex items-center gap-3 mb-4"> <div className="flex items-center gap-3 mb-4">
<div className="bg-gradient-to-br from-rose-500/20 via-pink-500/20 to-orange-500/20 dark:from-rose-500/30 dark:via-pink-500/30 dark:to-orange-500/30 p-3 rounded-xl">
<Globe className="h-6 w-6 text-rose-600 dark:text-rose-400" /> <h3 className="text-xl font-semibold text-foreground text-center justify-center">Providing Support to all of South Florida's Diverse Communities</h3>
</div> </div>
<h3 className="text-xl font-semibold text-foreground">Ethnicity</h3> <div className="space-y-3">
<div className="flex justify-center items-center">
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={isInView ? { opacity: 1, scale: 1 } : {}}
transition={{ duration: 0.5, delay: 0.5 }}
className="relative w-full max-w-md h-auto"
>
<Image
src="/flagss.png"
alt="Organization of American States Flags"
width={400}
height={267}
className="w-full h-auto object-contain rounded-lg"
priority
/>
</motion.div>
</div>
</div> </div>
<p className="text-muted-foreground">Black and African American</p>
</motion.div> </motion.div>
</div> </div>
</div> </div>

View File

@ -3,7 +3,7 @@
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { useInView } from "framer-motion"; import { useInView } from "framer-motion";
import { useRef } from "react"; import { useRef } from "react";
import { CreditCard, DollarSign, Shield } from "lucide-react"; import { CreditCard, DollarSign } from "lucide-react";
import { useAppTheme } from "@/components/ThemeProvider"; import { useAppTheme } from "@/components/ThemeProvider";
export function Finances() { export function Finances() {
@ -19,31 +19,6 @@ export function Finances() {
"Visa" "Visa"
]; ];
const insuranceProviders = [
"Aetna",
"Aetna - Medicare",
"Aetna - WebTPA",
"All Savers",
"Ambetter",
"AvMed",
"Cigna and Evernorth",
"EAP:Cigna",
"EAP:UnitedHealthcare/Optum",
"Golden Rule",
"Harvard Pilgrim/United",
"Humana",
"Humana - Medicare",
"Humana Dual- Medicaid/Medicare",
"Medicaid",
"Optum",
"Oscar Health",
"Oxford",
"Surest (formerly Bind)",
"Tufts Health/Cigna",
"UHC/Optum - Medicare",
"United Medical Resources (UMR)",
"UnitedHealthcare UHC | UBH"
];
return ( return (
<section <section
@ -132,7 +107,7 @@ export function Finances() {
</motion.p> </motion.p>
</motion.div> </motion.div>
<div className="grid md:grid-cols-3 gap-6 mb-8"> <div className="grid md:grid-cols-2 gap-6 mb-8">
{/* Fees */} {/* Fees */}
<motion.div <motion.div
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
@ -178,33 +153,6 @@ export function Finances() {
</div> </div>
</motion.div> </motion.div>
{/* Insurance */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.5, delay: 0.4 }}
className="bg-card/50 backdrop-blur-sm rounded-2xl p-6 border border-border/50 hover:border-rose-500/50 hover:shadow-lg hover:shadow-rose-500/10 hover:scale-105 transition-all duration-300"
>
<div className="flex items-center gap-3 mb-4">
<div className="bg-gradient-to-br from-rose-500/20 via-pink-500/20 to-orange-500/20 dark:from-rose-500/30 dark:via-pink-500/30 dark:to-orange-500/30 p-3 rounded-xl">
<Shield className="h-6 w-6 text-rose-600 dark:text-rose-400" />
</div>
<h3 className="text-xl font-semibold text-foreground">Insurance</h3>
</div>
<div className="max-h-64 overflow-y-auto space-y-2 pr-2">
{insuranceProviders.map((provider, index) => (
<motion.p
key={provider}
className="text-sm text-muted-foreground"
initial={{ opacity: 0, x: -10 }}
animate={isInView ? { opacity: 1, x: 0 } : {}}
transition={{ duration: 0.3, delay: 0.5 + index * 0.02 }}
>
{provider}
</motion.p>
))}
</div>
</motion.div>
</div> </div>
</div> </div>
</section> </section>

View File

@ -18,7 +18,7 @@ export function HeroSection() {
<div <div
className="absolute inset-0 z-0" className="absolute inset-0 z-0"
style={{ style={{
backgroundImage: `url('https://images.unsplash.com/photo-1506126613408-eca07ce68773?ixlib=rb-4.0.3&auto=format&fit=crop&w=2070&q=80')`, backgroundImage: `url('/large.jpeg')`,
backgroundSize: 'cover', backgroundSize: 'cover',
backgroundPosition: 'center', backgroundPosition: 'center',
backgroundRepeat: 'no-repeat', backgroundRepeat: 'no-repeat',
@ -29,7 +29,7 @@ export function HeroSection() {
<div <div
className="absolute inset-0 z-[1]" className="absolute inset-0 z-[1]"
style={{ style={{
backgroundColor: isDark ? 'rgba(0, 0, 0, 0.35)' : 'rgba(0, 0, 0, 0.25)' backgroundColor: isDark ? 'rgba(0, 0, 0, 0.65)' : 'rgba(0, 0, 0, 0.55)'
}} }}
/> />

View File

@ -220,8 +220,8 @@ export function Services() {
animate={isInView ? { opacity: 1 } : {}} animate={isInView ? { opacity: 1 } : {}}
transition={{ duration: 0.8, delay: 0.9 }} transition={{ duration: 0.8, delay: 0.9 }}
> >
Therapy is tailored for children (age 610), teens, adults, and older adults, with services offered to individuals and a Therapy is tailored for children (age 610), teens, adults, and older adults, with services offered to individuals.
special focus on supporting Black and African American families in Miami and Hollywood, Florida. I welcome clients from diverse backgrounds and communities throughout Miami and Hollywood, Florida.
</motion.p> </motion.p>
</div> </div>
</motion.div> </motion.div>

BIN
public/flagss.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

BIN
public/section-image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

BIN
public/session.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

BIN
public/woman.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB