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:
parent
911ae7433f
commit
487132ee9b
@ -21,7 +21,7 @@ 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="/section-image.png"
|
src="/woman.jpg"
|
||||||
alt="Therapy and counseling session with African American clients"
|
alt="Therapy and counseling session with African American clients"
|
||||||
fill
|
fill
|
||||||
className="object-cover object-center"
|
className="object-cover object-center"
|
||||||
|
|||||||
@ -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,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={`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="/section-image.png"
|
src="/session.jpg"
|
||||||
alt="Therapy session with diverse clients"
|
alt="Therapy session with diverse clients"
|
||||||
fill
|
fill
|
||||||
className="object-cover"
|
className="object-cover"
|
||||||
@ -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-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label
|
<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">
|
|
||||||
<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
|
||||||
|
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'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<SelectTrigger id="preferredTime" className={`h-11 ${isDark ? 'bg-gray-700 border-gray-600 text-white' : 'bg-white border-gray-300 text-gray-900'}`}>
|
<input
|
||||||
<SelectValue placeholder="Select time" />
|
type="checkbox"
|
||||||
</SelectTrigger>
|
checked={formData.preferredTimes.includes(time.value)}
|
||||||
<SelectContent className={isDark ? 'bg-gray-700 border-gray-600' : 'bg-white border-gray-300'}>
|
onChange={() => handleTimeToggle(time.value)}
|
||||||
<SelectItem value="9:00 AM">9:00 AM</SelectItem>
|
className="sr-only"
|
||||||
<SelectItem value="10:00 AM">10:00 AM</SelectItem>
|
/>
|
||||||
<SelectItem value="11:00 AM">11:00 AM</SelectItem>
|
<span className="text-sm font-medium">{time.label}</span>
|
||||||
<SelectItem value="12:00 PM">12:00 PM</SelectItem>
|
</label>
|
||||||
<SelectItem value="1:00 PM">1:00 PM</SelectItem>
|
))}
|
||||||
<SelectItem value="2:00 PM">2:00 PM</SelectItem>
|
</div>
|
||||||
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -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"
|
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>
|
|
||||||
<h3 className="text-xl font-semibold text-foreground">Communities</h3>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="flex justify-center items-center">
|
<div className="flex justify-center items-center">
|
||||||
@ -166,7 +164,7 @@ export function ClientFocus() {
|
|||||||
className="relative w-full max-w-md h-auto"
|
className="relative w-full max-w-md h-auto"
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
src="/flags.png"
|
src="/flagss.png"
|
||||||
alt="Organization of American States Flags"
|
alt="Organization of American States Flags"
|
||||||
width={400}
|
width={400}
|
||||||
height={267}
|
height={267}
|
||||||
|
|||||||
@ -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',
|
||||||
|
|||||||
BIN
public/flags.png
BIN
public/flags.png
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
BIN
public/flagss.png
Normal file
BIN
public/flagss.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 83 KiB |
BIN
public/session.jpg
Normal file
BIN
public/session.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.2 MiB |
BIN
public/woman.jpg
Normal file
BIN
public/woman.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.3 MiB |
Loading…
Reference in New Issue
Block a user