Files
uldp.edu.vn/app/appointment/page.tsx
2026-01-31 12:35:43 +07:00

502 lines
21 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useState } from "react";
import appointmentData from "./appointment.json";
export default function AppointmentPage() {
const [selectedType, setSelectedType] = useState("consultation");
const [selectedConsultant, setSelectedConsultant] = useState("");
const [selectedDate, setSelectedDate] = useState("");
const [selectedTime, setSelectedTime] = useState("");
const [selectedOffice, setSelectedOffice] = useState("online");
const [openFaq, setOpenFaq] = useState<number | null>(null);
const [formData, setFormData] = useState({
fullName: "",
email: "",
phone: "",
visaType: "",
country: "",
travelDate: "",
previousVisa: "",
notes: "",
});
const handleInputChange = (
e: React.ChangeEvent<
HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
>,
) => {
const { name, value } = e.target;
setFormData((prev) => ({
...prev,
[name]: value,
}));
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
console.log("Appointment booked:", {
type: selectedType,
consultant: selectedConsultant,
date: selectedDate,
time: selectedTime,
office: selectedOffice,
...formData,
});
alert(
"Đặt lịch thành công! Chúng tôi sẽ liên hệ xác nhận trong thời gian sớm nhất.",
);
};
const toggleFaq = (index: number) => {
setOpenFaq(openFaq === index ? null : index);
};
const selectedAppointmentType = appointmentData.appointmentTypes.find(
(type) => type.id === selectedType,
);
const selectedConsultantData = appointmentData.consultants.find(
(c) => c.id === selectedConsultant,
);
return (
<div className="container mx-auto px-4 py-8">
<div className="max-w-6xl mx-auto">
{/* Header */}
<div className="text-center mb-12">
<h1 className="text-4xl font-bold text-gray-900 mb-4">
{appointmentData.title}
</h1>
<p className="text-xl text-gray-600 mb-8">
{appointmentData.subtitle}
</p>
{/* Benefits */}
<div className="grid md:grid-cols-4 gap-6 max-w-4xl mx-auto">
{appointmentData.hero.benefits.map((benefit, index) => (
<div key={index} className="bg-blue-50 p-4 rounded-lg">
<p className="text-blue-800 font-medium">{benefit}</p>
</div>
))}
</div>
</div>
<div className="grid lg:grid-cols-3 gap-8">
{/* Booking Form */}
<div className="lg:col-span-2">
<div className="bg-white rounded-lg shadow-lg p-6">
<h2 className="text-2xl font-bold text-gray-900 mb-6">
Đt Lịch Hẹn
</h2>
<form onSubmit={handleSubmit} className="space-y-6">
{/* Appointment Type */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-3">
Loại vấn <span className="text-red-500">*</span>
</label>
<div className="grid md:grid-cols-2 gap-4">
{appointmentData.appointmentTypes.map((type) => (
<div
key={type.id}
className={`border rounded-lg p-4 cursor-pointer transition-colors ${
selectedType === type.id
? "border-blue-500 bg-blue-50"
: "border-gray-200 hover:border-gray-300"
}`}
onClick={() => setSelectedType(type.id)}
>
<div className="flex items-center justify-between mb-2">
<div className="flex items-center">
<span className="text-2xl mr-3">{type.icon}</span>
<div>
<h3 className="font-semibold">{type.name}</h3>
{type.popular && (
<span className="bg-green-100 text-green-800 px-2 py-1 rounded text-xs">
Phổ biến
</span>
)}
</div>
</div>
<div className="text-right">
<div className="font-bold text-blue-600">
{type.price}
</div>
<div className="text-sm text-gray-500">
{type.duration}
</div>
</div>
</div>
<p className="text-gray-600 text-sm">
{type.description}
</p>
</div>
))}
</div>
</div>
{/* Consultant Selection */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-3">
Chọn chuyên gia
</label>
<div className="space-y-3">
<div
className={`border rounded-lg p-4 cursor-pointer transition-colors ${
selectedConsultant === ""
? "border-blue-500 bg-blue-50"
: "border-gray-200 hover:border-gray-300"
}`}
onClick={() => setSelectedConsultant("")}
>
<div className="font-medium">
Đ chúng tôi chọn chuyên gia phù hợp
</div>
<div className="text-sm text-gray-600">
Dựa trên loại visa quốc gia bạn quan tâm
</div>
</div>
{appointmentData.consultants.map((consultant) => (
<div
key={consultant.id}
className={`border rounded-lg p-4 cursor-pointer transition-colors ${
selectedConsultant === consultant.id
? "border-blue-500 bg-blue-50"
: "border-gray-200 hover:border-gray-300"
}`}
onClick={() => setSelectedConsultant(consultant.id)}
>
<div className="flex items-center">
<div className="w-12 h-12 bg-gray-200 rounded-full flex items-center justify-center mr-4">
<span className="text-gray-600">👤</span>
</div>
<div className="flex-1">
<div className="font-semibold">
{consultant.name}
</div>
<div className="text-sm text-gray-600">
{consultant.title}
</div>
<div className="flex items-center text-sm text-gray-500">
<span className="mr-2">
{consultant.rating}
</span>
<span className="mr-2">
({consultant.reviews} đánh giá)
</span>
<span>{consultant.experience}</span>
</div>
</div>
</div>
</div>
))}
</div>
</div>
{/* Date & Time */}
<div className="grid md:grid-cols-2 gap-6">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Ngày hẹn <span className="text-red-500">*</span>
</label>
<input
type="date"
value={selectedDate}
onChange={(e) => setSelectedDate(e.target.value)}
min={new Date().toISOString().split("T")[0]}
required
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Giờ hẹn <span className="text-red-500">*</span>
</label>
<select
value={selectedTime}
onChange={(e) => setSelectedTime(e.target.value)}
required
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
>
<option value="">Chọn giờ</option>
{appointmentData.timeSlots.map((slot) => (
<option
key={slot.time}
value={slot.time}
disabled={!slot.available}
>
{slot.label} {!slot.available && "(Đã đặt)"}
</option>
))}
</select>
</div>
</div>
{/* Office Selection */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-3">
Hình thức vấn <span className="text-red-500">*</span>
</label>
<div className="space-y-3">
<div
className={`border rounded-lg p-4 cursor-pointer transition-colors ${
selectedOffice === "online"
? "border-blue-500 bg-blue-50"
: "border-gray-200 hover:border-gray-300"
}`}
onClick={() => setSelectedOffice("online")}
>
<div className="flex items-center">
<span className="text-2xl mr-3">💻</span>
<div>
<div className="font-medium"> vấn online</div>
<div className="text-sm text-gray-600">
Video call qua Zoom/Google Meet
</div>
</div>
</div>
</div>
{appointmentData.offices.map((office, index) => (
<div
key={index}
className={`border rounded-lg p-4 cursor-pointer transition-colors ${
selectedOffice === office.name
? "border-blue-500 bg-blue-50"
: "border-gray-200 hover:border-gray-300"
}`}
onClick={() => setSelectedOffice(office.name)}
>
<div className="flex items-center">
<span className="text-2xl mr-3">🏢</span>
<div>
<div className="font-medium">
Văn phòng {office.name}
</div>
<div className="text-sm text-gray-600">
{office.address}
</div>
<div className="text-sm text-gray-500">
{office.hours}
</div>
</div>
</div>
</div>
))}
</div>
</div>
{/* Personal Information */}
<div className="border-t pt-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">
Thông Tin Nhân
</h3>
<div className="grid md:grid-cols-2 gap-6">
{appointmentData.formFields.map((field) => (
<div
key={field.name}
className={
field.type === "textarea" ? "md:col-span-2" : ""
}
>
<label className="block text-sm font-medium text-gray-700 mb-2">
{field.label}
{field.required && (
<span className="text-red-500 ml-1">*</span>
)}
</label>
{field.type === "textarea" ? (
<textarea
name={field.name}
value={
formData[field.name as keyof typeof formData]
}
onChange={handleInputChange}
placeholder={field.placeholder}
required={field.required}
rows={4}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
) : field.type === "select" ? (
<select
name={field.name}
value={
formData[field.name as keyof typeof formData]
}
onChange={handleInputChange}
required={field.required}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
>
<option value="">
Chọn {field.label.toLowerCase()}
</option>
{field.options?.map((option, index) => (
<option key={index} value={option}>
{option}
</option>
))}
</select>
) : (
<input
type={field.type}
name={field.name}
value={
formData[field.name as keyof typeof formData]
}
onChange={handleInputChange}
placeholder={field.placeholder}
required={field.required}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
)}
</div>
))}
</div>
</div>
{/* Submit Button */}
<button
type="submit"
className="w-full bg-blue-600 text-white py-3 px-6 rounded-lg hover:bg-blue-700 transition-colors font-medium text-lg"
>
Đt Lịch Hẹn
</button>
</form>
</div>
</div>
{/* Sidebar */}
<div className="space-y-6">
{/* Booking Summary */}
<div className="bg-white rounded-lg shadow-lg p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">
Tóm Tắt Đt Lịch
</h3>
{selectedAppointmentType && (
<div className="space-y-3">
<div className="flex justify-between">
<span className="text-gray-600">Loại vấn:</span>
<span className="font-medium">
{selectedAppointmentType.name}
</span>
</div>
<div className="flex justify-between">
<span className="text-gray-600">Thời gian:</span>
<span className="font-medium">
{selectedAppointmentType.duration}
</span>
</div>
<div className="flex justify-between">
<span className="text-gray-600">Phí:</span>
<span className="font-medium text-blue-600">
{selectedAppointmentType.price}
</span>
</div>
{selectedConsultantData && (
<div className="border-t pt-3">
<div className="flex justify-between">
<span className="text-gray-600">Chuyên gia:</span>
<span className="font-medium">
{selectedConsultantData.name}
</span>
</div>
</div>
)}
{selectedDate && selectedTime && (
<div className="border-t pt-3">
<div className="flex justify-between">
<span className="text-gray-600">Ngày giờ:</span>
<span className="font-medium">
{selectedDate} lúc{" "}
{
appointmentData.timeSlots.find(
(s) => s.time === selectedTime,
)?.label
}
</span>
</div>
</div>
)}
{selectedOffice && (
<div className="flex justify-between">
<span className="text-gray-600">Hình thức:</span>
<span className="font-medium">
{selectedOffice === "online"
? "Online"
: `Văn phòng ${selectedOffice}`}
</span>
</div>
)}
</div>
)}
</div>
{/* Contact Info */}
<div className="bg-blue-50 rounded-lg p-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">
Cần Hỗ Trợ?
</h3>
<div className="space-y-3">
<div className="flex items-center">
<span className="text-blue-600 mr-2">📞</span>
<span>1900 1234</span>
</div>
<div className="flex items-center">
<span className="text-blue-600 mr-2"></span>
<span>info@visaservice.com</span>
</div>
<div className="flex items-center">
<span className="text-blue-600 mr-2">💬</span>
<span>Chat trực tuyến 24/7</span>
</div>
</div>
</div>
</div>
</div>
{/* FAQs */}
<div className="mt-16">
<h2 className="text-2xl font-bold text-gray-900 mb-8 text-center">
Câu Hỏi Thường Gặp
</h2>
<div className="max-w-3xl mx-auto space-y-4">
{appointmentData.faqs.map((faq, index) => (
<div
key={index}
className="bg-white rounded-lg shadow-md overflow-hidden"
>
<button
className="w-full p-6 text-left hover:bg-gray-50 transition-colors"
onClick={() => toggleFaq(index)}
>
<div className="flex items-center justify-between">
<h3 className="font-semibold text-gray-900">
{faq.question}
</h3>
<span className="text-gray-400">
{openFaq === index ? "" : "+"}
</span>
</div>
</button>
{openFaq === index && (
<div className="px-6 pb-6">
<p className="text-gray-700">{faq.answer}</p>
</div>
)}
</div>
))}
</div>
</div>
</div>
</div>
);
}