Release landing page updates
هذا الالتزام موجود في:
4
.dockerignore
Normal file
4
.dockerignore
Normal file
@@ -0,0 +1,4 @@
|
||||
.git
|
||||
.gitignore
|
||||
landing-page-deploy.zip
|
||||
|
||||
18
.ghaymah.example.json
Normal file
18
.ghaymah.example.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "tabeley-landing",
|
||||
"ports": [
|
||||
{
|
||||
"expose": true,
|
||||
"number": 80
|
||||
}
|
||||
],
|
||||
"publicAccess": {
|
||||
"enabled": true,
|
||||
"domain": "auto"
|
||||
},
|
||||
"resourceTier": "t1",
|
||||
"dockerFileName": "Dockerfile",
|
||||
"env": {
|
||||
"TABELEY_API_BASE_URL": "https://your-backend-domain.example.com/api"
|
||||
}
|
||||
}
|
||||
18
.ghaymah.json
Normal file
18
.ghaymah.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "tabeley-landing",
|
||||
"ports": [
|
||||
{
|
||||
"expose": true,
|
||||
"number": 80
|
||||
}
|
||||
],
|
||||
"publicAccess": {
|
||||
"enabled": true,
|
||||
"domain": "auto"
|
||||
},
|
||||
"resourceTier": "t1",
|
||||
"dockerFileName": "Dockerfile",
|
||||
"env": {
|
||||
"TABELEY_API_BASE_URL": "https://your-backend-domain.example.com/api"
|
||||
}
|
||||
}
|
||||
8
Dockerfile
Normal file
8
Dockerfile
Normal file
@@ -0,0 +1,8 @@
|
||||
FROM nginx:1.27-alpine
|
||||
|
||||
# Serve the static landing page through Nginx on port 80.
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY . /usr/share/nginx/html
|
||||
COPY docker-entrypoint.d/40-render-config.sh /docker-entrypoint.d/40-render-config.sh
|
||||
|
||||
EXPOSE 80
|
||||
ثنائية
assets/images/tlogo.jpg
Normal file
ثنائية
assets/images/tlogo.jpg
Normal file
ملف ثنائي غير معروض.
|
بعد العرض: | الارتفاع: | الحجم: 68 KiB |
ثنائية
assets/images/tlogo.webp
Normal file
ثنائية
assets/images/tlogo.webp
Normal file
ملف ثنائي غير معروض.
|
بعد العرض: | الارتفاع: | الحجم: 4.7 KiB |
ثنائية
assets/images/tlogoapp.png
Normal file
ثنائية
assets/images/tlogoapp.png
Normal file
ملف ثنائي غير معروض.
|
بعد العرض: | الارتفاع: | الحجم: 1.4 MiB |
ثنائية
assets/images/tlogoapp.webp
Normal file
ثنائية
assets/images/tlogoapp.webp
Normal file
ملف ثنائي غير معروض.
|
بعد العرض: | الارتفاع: | الحجم: 5.5 KiB |
1
config.js.template
Normal file
1
config.js.template
Normal file
@@ -0,0 +1 @@
|
||||
window.TABELEY_API_BASE_URL = "${TABELEY_API_BASE_URL}";
|
||||
6
docker-entrypoint.d/40-render-config.sh
Normal file
6
docker-entrypoint.d/40-render-config.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
envsubst '${TABELEY_API_BASE_URL}' \
|
||||
< /usr/share/nginx/html/config.js.template \
|
||||
> /usr/share/nginx/html/config.js
|
||||
389
index.html
Normal file
389
index.html
Normal file
@@ -0,0 +1,389 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ar" dir="rtl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Tabeley | احجز طاولتك خلال ثوانٍ</title>
|
||||
<meta name="description" content="Tabeley تطبيق عربي حديث لحجز طاولات المطاعم والمقاهي بسرعة، دون انتظار، مع تأكيد فوري وتجربة حجز منظمة وسهلة.">
|
||||
<meta property="og:title" content="Tabeley | احجز طاولتك خلال ثوانٍ">
|
||||
<meta property="og:description" content="منصة ذكية لحجز المطاعم والمقاهي بتجربة عربية حديثة، تأكيد فوري، وخطوات واضحة من اختيار المطعم حتى تأكيد الحجز.">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:image" content="/assets/images/tlogoapp.webp">
|
||||
<meta property="og:locale" content="ar_AR">
|
||||
<link rel="icon" href="/assets/images/tlogoapp.webp" type="image/webp">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<div class="container header-shell">
|
||||
<a href="#top" class="brand">
|
||||
<img src="assets/images/tlogoapp.webp" alt="شعار Tabeley" class="brand-logo">
|
||||
<div class="brand-copy">
|
||||
<strong class="brand-name">Tabeley</strong>
|
||||
<small>Dining Reservation Platform</small>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<button class="menu-toggle" type="button" aria-expanded="false" aria-controls="site-menu" aria-label="فتح القائمة">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</button>
|
||||
|
||||
<nav class="nav" id="site-menu">
|
||||
<a href="#features">المزايا</a>
|
||||
<a href="#screenshots">لقطات التطبيق</a>
|
||||
<a href="#how-it-works">كيف يعمل</a>
|
||||
<a href="#faq">الأسئلة الشائعة</a>
|
||||
<a href="#contact">تواصل معنا</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="top">
|
||||
<section class="hero section">
|
||||
<div class="container hero-grid">
|
||||
<div class="hero-copy reveal">
|
||||
<span class="eyebrow">منصة حجز ذكية للمطاعم والمقاهي</span>
|
||||
<h1>اختصر وقتك واحجز طاولتك خلال ثوانٍ</h1>
|
||||
<p class="hero-text">
|
||||
يوفّر Tabeley نظام حجز ذكي وسريع يمنحك تجربة منظمة وواضحة من أول اختيار المطعم حتى التأكيد الفوري،
|
||||
دون انتظار أو اتصالات متكررة، لتستمتع بتجربة تناول أكثر راحة واحترافية.
|
||||
</p>
|
||||
<div class="hero-actions">
|
||||
<a href="/files/tabeley.apk" class="btn btn-primary" download>حمّل التطبيق الآن</a>
|
||||
<button class="btn btn-secondary" disabled>قريباً على iPhone</button>
|
||||
</div>
|
||||
<p class="download-note">تنزيل مباشر لنسخة Android APK.</p>
|
||||
</div>
|
||||
|
||||
<div class="hero-visual reveal reveal-delay-1" aria-hidden="true">
|
||||
<div class="phone-stack">
|
||||
<article class="phone-card is-main">
|
||||
<div class="phone-notch"></div>
|
||||
<div class="screen-content app-home-screen">
|
||||
<div class="app-screen-bar">
|
||||
<strong>Nearby</strong>
|
||||
<span class="mini-pill">Tabeley</span>
|
||||
</div>
|
||||
<div class="search-shell">ابحث عن مطعم أو مقهى</div>
|
||||
<div class="offer-banner">
|
||||
<span>خصم اليوم</span>
|
||||
<strong>حتى 20% على الحجوزات المبكرة</strong>
|
||||
</div>
|
||||
<div class="venue-card-ui">
|
||||
<div class="venue-card-overlay">
|
||||
<div class="venue-card-top">
|
||||
<span class="venue-icon-ui">🍽</span>
|
||||
<span class="type-badge-ui">Restaurant</span>
|
||||
</div>
|
||||
<div class="venue-card-bottom">
|
||||
<strong>مطعم </strong>
|
||||
<span>وسط المدينة</span>
|
||||
<div class="price-chip-ui">$12.00 / person</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="screens section section-soft" id="screenshots">
|
||||
<div class="container">
|
||||
<div class="section-head reveal">
|
||||
<span class="eyebrow">لقطات من التطبيق</span>
|
||||
|
||||
<p>تصميم مبسّط يساعد المستخدم على الانتقال من اختيار المطعم حتى تأكيد الحجز بثقة ووضوح.</p>
|
||||
</div>
|
||||
<div class="screens-grid">
|
||||
<article class="mockup reveal">
|
||||
<div class="mockup-frame">
|
||||
<div class="mockup-notch"></div>
|
||||
<div class="mockup-screen app-screen app-screen-home">
|
||||
<div class="app-screen-bar">
|
||||
<strong>Nearby</strong>
|
||||
<span class="mini-pill">3 مطاعم</span>
|
||||
</div>
|
||||
<div class="search-shell">Search restaurants</div>
|
||||
<div class="offer-banner small">
|
||||
<span>Offers</span>
|
||||
<strong>خصم 15% بين 5 و7 مساءً</strong>
|
||||
</div>
|
||||
<div class="venue-card-ui compact">
|
||||
<div class="venue-card-overlay">
|
||||
<div class="venue-card-top">
|
||||
<span class="venue-icon-ui">🍽</span>
|
||||
<span class="type-badge-ui">Cafe</span>
|
||||
</div>
|
||||
<div class="venue-card-bottom">
|
||||
<strong> مطعم 2 </strong>
|
||||
<span>قهوة ومشاوي</span>
|
||||
<div class="price-chip-ui">$9.00 / person</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
<article class="mockup reveal reveal-delay-1">
|
||||
<div class="mockup-frame">
|
||||
<div class="mockup-notch"></div>
|
||||
<div class="mockup-screen app-screen app-screen-book">
|
||||
<div class="app-screen-bar">
|
||||
<strong>Book Table</strong>
|
||||
<span class="mini-pill">Venue #12</span>
|
||||
</div>
|
||||
<div class="field-card-ui">
|
||||
<span>التاريخ</span>
|
||||
<strong>24 / 03 / 2026</strong>
|
||||
</div>
|
||||
<div class="time-slots-ui">
|
||||
<span class="slot-ui">6:00 PM</span>
|
||||
<span class="slot-ui active">8:30 PM</span>
|
||||
<span class="slot-ui">9:30 PM</span>
|
||||
</div>
|
||||
<div class="field-card-ui">
|
||||
<span>Guests</span>
|
||||
<strong>4 أشخاص</strong>
|
||||
</div>
|
||||
<div class="summary-card-ui">
|
||||
<strong>Reservation Summary</strong>
|
||||
<div><span>Date</span><span>24/03/2026</span></div>
|
||||
<div><span>Time</span><span>8:30 PM</span></div>
|
||||
<div><span>Guests</span><span>4</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
<article class="mockup reveal reveal-delay-2">
|
||||
<div class="mockup-frame">
|
||||
<div class="mockup-notch"></div>
|
||||
<div class="mockup-screen app-screen app-screen-bookings">
|
||||
<div class="app-screen-bar">
|
||||
<strong>My Bookings</strong>
|
||||
<span class="mini-pill">2 حجوزات</span>
|
||||
</div>
|
||||
<div class="booking-card-ui">
|
||||
<strong>مطعم </strong>
|
||||
<span>24/03/2026 • 8:30 PM</span>
|
||||
<span>Guests: 4</span>
|
||||
<span class="status-pill approved">APPROVED</span>
|
||||
<div class="timeline-ui">
|
||||
<span class="dot done"></span>
|
||||
<span class="line done"></span>
|
||||
<span class="dot done"></span>
|
||||
<span class="line"></span>
|
||||
<span class="dot"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="booking-card-ui secondary">
|
||||
<strong>قهوة المدينة</strong>
|
||||
<span>27/03/2026 • 6:00 PM</span>
|
||||
<span>Guests: 2</span>
|
||||
<span class="status-pill pending">PENDING</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="features section" id="features">
|
||||
<div class="container">
|
||||
<div class="section-head reveal">
|
||||
<span class="eyebrow">مزايا التطبيق</span>
|
||||
<h2>كل ما تحتاجه لحجز أسرع وأكثر تنظيمًا</h2>
|
||||
</div>
|
||||
<div class="features-grid">
|
||||
<article class="feature-card reveal">
|
||||
<span class="feature-icon">☎</span>
|
||||
<h3>حجز فوري بدون اتصال</h3>
|
||||
<p>أكّد حجزك مباشرة من التطبيق دون الحاجة إلى المكالمات أو الانتظار.</p>
|
||||
</article>
|
||||
<article class="feature-card reveal reveal-delay-1">
|
||||
<span class="feature-icon">%</span>
|
||||
<h3>عروض وخصومات حسب الأوقات</h3>
|
||||
<p>اكتشف مزايا إضافية مرتبطة بأوقات محددة وتجارب حجز أكثر قيمة.</p>
|
||||
</article>
|
||||
<article class="feature-card reveal reveal-delay-2">
|
||||
<span class="feature-icon">⌂</span>
|
||||
<h3>تنوع كبير في المطاعم والمقاهي</h3>
|
||||
<p>خيارات متعددة لتناسب الأذواق المختلفة والمناسبات اليومية والخاصة.</p>
|
||||
</article>
|
||||
<article class="feature-card reveal">
|
||||
<span class="feature-icon">▣</span>
|
||||
<h3>إدارة حجوزاتك بسهولة</h3>
|
||||
<p>راجع مواعيدك، وعدّل تفاصيل الحجز أو ألغِه من مكان واحد منظم.</p>
|
||||
</article>
|
||||
<article class="feature-card reveal reveal-delay-1">
|
||||
<span class="feature-icon">⏰</span>
|
||||
<h3>تنبيهات وتذكير قبل الموعد</h3>
|
||||
<p>ابقَ على اطلاع بموعدك القادم عبر تذكيرات مريحة قبل وقت الحجز.</p>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="how section section-soft" id="how-it-works">
|
||||
<div class="container">
|
||||
<div class="section-head reveal">
|
||||
<span class="eyebrow">كيف يعمل</span>
|
||||
<h2>خطوات بسيطة من البحث حتى الجلوس على الطاولة</h2>
|
||||
</div>
|
||||
<div class="timeline">
|
||||
<article class="timeline-step reveal">
|
||||
<span class="timeline-icon">1</span>
|
||||
<h3>اختر المطعم</h3>
|
||||
<p>تصفّح الخيارات المتاحة واختر المكان الذي يناسب ذوقك وموقعك.</p>
|
||||
</article>
|
||||
<article class="timeline-step reveal reveal-delay-1">
|
||||
<span class="timeline-icon">2</span>
|
||||
<h3>حدّد الموعد</h3>
|
||||
<p>اختر التاريخ والوقت وعدد الأشخاص ضمن واجهة واضحة وسريعة.</p>
|
||||
</article>
|
||||
<article class="timeline-step reveal reveal-delay-2">
|
||||
<span class="timeline-icon">3</span>
|
||||
<h3>أكّد الحجز</h3>
|
||||
<p>راجع تفاصيلك واحصل على تأكيد فوري دون تعقيد.</p>
|
||||
</article>
|
||||
<article class="timeline-step reveal reveal-delay-3">
|
||||
<span class="timeline-icon">4</span>
|
||||
<h3>استمتع بتجربتك</h3>
|
||||
<p>اذهب في الموعد المحدد واستفد من تجربة أكثر تنظيمًا وراحة.</p>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<!--
|
||||
<section class="partners section" id="partners">
|
||||
<div class="container">
|
||||
<div class="section-head reveal">
|
||||
<span class="eyebrow">قريبًا في التطبيق</span>
|
||||
<h2>مطاعم مميزة تنضم قريباً إلى Tabeley</h2>
|
||||
<p>نعمل على توسيع الشبكة لتشمل خيارات أكثر تناسب اللقاءات اليومية والتجارب الخاصة.</p>
|
||||
</div>
|
||||
|
||||
<div class="badge-cloud reveal">
|
||||
<span class="partner-badge">مطعم 1</span>
|
||||
<span class="partner-badge"></span>
|
||||
<span class="partner-badge">Luna Cafe</span>
|
||||
<span class="partner-badge">دار الياسمين</span>
|
||||
<span class="partner-badge">Urban Table</span>
|
||||
<span class="partner-badge">قهوة المدينة</span>
|
||||
</div>-->
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="trust section section-soft" id="trust">
|
||||
<div class="container trust-shell reveal">
|
||||
<div>
|
||||
<span class="eyebrow">عن Tabeley</span>
|
||||
<h2>شركة ناشئة سورية تبني تجربة حجز أكثر ذكاءً</h2>
|
||||
</div>
|
||||
<p>
|
||||
Tabeley مشروع تقني ناشئ من سوريا يهدف إلى تنظيم تجربة الحجز في قطاع المطاعم بشكل رقمي وآمن،
|
||||
مع التركيز على سهولة الاستخدام، حماية البيانات، وتحسين التنسيق بين العميل والمطعم لرفع جودة التجربة بالكامل.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="faq section" id="faq">
|
||||
<div class="container faq-shell">
|
||||
<div class="section-head reveal">
|
||||
<span class="eyebrow">الأسئلة الشائعة</span>
|
||||
<h2>إجابات مباشرة على أكثر الأسئلة المتكررة</h2>
|
||||
</div>
|
||||
<div class="faq-list">
|
||||
<details class="faq-item reveal" open>
|
||||
<summary>هل الحجز مجاني؟</summary>
|
||||
<p>نعم، تجربة الحجز للمستخدم مجانية، والهدف هو تسهيل الوصول إلى المطاعم وتنظيم الزيارة دون رسوم إضافية على عملية الحجز.</p>
|
||||
</details>
|
||||
<details class="faq-item reveal reveal-delay-1">
|
||||
<summary>هل يمكن تعديل أو إلغاء الحجز؟</summary>
|
||||
<p>يوفّر التطبيق إدارة سهلة للحجوزات، بحيث يمكن تعديل الموعد أو إلغاء الحجز وفق سياسة المطعم وإتاحة الوقت المحدد.</p>
|
||||
</details>
|
||||
<details class="faq-item reveal reveal-delay-2">
|
||||
<summary>هل يوفر التطبيق خصومات؟</summary>
|
||||
<p>نعم، سيعرض التطبيق عروضًا وخصومات مرتبطة ببعض الأوقات والمطاعم المشاركة عند توفرها داخل المنصة.</p>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="download section" id="download">
|
||||
<div class="container download-shell reveal">
|
||||
<div>
|
||||
<span class="eyebrow eyebrow-light">جاهز للتجربة؟</span>
|
||||
<h2>ابدأ تجربة حجز أسهل اليوم</h2>
|
||||
<p>نزّل التطبيق وابدأ بتنظيم حجوزاتك بطريقة أسرع وأوضح وأكثر راحة.</p>
|
||||
<p class="download-note download-note-light">رابط مباشر وآمن لتحميل نسخة Android APK.</p>
|
||||
</div>
|
||||
<a href="/files/tabeley.apk" class="play-badge" aria-label="تحميل تطبيق Tabeley لأندرويد" download>
|
||||
<span class="play-icon">▶</span>
|
||||
<span>
|
||||
<small>تنزيل مباشر</small>
|
||||
<strong>Android APK</strong>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="contact section" id="contact">
|
||||
<div class="container contact-grid">
|
||||
<div class="contact-info reveal">
|
||||
<span class="eyebrow">تواصل معنا</span>
|
||||
<h2>نرحّب باستفساراتكم وشراكاتكم</h2>
|
||||
<p>إذا كنت مطعمًا مهتمًا بالانضمام أو مستخدمًا لديه استفسار، يمكنك التواصل معنا مباشرة عبر القنوات التالية.</p>
|
||||
<div class="contact-cards">
|
||||
<a href="mailto:support@tabeley.com" class="contact-card">
|
||||
<span class="contact-card-icon">✉</span>
|
||||
<span>
|
||||
<strong>البريد الإلكتروني</strong>
|
||||
<span dir="ltr">support@tabeley.com</span>
|
||||
</span>
|
||||
</a>
|
||||
<a href="tel:+963983233965" class="contact-card">
|
||||
<span class="contact-card-icon">☎</span>
|
||||
<span>
|
||||
<strong>رقم الهاتف</strong>
|
||||
<span dir="ltr">+963983233965</span>
|
||||
</span>
|
||||
</a>
|
||||
<a href="https://www.instagram.com/tabeley_reservations?igsh=MTQ4ZzNxMzR0anExNA==" target="_blank" rel="noopener" class="contact-card">
|
||||
<span class="contact-card-icon">◎</span>
|
||||
<span>
|
||||
<strong>إنستغرام</strong>
|
||||
<span>@tabeley_reservations</span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form id="contactForm" class="contact-form reveal reveal-delay-1">
|
||||
<h3>أرسل لنا رسالة</h3>
|
||||
<input type="text" placeholder="الاسم الكامل" required>
|
||||
<input type="email" placeholder="البريد الإلكتروني" required>
|
||||
<textarea placeholder="اكتب رسالتك هنا" required></textarea>
|
||||
<button type="submit">إرسال الرسالة</button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="site-footer">
|
||||
<div class="container footer-shell">
|
||||
<p>© 2026 Tabeley. جميع الحقوق محفوظة.</p>
|
||||
<div class="footer-links">
|
||||
<a href="#">سياسة الخصوصية</a>
|
||||
<a href="#">شروط الاستخدام</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="script.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
ثنائية
landing-page-deploy.zip
Normal file
ثنائية
landing-page-deploy.zip
Normal file
ملف ثنائي غير معروض.
21
nginx.conf
Normal file
21
nginx.conf
Normal file
@@ -0,0 +1,21 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_types text/plain text/css application/javascript application/json image/svg+xml;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
|
||||
location ~* \.(css|js|jpg|jpeg|png|webp|svg|ico)$ {
|
||||
expires 30d;
|
||||
add_header Cache-Control "public, max-age=2592000";
|
||||
}
|
||||
}
|
||||
146
script.js
Normal file
146
script.js
Normal file
@@ -0,0 +1,146 @@
|
||||
(function () {
|
||||
const reveals = document.querySelectorAll('.reveal');
|
||||
const menuToggle = document.querySelector('.menu-toggle');
|
||||
const navMenu = document.querySelector('.nav');
|
||||
|
||||
if (menuToggle && navMenu) {
|
||||
menuToggle.addEventListener('click', function () {
|
||||
const isOpen = navMenu.classList.toggle('is-open');
|
||||
menuToggle.classList.toggle('is-open', isOpen);
|
||||
menuToggle.setAttribute('aria-expanded', String(isOpen));
|
||||
});
|
||||
|
||||
navMenu.querySelectorAll('a').forEach((link) => {
|
||||
link.addEventListener('click', function () {
|
||||
navMenu.classList.remove('is-open');
|
||||
menuToggle.classList.remove('is-open');
|
||||
menuToggle.setAttribute('aria-expanded', 'false');
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener('click', function (event) {
|
||||
if (!navMenu.classList.contains('is-open')) return;
|
||||
if (navMenu.contains(event.target) || menuToggle.contains(event.target)) return;
|
||||
|
||||
navMenu.classList.remove('is-open');
|
||||
menuToggle.classList.remove('is-open');
|
||||
menuToggle.setAttribute('aria-expanded', 'false');
|
||||
});
|
||||
|
||||
window.addEventListener('resize', function () {
|
||||
if (window.innerWidth > 900) {
|
||||
navMenu.classList.remove('is-open');
|
||||
menuToggle.classList.remove('is-open');
|
||||
menuToggle.setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if ('IntersectionObserver' in window) {
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('is-visible');
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
}, { threshold: 0.16 });
|
||||
|
||||
reveals.forEach((item) => observer.observe(item));
|
||||
} else {
|
||||
reveals.forEach((item) => item.classList.add('is-visible'));
|
||||
}
|
||||
|
||||
const faqItems = document.querySelectorAll('.faq-item');
|
||||
faqItems.forEach((item) => {
|
||||
item.addEventListener('toggle', function () {
|
||||
if (!item.open) return;
|
||||
faqItems.forEach((other) => {
|
||||
if (other !== item) {
|
||||
other.open = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const form = document.getElementById('contactForm');
|
||||
if (!form) return;
|
||||
|
||||
const submitButton = form.querySelector('button[type="submit"]');
|
||||
const defaultSubmitLabel = submitButton ? submitButton.textContent : 'إرسال الرسالة';
|
||||
const fallbackApi = 'https://app.tabeley.com/api';
|
||||
const configuredBase = window.TABELEY_API_BASE_URL;
|
||||
const originBase = `${window.location.origin}/api`;
|
||||
|
||||
const candidates = [configuredBase, originBase, fallbackApi]
|
||||
.filter(Boolean)
|
||||
.map((url) => String(url).replace(/\/+$/, ''))
|
||||
.filter((url, idx, arr) => arr.indexOf(url) === idx);
|
||||
|
||||
function getFieldValue(selector) {
|
||||
const field = form.querySelector(selector);
|
||||
return field ? field.value.trim() : '';
|
||||
}
|
||||
|
||||
async function postFeedback(baseUrl, payload) {
|
||||
return fetch(`${baseUrl}/public/feedback`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json'
|
||||
},
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
form.addEventListener('submit', async function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const name = getFieldValue('input[type="text"]');
|
||||
const email = getFieldValue('input[type="email"]');
|
||||
const message = getFieldValue('textarea');
|
||||
|
||||
if (message.length < 5) {
|
||||
alert('يرجى كتابة رسالة أوضح قبل الإرسال.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (submitButton) {
|
||||
submitButton.disabled = true;
|
||||
submitButton.textContent = 'جارٍ الإرسال...';
|
||||
}
|
||||
|
||||
const payload = { message, name, email };
|
||||
let sent = false;
|
||||
let lastError = null;
|
||||
|
||||
for (const base of candidates) {
|
||||
try {
|
||||
const response = await postFeedback(base, payload);
|
||||
if (response.ok) {
|
||||
sent = true;
|
||||
break;
|
||||
}
|
||||
|
||||
const body = await response.text();
|
||||
lastError = body || `HTTP ${response.status}`;
|
||||
} catch (err) {
|
||||
lastError = err;
|
||||
}
|
||||
}
|
||||
|
||||
if (submitButton) {
|
||||
submitButton.disabled = false;
|
||||
submitButton.textContent = defaultSubmitLabel;
|
||||
}
|
||||
|
||||
if (sent) {
|
||||
form.reset();
|
||||
alert('تم إرسال رسالتك بنجاح.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.error('Feedback send failed:', lastError);
|
||||
alert('تعذر إرسال الرسالة حالياً. حاول مرة أخرى لاحقاً.');
|
||||
});
|
||||
})();
|
||||
1151
styles.css
Normal file
1151
styles.css
Normal file
تم حذف اختلاف الملف لأن الملف كبير جداً
تحميل الاختلاف
المرجع في مشكلة جديدة
حظر مستخدم