diff --git a/next-env.d.ts b/next-env.d.ts new file mode 100644 index 0000000..4f11a03 --- /dev/null +++ b/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/next.config.js b/next.config.js new file mode 100644 index 0000000..f89c308 --- /dev/null +++ b/next.config.js @@ -0,0 +1,7 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, + swcMinify: true +}; + +module.exports = nextConfig; diff --git a/package.json b/package.json new file mode 100644 index 0000000..56fd041 --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "booking-web", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "next": "13.5.6", + "react": "18.2.0", + "react-dom": "18.2.0" + }, + "devDependencies": { + "@types/node": "20.5.9", + "@types/react": "18.2.21", + "@types/react-dom": "18.2.7", + "typescript": "5.2.2" + } +} diff --git a/src/components/AccountModal.tsx b/src/components/AccountModal.tsx new file mode 100644 index 0000000..104708d --- /dev/null +++ b/src/components/AccountModal.tsx @@ -0,0 +1,176 @@ +import { FormEvent, useEffect, useState } from 'react'; +import { useAuth } from '../hooks/useAuth'; +import { useBookingFlow } from '../hooks/useBookingFlow'; + +export function AccountModal() { + const { login } = useAuth(); + const { + isAccountModalOpen, + closeAccountModal, + openBookingFlow, + setAuthNotification, + authNotification + } = useBookingFlow(); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [error, setError] = useState(null); + const [isSubmitting, setSubmitting] = useState(false); + const [showReset, setShowReset] = useState(false); + const [resetEmail, setResetEmail] = useState(''); + const [isResetting, setResetting] = useState(false); + + useEffect(() => { + if (!isAccountModalOpen) { + setShowReset(false); + setError(null); + setResetEmail(''); + setSubmitting(false); + setResetting(false); + setAuthNotification(null); + } + }, [isAccountModalOpen, setAuthNotification]); + + if (!isAccountModalOpen) { + return null; + } + + const handleLogin = async (event: FormEvent) => { + event.preventDefault(); + setError(null); + setSubmitting(true); + + try { + await login(email, password); + setAuthNotification(null); + closeAccountModal(); + openBookingFlow(); + } catch (loginError) { + setError(loginError instanceof Error ? loginError.message : 'حدث خطأ غير متوقع.'); + } finally { + setSubmitting(false); + } + }; + + const handleResetPassword = async (event: FormEvent) => { + event.preventDefault(); + setError(null); + setResetting(true); + + try { + const response = await fetch('/api/auth/reset-password', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ email: resetEmail }) + }); + + if (!response.ok) { + const body = await response.json().catch(() => ({})); + throw new Error(body.error ?? 'تعذّر إرسال رسالة استرجاع كلمة المرور.'); + } + + setAuthNotification('تم إرسال رسالة استرجاع كلمة المرور إلى بريدك الإلكتروني.'); + setShowReset(false); + setResetEmail(''); + } catch (resetError) { + setError(resetError instanceof Error ? resetError.message : 'تعذّر إرسال الرسالة.'); + } finally { + setResetting(false); + } + }; + + return ( +
+
+ {showReset ? ( + <> +

استعادة كلمة المرور

+

أدخل بريدك الإلكتروني وسنرسل لك رابط إعادة التعيين.

+
+
+ + setResetEmail(event.target.value)} + required + /> +
+ {error &&
{error}
} +
+ + +
+
+ + ) : ( + <> +

تسجيل الدخول

+ {authNotification && ( +
+ {authNotification} +
+ )} +
+
+ + setEmail(event.target.value)} + required + /> +
+
+ + setPassword(event.target.value)} + required + /> +
+ {error &&
{error}
} +
+ + +
+
+
+ +
+ + )} +
+
+ ); +} diff --git a/src/components/BookingModal.tsx b/src/components/BookingModal.tsx new file mode 100644 index 0000000..23d68b4 --- /dev/null +++ b/src/components/BookingModal.tsx @@ -0,0 +1,53 @@ +import { FormEvent, useState } from 'react'; +import { useAuth } from '../hooks/useAuth'; +import { useBookingFlow } from '../hooks/useBookingFlow'; + +export function BookingModal() { + const { user } = useAuth(); + const { isBookingModalOpen, closeBookingModal } = useBookingFlow(); + const [details, setDetails] = useState(''); + const [isSubmitting, setSubmitting] = useState(false); + const [confirmation, setConfirmation] = useState(null); + + if (!isBookingModalOpen) { + return null; + } + + const handleSubmit = async (event: FormEvent) => { + event.preventDefault(); + setSubmitting(true); + await new Promise((resolve) => setTimeout(resolve, 400)); + setConfirmation('تم استلام حجزك وسيتم التواصل معك قريباً.'); + setSubmitting(false); + }; + + return ( +
+
+

نموذج الحجز

+

مرحباً {user?.name ?? 'بالعميل'}! أخبرنا بالمزيد عن طلبك.

+
+
+ +