diff --git a/src/components/RegistrationReviewDialog.tsx b/src/components/RegistrationReviewDialog.tsx new file mode 100644 index 0000000..be5b6ff --- /dev/null +++ b/src/components/RegistrationReviewDialog.tsx @@ -0,0 +1,107 @@ +import React from "react"; + +export interface RegistrationData { + fullName: string; + email: string; + phone: string; + wilaya: string; + city: string; + address: string; + notes?: string; + guests: number; +} + +interface RegistrationReviewDialogProps { + open: boolean; + onClose: () => void; + onConfirm: () => void; + data: RegistrationData; + agreementChecked: boolean; + onAgreementChange: (checked: boolean) => void; +} + +export const RegistrationReviewDialog: React.FC = ({ + open, + onClose, + onConfirm, + data, + agreementChecked, + onAgreementChange, +}) => { + if (!open) { + return null; + } + + return ( +
+
+
+

مراجعة معلومات التسجيل

+ +
+ +
+ + + + + + + + {data.notes ? : null} +
+ + + +
+ + +
+
+
+ ); +}; + +interface InfoRowProps { + label: string; + value: string; +} + +const InfoRow: React.FC = ({ label, value }) => ( +

+ {label} + {value} +

+); + +export default RegistrationReviewDialog; diff --git a/src/lib/registration.ts b/src/lib/registration.ts new file mode 100644 index 0000000..cb5efad --- /dev/null +++ b/src/lib/registration.ts @@ -0,0 +1,42 @@ +import { RegistrationData } from "../components/RegistrationReviewDialog"; + +export interface RegistrationPayload extends RegistrationData { + status: "pending"; +} + +export interface RegistrationResponse { + success: boolean; + message: string; +} + +/** + * Prepares the payload and triggers the registration API to persist a pending request + * and send the confirmation email. + */ +export async function submitRegistration( + data: RegistrationData, + signal?: AbortSignal +): Promise { + const payload: RegistrationPayload = { + ...data, + status: "pending", + }; + + const response = await fetch("/api/registrations", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + registration: payload, + sendConfirmationEmail: true, + }), + signal, + }); + + if (!response.ok) { + throw new Error("فشل إرسال طلب التسجيل. يرجى المحاولة لاحقًا."); + } + + return (await response.json()) as RegistrationResponse; +} diff --git a/src/pages/register.tsx b/src/pages/register.tsx new file mode 100644 index 0000000..24aae92 --- /dev/null +++ b/src/pages/register.tsx @@ -0,0 +1,401 @@ +import React, { FormEvent, useMemo, useState } from "react"; +import Head from "next/head"; +import RegistrationReviewDialog, { + RegistrationData, +} from "../components/RegistrationReviewDialog"; +import { submitRegistration } from "../lib/registration"; + +const wilayas: string[] = [ + "أدرار", + "الشلف", + "الأغواط", + "أم البواقي", + "باتنة", + "بجاية", + "بسكرة", + "بشار", + "البليدة", + "البويرة", + "تمنراست", + "تبسة", + "تلمسان", + "تيارت", + "تيزي وزو", + "الجزائر العاصمة", + "الجلفة", + "جيجل", + "سطيف", + "سعيدة", + "سكيكدة", + "سيدي بلعباس", + "عنابة", + "قالمة", + "قسنطينة", + "المدية", + "مستغانم", + "المسيلة", + "معسكر", + "ورقلة", + "وهران", + "البيض", + "إليزي", + "برج بوعريريج", + "بومرداس", + "الطارف", + "تندوف", + "تيسمسيلت", + "الوادي", + "خنشلة", + "سوق أهراس", + "تيبازة", + "ميلة", + "عين الدفلى", + "النعامة", + "عين تموشنت", + "غرداية", + "غليزان", + "تيميمون", + "برج باجي مختار", + "أولاد جلال", + "بني عباس", + "عين صالح", + "عين قزام", + "تقرت", + "جانت", + "المغير", + "المنيعة", +]; + +const defaultFormState: RegistrationData = { + fullName: "", + email: "", + phone: "", + wilaya: "", + city: "", + address: "", + notes: "", + guests: 1, +}; + +const RegisterPage: React.FC = () => { + const initialData = useMemo(() => ({ ...defaultFormState }), []); + const [formData, setFormData] = useState(initialData); + const [isReviewOpen, setIsReviewOpen] = useState(false); + const [agreementChecked, setAgreementChecked] = useState(false); + const [isSubmitting, setIsSubmitting] = useState(false); + const [showThankYou, setShowThankYou] = useState(false); + const [error, setError] = useState(null); + + const handleChange = ( + event: React.ChangeEvent + ) => { + const { name, value } = event.target; + setFormData((prev) => ({ + ...prev, + [name]: name === "guests" ? Number(value) || 0 : value, + })); + }; + + const handleSubmit = (event: FormEvent) => { + event.preventDefault(); + setIsReviewOpen(true); + }; + + const handleDialogConfirm = async () => { + try { + setIsSubmitting(true); + setError(null); + await submitRegistration(formData); + setShowThankYou(true); + setIsReviewOpen(false); + setAgreementChecked(false); + } catch (submissionError) { + const message = + submissionError instanceof Error + ? submissionError.message + : "حدث خطأ غير متوقع. يرجى المحاولة مرة أخرى."; + setError(message); + } finally { + setIsSubmitting(false); + } + }; + + const handleReset = () => { + setFormData(initialData); + setError(null); + }; + + const handleConfirmBooking = () => { + // Placeholder for navigation or additional confirmation behaviour. + alert("تم تأكيد الحجز! سيتم التواصل معك عبر البريد الإلكتروني قريبًا."); + }; + + return ( + <> + + التسجيل لحجز الفعالية + + +
+
+
+
+
+
+

سجّل الآن لحجز مكانك

+

+ يرجى تعبئة جميع الحقول بدقة حتى نتمكن من تأكيد حجزك وإرسال بريد التأكيد. +

+ + {error ? ( +

{error}

+ ) : null} + +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+ +
+
+ + +
+ +
+ +