Update login and book-now pages to enhance user experience by changing images for better representation, refactoring appointment scheduling to use preferred days and times, and improving error handling for user input.

This commit is contained in:
iamkiddy 2025-11-22 20:18:52 +00:00
parent 911ae7433f
commit 487132ee9b
8 changed files with 117 additions and 95 deletions

View File

@ -21,7 +21,7 @@ export default function Login() {
{/* Background Image */}
<div className="absolute inset-0 z-0">
<Image
src="/section-image.png"
src="/woman.jpg"
alt="Therapy and counseling session with African American clients"
fill
className="object-cover object-center"

View File

@ -78,9 +78,8 @@ export default function BookNowPage() {
lastName: "",
email: "",
phone: "",
appointmentType: "",
preferredDate: "",
preferredTime: "",
preferredDays: [] as string[],
preferredTimes: [] as string[],
message: "",
});
const [loading, setLoading] = useState(false);
@ -105,17 +104,41 @@ export default function BookNowPage() {
setError(null);
try {
// Convert time to 24-hour format for ISO string
const time24 = formData.preferredTime.includes("PM")
? formData.preferredTime.replace("PM", "").trim().split(":").map((v, i) =>
i === 0 ? (parseInt(v) === 12 ? 12 : parseInt(v) + 12) : v
).join(":")
: formData.preferredTime.replace("AM", "").trim().split(":").map((v, i) =>
i === 0 ? (parseInt(v) === 12 ? "00" : v.padStart(2, "0")) : v
).join(":");
if (formData.preferredDays.length === 0) {
setError("Please select at least one available day.");
setLoading(false);
return;
}
if (formData.preferredTimes.length === 0) {
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)
const dateTimeString = `${formData.preferredDate}T${time24}:00Z`;
const dateTimeString = `${dateString}T${time24}:00Z`;
// Prepare request payload
const payload = {
@ -123,9 +146,10 @@ export default function BookNowPage() {
last_name: formData.lastName,
email: formData.email,
phone: formData.phone,
appointment_type: formData.appointmentType,
scheduled_at: dateTimeString,
duration: 60, // Default to 60 minutes
preferred_days: formData.preferredDays,
preferred_times: formData.preferredTimes,
notes: formData.message || "",
};
@ -199,6 +223,24 @@ export default function BookNowPage() {
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 date = new Date(dateString);
return date.toLocaleString("en-US", {
@ -219,7 +261,7 @@ 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="absolute inset-0">
<Image
src="/section-image.png"
src="/session.jpg"
alt="Therapy session with diverse clients"
fill
className="object-cover"
@ -367,9 +409,8 @@ export default function BookNowPage() {
lastName: "",
email: "",
phone: "",
appointmentType: "",
preferredDate: "",
preferredTime: "",
preferredDays: [],
preferredTimes: [],
message: "",
});
}}
@ -490,92 +531,75 @@ export default function BookNowPage() {
Appointment Details
</h2>
<div className="space-y-2">
<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-4">
<div className="space-y-2">
<label
htmlFor="preferredDate"
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'}`} />
Preferred Date *
Available Days *
</label>
<Input
id="preferredDate"
type="date"
value={formData.preferredDate}
onChange={(e) =>
handleChange("preferredDate", e.target.value)
}
required
min={new Date().toISOString().split("T")[0]}
className={`h-11 ${isDark ? 'bg-gray-700 border-gray-600 text-white' : 'bg-white border-gray-300 text-gray-900'}`}
/>
<div className="flex flex-wrap gap-3">
{['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'].map((day) => (
<label
key={day}
className={`flex items-center gap-2 cursor-pointer px-4 py-2 rounded-lg border transition-all ${
formData.preferredDays.includes(day)
? isDark
? 'bg-rose-600 border-rose-500 text-white'
: 'bg-rose-500 border-rose-500 text-white'
: 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 className="space-y-2">
<label
htmlFor="preferredTime"
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'}`} />
Preferred Time *
</label>
<Select
value={formData.preferredTime}
onValueChange={(value) =>
handleChange("preferredTime", value)
}
required
>
<SelectTrigger id="preferredTime" className={`h-11 ${isDark ? 'bg-gray-700 border-gray-600 text-white' : 'bg-white border-gray-300 text-gray-900'}`}>
<SelectValue placeholder="Select time" />
</SelectTrigger>
<SelectContent className={isDark ? 'bg-gray-700 border-gray-600' : 'bg-white border-gray-300'}>
<SelectItem value="9:00 AM">9:00 AM</SelectItem>
<SelectItem value="10:00 AM">10:00 AM</SelectItem>
<SelectItem value="11:00 AM">11:00 AM</SelectItem>
<SelectItem value="12:00 PM">12:00 PM</SelectItem>
<SelectItem value="1:00 PM">1:00 PM</SelectItem>
<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>
<SelectItem value="5:00 PM">5:00 PM</SelectItem>
</SelectContent>
</Select>
<div className="flex flex-wrap gap-3">
{[
{ value: 'morning', label: 'Morning' },
{ value: 'lunchtime', label: 'Lunchtime' },
{ value: 'afternoon', label: 'Afternoon' }
].map((time) => (
<label
key={time.value}
className={`flex items-center gap-2 cursor-pointer px-4 py-2 rounded-lg border transition-all ${
formData.preferredTimes.includes(time.value)
? isDark
? 'bg-rose-600 border-rose-500 text-white'
: 'bg-rose-500 border-rose-500 text-white'
: 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.preferredTimes.includes(time.value)}
onChange={() => handleTimeToggle(time.value)}
className="sr-only"
/>
<span className="text-sm font-medium">{time.label}</span>
</label>
))}
</div>
</div>
</div>
</div>

View File

@ -152,10 +152,8 @@ 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"
>
<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" />
</div>
<h3 className="text-xl font-semibold text-foreground">Communities</h3>
<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 className="space-y-3">
<div className="flex justify-center items-center">
@ -166,7 +164,7 @@ export function ClientFocus() {
className="relative w-full max-w-md h-auto"
>
<Image
src="/flags.png"
src="/flagss.png"
alt="Organization of American States Flags"
width={400}
height={267}

View File

@ -18,7 +18,7 @@ export function HeroSection() {
<div
className="absolute inset-0 z-0"
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',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

BIN
public/flagss.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

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