// Duyuf marketing + browse website (desktop / responsive) // Headless web entry to the marketplace — hero search, featured // packages, top providers, how-it-works, become-a-provider CTA, footer. function WebNav({ theme, lang, onLanguageChange, dark, onThemeChange }) { const T = theme; const rtl = isRTL(lang); const ctx = useWeb(); const [menuOpen, setMenuOpen] = React.useState(false); const [languageOpen, setLanguageOpen] = React.useState(false); const [, refreshProvider] = React.useState(0); React.useEffect(() => { const update = () => refreshProvider((v) => v + 1); window.addEventListener("duyuf-provider-updated", update); return () => window.removeEventListener("duyuf-provider-updated", update); }, []); const isProvider = Boolean(localStorage.getItem("duyuf_provider_session")); const isCustomer = Boolean(localStorage.getItem("duyuf_customer") || firebase.auth().currentUser); const isAdminMode = String(ctx?.page || "").startsWith("admin-"); const isProviderPortal = String(ctx?.page || "").startsWith("provider-"); const storedProvider = (() => { try { return JSON.parse(localStorage.getItem("duyuf_provider") || "null"); } catch (_) { return null; } })(); const isAdminProvider = Boolean( storedProvider?.isAdmin || localStorage.getItem("duyuf_admin_session"), ); const visitorItems = [ { id: "home", ar: "الرئيسية", en: "Home" }, { id: "umrah", ar: "العمرة", en: "Umrah" }, { id: "hotels", ar: "الفنادق", en: "Hotels" }, { id: "flights", ar: "الطيران", en: "Flights" }, { id: "transport", ar: "المواصلات", en: "Transport" }, { id: "contact", ar: "تواصل", en: "Contact" }, { id: isCustomer?"customer-bookings":"customer-bookings", ar: isCustomer?"حجوزاتي":"تسجيل الدخول", en:isCustomer?"My bookings":"Sign in" }, ...(isProvider?[{id:"provider-switch",ar:"الانتقال إلى وضع المزود",en:"Switch to provider"}]:[]), ...(isAdminProvider||localStorage.getItem("duyuf_admin_session")?[{id:"admin-switch",ar:"الانتقال إلى وضع الأدمن",en:"Switch to admin"}]:[]), ]; const providerItems = [ { id: "provider-dashboard", ar: "لوحة التحكم", en: "Dashboard" }, { id: "provider-dashboard", ar: "إدارة الباقات", en: "Manage packages" }, { id: "provider-add-package", ar: "إضافة باقة", en: "Add package" }, { id: "provider-meeting-points", ar: "نقاط التجمع", en: "Meeting points" }, { id: "provider-profile", ar: "الملف التجاري", en: "Business profile" }, ...(isAdminProvider ? [ { id: "admin-switch", ar: "الانتقال إلى وضع الأدمن", en: "Switch to admin", }, ] : []), { id: "customer-switch", ar: "تجربة العميل", en: "Customer experience" }, { id: "home", ar: "تصفح الموقع الرئيسي", en: "Browse main website" }, { id: "signout", ar: "تسجيل الخروج", en: "Sign out" }, ]; const adminItems = [ { id: "admin-dashboard", ar: "لوحة تحكم الأدمن", en: "Admin dashboard" }, { id: "provider-switch", ar: "العودة إلى وضع المزود", en: "Return to provider mode", }, { id: "customer-switch", ar: "تجربة العميل", en: "Customer experience" }, ]; const items = isAdminMode ? adminItems : isProviderPortal ? providerItems : visitorItems; const switchToAdmin = async () => { setMenuOpen(false); localStorage.setItem("duyuf_dashboard_mode", "admin"); if (localStorage.getItem("duyuf_admin_session")) return ctx?.setPage("admin-dashboard"); try { const user = firebase.auth().currentUser; if (!user) return ctx?.setPage("admin-login"); const idToken = await user.getIdToken(true); const response = await fetch( `${window.DUYUF_API_URL}/v1/auth/firebase-admin`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ idToken }), }, ); const body = await response.json(); if (!response.ok) throw new Error(body.error || "admin_login_failed"); localStorage.setItem("duyuf_admin_session", body.sessionToken); localStorage.setItem("duyuf_admin", JSON.stringify(body.admin)); ctx?.setPage("admin-dashboard"); } catch (_) { ctx?.setPage("admin-login"); } }; const navigate = (page) => { if (page === "admin-dashboard") localStorage.setItem("duyuf_dashboard_mode", "admin"); if (page === "provider-dashboard") localStorage.setItem("duyuf_dashboard_mode", "provider"); if (page === "umrah") { ctx?.setPage("home"); setMenuOpen(false); window.setTimeout(() => document.getElementById("umrah-home-search")?.scrollIntoView({ behavior: "smooth", block: "center" }), 60); return; } if (page === "admin-switch") return switchToAdmin(); if (page === "provider-switch") { setMenuOpen(false); if (!localStorage.getItem("duyuf_provider_session")) return ctx?.setPage("signin"); localStorage.setItem("duyuf_dashboard_mode", "provider"); window.location.assign("/?page=provider-dashboard&mode=provider"); return; } if(page==="customer-switch"){ setMenuOpen(false); localStorage.setItem("duyuf_dashboard_mode","customer"); ctx?.setPage("customer-bookings"); return; } if (page === "signout") return logout(); ctx?.setPage(page); setMenuOpen(false); }; const logout = async () => { try { await firebase.auth().signOut(); } catch (_) {} localStorage.removeItem("duyuf_provider_session"); localStorage.removeItem("duyuf_provider"); localStorage.removeItem("duyuf_admin_session"); localStorage.removeItem("duyuf_admin"); localStorage.removeItem("duyuf_customer"); setMenuOpen(false); ctx?.setPage("home"); }; return ( <>
navigate( isAdminMode ? "admin-dashboard" : isProviderPortal || isProvider ? "provider-dashboard" : "home", ) } style={{ cursor: "pointer" }} >
{languageOpen && (
{[ ["ar", "العربية"], ["en", "English"], ].map(([code, label]) => ( ))}
)}
{menuOpen && (
{rtl ? "اللغة" : "Language"}
{rtl ? "المظهر" : "Appearance"}
); } function WebHero({ theme, lang }) { const T = theme; const rtl = isRTL(lang); const ctx = useWeb(); const search = ctx?.umrahSearch || {}; const [datesOpen, setDatesOpen] = React.useState(false); const dateAt = (offset) => { const d = new Date(); d.setDate(d.getDate()+offset); return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,"0")}-${String(d.getDate()).padStart(2,"0")}`; }; const tenDayPresets = [0,10,20].map((offset,index)=>({ start: dateAt(offset), end: dateAt(offset+9), label: rtl ? (index===0 ? "العشرة أيام القادمة" : `الفترة ${index+1} · 10 أيام`) : (index===0 ? "Next 10 days" : `Period ${index+1} · 10 days`), })); const updateSearch = (key, value) => ctx?.setUmrahSearch((current) => ({ ...current, [key]: value })); const applySearch = () => { const normalized = { ...search, destination: "مكة المكرمة", dateFrom: search.dateFrom || search.date || "", dateTo: search.dateTo || search.dateFrom || search.date || "", }; ctx?.setUmrahSearch(normalized); ctx?.setAppliedUmrahSearch(normalized); window.requestAnimationFrame(() => document.getElementById("home-packages")?.scrollIntoView({ behavior: "smooth", block: "start", }), ); }; const fieldStyle = { width: "100%", border: 0, outline: 0, background: "transparent", color: T.text, fontFamily: "Alexandria", fontWeight: 700, fontSize: 15, cursor: "pointer", }; return (
{rtl ? "مزوّدون معتمدون · حماية كاملة" : "Verified providers · Protected booking"}

{rtl ? "رحلتك إلى بيت الله، في خطوات ميسّرة." : "Your journey to the holy sites, made simple."}

{rtl ? "باقات عمرة كاملة، فنادق قرب الحرم، طيران ومواصلات — من أكثر من 68 مزوّداً موثوقاً." : "Complete Umrah packages, stays near the Haram, flights and transport — from 68+ verified providers."}

{[ { id: "home", icon: "kaaba", ar: "باقات العمرة", en: "Umrah packages" }, { id: "hotels", icon: "bed", ar: "الإقامة", en: "Stays" }, { id: "flights", icon: "plane", ar: "الطيران", en: "Flights" }, { id: "transport", icon: "bus", ar: "المواصلات", en: "Transport" }, ].map((service) => ( ))}
{/* Direct package filtering — no wizard */} {/* Trust stats */}
{[ { v: "68+", l: rtl ? "مزوّد معتمد" : "Verified providers" }, { v: "12K+", l: rtl ? "رحلة ميسّرة" : "Trips arranged" }, { v: "4.8★", l: rtl ? "تقييم المعتمرين" : "Customer rating" }, { v: rtl ? "3 لغات" : "3 langs", l: rtl ? "العربية · English · اردو" : "Arabic · English · Urdu", }, ].map((s, i) => (
{s.v}
{s.l}
))}
); } function WebServicePlaceholderPage({ theme, lang, service }) { const T = theme; const rtl = isRTL(lang); const copy = service === "flights" ? { ar: "الطيران", en: "Flights", icon: "plane" } : { ar: "المواصلات", en: "Transport", icon: "bus" }; return (

{rtl ? copy.ar : copy.en}

{rtl ? "سيتم إطلاق محرك البحث وربطه بمزوّد الخدمة قريبًا." : "The search engine and provider integration will be available soon."}

); } function WebPackages({ theme, lang }) { const T = theme; const rtl = isRTL(lang); const ctx = useWeb(); const basePackages = [ { hotel: rtl ? "فيرمونت ساعة مكة" : "Fairmont Makkah", dates: rtl ? "16–25 رمضان" : "16–25 Ramadan", nights: 7, transport: "flight", price: 4899, seats: 4, tag: rtl ? "الأكثر طلباً" : "Most booked", tone: "dark", provider: rtl ? "الإيلاف الفاخرة" : "Elaf Premium", originCity: "الرياض", destinationCity: "مكة المكرمة", }, { hotel: rtl ? "سويس أوتيل الزمزم" : "Swissôtel Al Maqam", dates: rtl ? "1–8 شوال" : "1–8 Shawwal", nights: 7, transport: "flight", price: 3450, seats: 12, tone: "brown", provider: rtl ? "الرحمة للحج" : "Al-Rahma", originCity: "جدة", destinationCity: "مكة المكرمة", }, { hotel: rtl ? "فندق ميلينيوم" : "Millennium Makkah", dates: rtl ? "20 رمضان" : "20 Ramadan", nights: 14, transport: "bus", price: 1899, seats: 3, tag: rtl ? "حصري" : "Exclusive", tone: "camel", provider: rtl ? "دار البركة" : "Dar Albaraka", originCity: "المدينة المنورة", destinationCity: "مكة المكرمة", }, { hotel: rtl ? "هيلتون مكة" : "Hilton Makkah", dates: rtl ? "12 شوال" : "12 Shawwal", nights: 5, transport: "flight", price: 2799, seats: 8, tone: "cream", provider: rtl ? "إيلاف ضيوف" : "Elaf Guests", originCity: "الدمام", destinationCity: "مكة المكرمة", }, ]; const [approved, setApproved] = React.useState([]); React.useEffect(() => { fetch(`${window.DUYUF_API_URL}/v1/public/packages`) .then((r) => r.json()) .then((body) => { if (!body.ok) return; setApproved( (body.packages || []).map((p, i) => ({ id: p.id, hotel: p.hotel_name, dates: `${p.available_from} — ${p.available_to}`, nights: Math.max(1, Number(p.duration_days) - 1), transport: p.travel_mode || "bus", price: Number(p.price_per_person), seats: p.seats, tag: rtl ? "جديد" : "New", tone: ["camel", "brown", "cream", "dark"][i % 4], provider: p.business_name, providerLogoUrl: p.provider_logo_key ? `${window.DUYUF_API_URL}/v1/public/package-images/${encodeURIComponent(p.provider_logo_key)}` : "", title: p.title, imageUrl: p.cover_key ? `${window.DUYUF_API_URL}/v1/public/package-images/${encodeURIComponent(p.cover_key)}` : JSON.parse(p.hotel_images_json || "[]")[0] || "", inclusions: JSON.parse(p.inclusions || "[]"), exclusions: JSON.parse(p.exclusions || "[]"), notes: p.notes || "", occupancyPrices: JSON.parse(p.occupancy_prices_json || "{}"), cancellationPolicy: p.cancellation_policy || "", supportsTabby: Boolean(p.supports_tabby), supportsTamara: Boolean(p.supports_tamara), isDynamic: true, travelMode: p.travel_mode, originCity: p.origin_city, destinationCity: p.destination_city, departureAt: p.departure_at, returnAt: p.return_at, availableFrom: p.available_from, availableTo: p.available_to, busType: p.bus_type, airline: p.airline, flightNumber: p.flight_number, flightArrivalCity: p.flight_arrival_city, airportTransferType: p.airport_transfer_type, airportTransferAt: p.airport_transfer_at, returnOriginCity: p.return_origin_city, returnDestinationCity: p.return_destination_city, returnDepartureAt: p.return_departure_at, returnAirline: p.return_airline, returnFlightNumber: p.return_flight_number, roomType: p.room_type, mealPlan: p.meal_plan, meetingPoint: p.meeting_point, meetingAt: p.meeting_at, boardingAt: p.boarding_at, arrivalAt: p.arrival_at, hotelDepartureAt: p.hotel_departure_at, returnArrivalAt: p.return_arrival_at, itineraryNotes: p.itinerary_notes, })), ); }) .catch(() => {}); }, [lang]); const activeSearch = ctx?.appliedUmrahSearch; const packages = approved .filter((p) => { if (!activeSearch) return true; if (activeSearch.origin && p.originCity !== activeSearch.origin) return false; if (activeSearch.travelMode && activeSearch.travelMode !== "all" && (p.travelMode || p.transport) !== activeSearch.travelMode) return false; if (activeSearch.guests && Number(p.seats || 0) < Number(activeSearch.guests)) return false; if (p.destinationCity && p.destinationCity !== "مكة المكرمة") return false; const tripStart = String(p.departureAt || p.availableFrom || "").slice(0, 10); const tripEnd = String(p.returnAt || p.availableTo || tripStart).slice(0, 10); if (activeSearch.dateFrom && tripEnd && tripEnd < activeSearch.dateFrom) return false; if (activeSearch.dateTo && tripStart && tripStart > activeSearch.dateTo) return false; return true; }) .slice(0, 8); return (
{rtl ? "باقات مختارة" : "Featured packages"}

{rtl ? "أحدث باقات العمرة من مزوّدين موثوقين" : "Latest Umrah packages from verified providers"}

ctx?.setPage("browse")} style={{ fontFamily: "Alexandria", fontSize: 13, color: T.primary, fontWeight: 600, cursor: "pointer", }} > {rtl ? "كل الباقات →" : "Browse all →"}
{packages.length === 0 && (
{rtl ? "لا توجد باقات مطابقة لمدينة القدوم والفترة المحددة حالياً. جرّب فترة أخرى." : "No packages currently match the selected departure city and dates. Try another period."}
)} {packages.map((p, i) => (
{ ctx?.setPkg(p); ctx?.setPage("detail"); }} style={{ background: T.surface, borderRadius: 18, border: `1px solid ${T.hairline}`, overflow: "hidden", boxShadow: T.cardShadow, cursor: "pointer", }} >
{p.imageUrl ? ( {p.hotel} ) : ( )} {p.tag && (
{p.tag}
)}
{p.hotel}
4.7
{p.dates}
{p.nights} {rtl ? "ليالٍ" : "nights"}
{p.originCity || (rtl ? "مدينة القدوم غير محددة" : "Departure city not specified")} {" → "} {p.destinationCity || (rtl ? "مكة المكرمة" : "Makkah")}
{rtl ? "يبدأ من" : "from"}
{p.price.toLocaleString()}{" "} SAR
))}
); } function WebHowItWorks({ theme, lang }) { const T = theme; const rtl = isRTL(lang); const steps = [ { n: "01", i: "search", t: rtl ? "اختر باقتك" : "Pick your package", d: rtl ? "تصفّح آلاف الباقات من مزوّدين موثوقين" : "Browse packages from verified providers", }, { n: "02", i: "card", t: rtl ? "احجز وادفع بأمان" : "Book securely", d: rtl ? "دفع آمن بـ Apple Pay، مدى، فيزا، STC Pay وغيرها" : "Apple Pay, mada, Visa, STC Pay & more", }, { n: "03", i: "qr", t: rtl ? "استلم رمز الحجز" : "Get your QR", d: rtl ? "تأكيد فوري ورمز QR للسفر" : "Instant confirmation with travel QR", }, { n: "04", i: "kaaba", t: rtl ? "سافر بسلام" : "Travel with ease", d: rtl ? "تنبيهات وإرشادات أثناء الرحلة" : "In-trip alerts and on-site guides", }, ]; return (
{rtl ? "كيف يعمل" : "How it works"}

{rtl ? "من البحث إلى الحرم في 4 خطوات" : "From search to the Haram in 4 steps"}

{steps.map((s, i) => (
{s.n}
{s.t}
{s.d}
))}
); } function WebProviders({ theme, lang }) { const T = theme; const rtl = isRTL(lang); const providers = [ rtl ? "الإيلاف الفاخرة" : "Elaf Premium", rtl ? "الرحمة للحج" : "Al-Rahma Hajj", rtl ? "إيلاف ضيوف" : "Elaf Guests", rtl ? "دار الراحة" : "Dar Al-Raha", rtl ? "بوابة الكعبة" : "Kaaba Gate", rtl ? "حجاج الجزيرة" : "Hujjaj Aljazeera", rtl ? "نسائم الرحمن" : "Nasaim Al-Rahman", rtl ? "دار البركة" : "Dar Albaraka", ]; return (
{rtl ? "موثوق من قبل أكثر من 68 مزوّداً" : "Trusted by 68+ verified providers"}
{providers.map((p, i) => (
))}
); } function WebBecomeProvider({ theme, lang }) { const T = theme; const rtl = isRTL(lang); const ctx = useWeb(); return (
{rtl ? "انضم إلى السوق" : "Join the marketplace"}

{rtl ? "وفّر باقاتك لآلاف المعتمرين عبر منصة ضيوف" : "Offer your packages to thousands of pilgrims via Duyuf"}

{rtl ? "لوحة إدارة كاملة لتكوين الباقات، إدارة الحجوزات، وتطبيق تشغيل لمتابعة العملاء أثناء الرحلة." : "A full dashboard to create packages, manage bookings, plus an operations app to track travelers during their trip."}

{[ { i: "trend-up", t: rtl ? "وصول لـ 12K+ معتمر شهرياً" : "12K+ pilgrims/month", }, { i: "percent", t: rtl ? "عمولة شفافة من لوحة الإدارة" : "Transparent commission", }, { i: "shield", t: rtl ? "دفعات آمنة ومجدولة" : "Secure scheduled payouts", }, { i: "bell", t: rtl ? "تطبيق تشغيل وتنبيهات للعملاء" : "Ops app + traveler alerts", }, ].map((f, i) => (
{f.t}
))}
); } function WebAppDownload({ theme, lang }) { const T = theme; const rtl = isRTL(lang); return (
{rtl ? "حمّل التطبيق" : "Get the app"}

{rtl ? "كل رحلتك في تطبيق واحد" : "Your whole journey in one app"}

{rtl ? "احجز، ادفع، تتبّع رحلتك، واستلم تنبيهات أثناء السفر — بالعربية، الإنجليزية، أو الأردية." : "Book, pay, track your trip, and receive on-trip alerts — in Arabic, English, or Urdu."}

{[ { t: rtl ? "تنزيل من" : "Download on", store: "App Store", icon: "", }, { t: rtl ? "احصل عليه من" : "Get it on", store: "Google Play", icon: "▶", }, ].map((s, i) => (
{s.icon}
{s.t}
{s.store}
))}
4.8 · 4,200+ {rtl ? "تقييم" : "ratings"}
{/* Phone mockup */}
); } function WebFAQ({ theme, lang }) { const T = theme; const rtl = isRTL(lang); const faqs = rtl ? [ { q: "هل يمكنني الحجز كزائر دون تسجيل؟", a: "يمكنك تصفّح كل الباقات بدون تسجيل، لكن إتمام الحجز يتطلب التحقق برقم الهاتف عبر رمز SMS.", }, { q: "كيف تختار المزوّدين الموجودين على المنصة؟", a: 'كل المزوّدين يمرّون بعملية توثيق KYC قبل ظهور باقاتهم. تظهر شارة "موثّق" على الباقات.', open: true, }, { q: "ما هي سياسة الإلغاء والاسترداد؟", a: "استرداد كامل قبل 7 أيام من السفر، وخصم 50% خلال 7 أيام. الاسترداد يصل لمحفظتك خلال 3 أيام عمل.", }, { q: "هل أحتاج لاختيار مقعد للعمرة بالطائرة؟", a: "لا، باقات العمرة (بالطائرة أو الحافلة) مجدولة مسبقاً ولا تتطلب اختيار مقعد. اختيار المقاعد متاح فقط في حجوزات الطيران المستقلة من أيقونة الطيران.", }, { q: "بأي وسائل دفع يمكنني الدفع؟", a: "Apple Pay، مدى، فيزا/ماستركارد، STC Pay، تابي وتمارا للأقساط، أو رصيد محفظة ضيوف.", }, { q: "كيف أصبح مزوّداً على المنصة؟", a: 'سجّل عبر "انضم كمزوّد"، أرسل وثائقك (سجل تجاري + ترخيص هيئة السياحة)، وبعد الموافقة ستحصل على لوحة إدارة وتطبيق التشغيل.', }, ] : [ { q: "Can I book as a guest without an account?", a: "You can browse all packages without signing up, but completing a booking requires phone verification via SMS code.", }, { q: "How are providers verified on Duyuf?", a: 'All providers go through KYC verification before their packages appear. Look for the "Verified" badge on package listings.', open: true, }, { q: "What is the cancellation policy?", a: "Full refund up to 7 days before travel, 50% within 7 days. Refunds land in your Duyuf wallet within 3 business days.", }, { q: "Do I select a seat for Umrah by Flight?", a: "No. Umrah packages (flight or bus) are pre-arranged and do not require seat selection. Seat selection is only available for standalone flight bookings from the Flights icon.", }, { q: "Which payment methods are supported?", a: "Apple Pay, mada, Visa/Mastercard, STC Pay, Tabby and Tamara installments, or your Duyuf wallet balance.", }, { q: "How do I become a provider?", a: 'Apply via "Become a provider", submit your documents (commercial registration + tourism license), and once approved you get a dashboard and operations app.', }, ]; return (
{rtl ? "الأسئلة الشائعة" : "Frequently asked"}

{rtl ? "كل ما تحتاج معرفته قبل حجز رحلتك" : "Everything you need to know before booking"}

{rtl ? "لم تجد إجابتك؟ تواصل مع فريق الدعم على واتساب وسنردّ خلال دقائق." : "Didn't find your answer? Reach our support team on WhatsApp and we'll reply in minutes."}

{faqs.map((f, i) => (
{f.q}
{f.open && (
{f.a}
)}
))}
); } function WebFooter({ theme, lang }) { const T = theme; const rtl = isRTL(lang); const cols = [ { h: rtl ? "الخدمات" : "Services", items: rtl ? ["عمرة بالطائرة", "عمرة بالحافلة", "فنادق", "طيران", "مواصلات"] : ["Umrah by flight", "Umrah by bus", "Hotels", "Flights", "Transport"], }, { h: rtl ? "الشركة" : "Company", items: rtl ? ["عن ضيوف", "قصتنا", "مدونة", "وظائف", "الصحافة"] : ["About", "Story", "Blog", "Careers", "Press"], }, { h: rtl ? "المزوّدون" : "For providers", items: rtl ? [ "انضم كمزوّد", "لوحة الإدارة", "تطبيق التشغيل", "الأسعار والعمولات", "الموارد", ] : [ "Become a provider", "Provider dashboard", "Ops app", "Pricing & commission", "Resources", ], }, { h: rtl ? "الدعم" : "Support", items: rtl ? ["مركز المساعدة", "اتصل بنا", "الشروط", "الخصوصية", "إعادة الجدولة"] : ["Help center", "Contact", "Terms", "Privacy", "Reschedules"], }, ]; return (

{rtl ? "منصة سعودية تجمع ضيوف الرحمن مع أفضل مزوّدي خدمات العمرة." : "A Saudi marketplace connecting pilgrims with the best Umrah service providers."}

{["phone", "mail", "globe"].map((i) => (
))}
{cols.map((c) => (
{c.h}
{c.items.map((it) => ( {it} ))}
))}
© 2026 Duyuf · {rtl ? "كل الحقوق محفوظة" : "All rights reserved"}
{[ { t: rtl ? "حمّل من" : "Download", store: "App Store", icon: "" }, { t: rtl ? "متوفر على" : "Get on", store: "Google Play", icon: "▶", }, ].map((s, i) => (
{s.icon}
{s.t}
{s.store}
))}
{rtl ? "المملكة العربية السعودية · SA" : "Saudi Arabia · SA"}
); } // ─── Home page (the original landing) ───────────────────────────────── function WebHomePage({ theme, lang }) { const T = theme; return ( <> ); } // ─── Web App — page router ──────────────────────────────────────────── class WebPageErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { failed: false }; } static getDerivedStateFromError() { return { failed: true }; } componentDidCatch(error) { console.error("Duyuf page error", error); } componentDidUpdate(prev) { if (prev.page !== this.props.page && this.state.failed) this.setState({ failed: false }); } render() { if (!this.state.failed) return this.props.children; const rtl = isRTL(this.props.lang), T = this.props.theme; return (

{rtl ? "تعذر فتح الصفحة" : "Could not open this page"}

{rtl ? "حدّث الصفحة وحاول مرة أخرى. إذا استمرت المشكلة ارجع إلى لوحة التحكم." : "Refresh and try again, or return to the dashboard."}

); } } function WebApp({ theme, lang, onLanguageChange, dark = false, onThemeChange, }) { const [page, setPage] = React.useState(() => { const requested = new URLSearchParams(window.location.search).get("page"); const publicPages = [ "home", "browse", "detail", "checkout", "hotels", "hotel-results", "hotel-details", "hotel-checkout", "flights", "transport", "become-provider", "contact", "signin", "customer-bookings", "booking-confirmation", ]; if ( requested === "provider-dashboard" && localStorage.getItem("duyuf_provider_session") ) { localStorage.setItem("duyuf_dashboard_mode", "provider"); return "provider-dashboard"; } if (requested === "payment-result") return "payment-result"; if (requested === "admin-login") return localStorage.getItem("duyuf_admin_session") ? "admin-dashboard" : "admin-login"; if (requested === "admin-dashboard") { localStorage.setItem("duyuf_dashboard_mode", "admin"); return localStorage.getItem("duyuf_admin_session") ? "admin-dashboard" : "admin-login"; } const dashboardMode = localStorage.getItem("duyuf_dashboard_mode"); if (!requested && dashboardMode === "customer") return "customer-bookings"; if (!requested && dashboardMode === "provider" && localStorage.getItem("duyuf_provider_session")) return "provider-dashboard"; if (!requested && dashboardMode === "admin" && localStorage.getItem("duyuf_admin_session")) return "admin-dashboard"; if (!requested && localStorage.getItem("duyuf_admin_session")) return "admin-dashboard"; if (!requested && localStorage.getItem("duyuf_provider_session")) return "provider-dashboard"; if (publicPages.includes(requested)) return requested; return "home"; }); const [pkg, setPkg] = React.useState(null); const [bookingConfirmation, setBookingConfirmation] = React.useState(() => { try { return JSON.parse(localStorage.getItem("duyuf_last_booking") || "null"); } catch (_) { return null; } }); const [hotel, setHotel] = React.useState({ query: null, results: [] }); const [umrahSearch, setUmrahSearch] = React.useState(() => { try { const saved = JSON.parse(sessionStorage.getItem("duyuf_umrah_search")); if (saved?.dateFrom && saved?.dateTo) return saved; const d=(offset)=>{const x=new Date();x.setDate(x.getDate()+offset);return `${x.getFullYear()}-${String(x.getMonth()+1).padStart(2,"0")}-${String(x.getDate()).padStart(2,"0")}`;}; return { ...(saved||{}), origin: saved?.origin||"جدة", destination: "مكة المكرمة", dateFrom:d(0), dateTo:d(9), travelMode:saved?.travelMode||"all", guests:saved?.guests||2 }; } catch (_) { const d=(offset)=>{const x=new Date();x.setDate(x.getDate()+offset);return `${x.getFullYear()}-${String(x.getMonth()+1).padStart(2,"0")}-${String(x.getDate()).padStart(2,"0")}`;}; return { origin: "جدة", destination: "مكة المكرمة", dateFrom:d(0), dateTo:d(9), travelMode: "all", guests: 2 }; } }); const [appliedUmrahSearch, setAppliedUmrahSearch] = React.useState(null); React.useEffect(() => { if (page === "admin-dashboard") localStorage.setItem("duyuf_dashboard_mode", "admin"); if (page === "provider-dashboard") localStorage.setItem("duyuf_dashboard_mode", "provider"); if (page === "customer-bookings") localStorage.setItem("duyuf_dashboard_mode", "customer"); sessionStorage.setItem("duyuf_umrah_search", JSON.stringify(umrahSearch)); }, [umrahSearch]); const ctx = React.useMemo( () => ({ page, setPage, pkg, setPkg, bookingConfirmation, setBookingConfirmation, hotel, setHotel, umrahSearch, setUmrahSearch, appliedUmrahSearch, setAppliedUmrahSearch }), [page, pkg, bookingConfirmation, hotel, umrahSearch, appliedUmrahSearch], ); const T = theme; const rtl = isRTL(lang); // Scroll to top on page change const scrollRef = React.useRef(null); React.useEffect(() => { if (scrollRef.current) scrollRef.current.scrollTop = 0; window.scrollTo(0, 0); }, [page]); const Page = { home: WebHomePage, browse: WebBrowsePage, detail: WebDetailPage, checkout: WebCheckoutPage, "booking-confirmation": WebBookingConfirmationPage, hotels: WebHotelsSearchPage, flights: (props) => , transport: (props) => , "hotel-results": WebHotelResultsPage, "hotel-details": WebHotelDetailsPage, "hotel-checkout": WebHotelCheckoutPage, "payment-result": WebPaymentResultPage, "become-provider": WebProviderSignupPage, contact: WebContactPage, signin: WebSignInPage, "customer-bookings": WebCustomerBookingsPage, "provider-dashboard": WebProviderDashboardPage, "provider-add-package": WebProviderAddPackagePage, "provider-meeting-points": WebProviderMeetingPointsPage, "provider-profile": WebProviderBusinessProfilePage, "provider-edit-package": WebProviderEditPackagePage, "provider-package-bookings": WebProviderBookingsPage, "admin-login": WebAdminLoginPage, "admin-dashboard": WebAdminDashboardPage, }[page] || WebHomePage; return (
{["flights","transport"].includes(page) &&
} setPage( String(page).startsWith("admin-") && localStorage.getItem("duyuf_admin_session") ? "admin-dashboard" : localStorage.getItem("duyuf_provider_session") ? "provider-dashboard" : "home", ) } >
); } // ─── Wrapped in a browser window for the canvas ────────────────────── function WebsiteArtboard({ theme, lang }) { return ( ); } Object.assign(window, { WebNav, WebHero, WebPackages, WebHowItWorks, WebProviders, WebBecomeProvider, WebAppDownload, WebFAQ, WebFooter, WebHomePage, WebApp, WebsiteArtboard, });