Forced merge: merge ai-dev into master
This commit is contained in:
commit
43477b80e2
File diff suppressed because one or more lines are too long
@ -30,26 +30,26 @@ const nextConfig = {
|
|||||||
source: '/home',
|
source: '/home',
|
||||||
destination: '/web_pages/home',
|
destination: '/web_pages/home',
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
source: '/services',
|
source: '/services',
|
||||||
destination: '/web_pages/services',
|
destination: '/web_pages/services',
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
source: '/contact',
|
source: '/contact',
|
||||||
destination: '/web_pages/contact',
|
destination: '/web_pages/contact',
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
source: '/faq',
|
source: '/faq',
|
||||||
destination: '/web_pages/faq',
|
destination: '/web_pages/faq',
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
source: '/blog',
|
source: '/blog',
|
||||||
destination: '/web_pages/blog',
|
destination: '/web_pages/blog',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
source: '/exclusive-offers',
|
||||||
|
destination: '/web_pages/exclusive-offers',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -67,6 +67,11 @@ export const webPagesNavBar = [
|
|||||||
href: '/blog',
|
href: '/blog',
|
||||||
label: 'blog',
|
label: 'blog',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
href: '/exclusive-offers',
|
||||||
|
label: 'عروض حصرية',
|
||||||
|
},
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export default menuNavBar;
|
export default menuNavBar;
|
||||||
|
|||||||
108
frontend/src/pages/web_pages/exclusive-offers.tsx
Normal file
108
frontend/src/pages/web_pages/exclusive-offers.tsx
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import type { ReactElement } from 'react';
|
||||||
|
import Head from 'next/head';
|
||||||
|
import LayoutGuest from '../../../layouts/Guest';
|
||||||
|
import WebSiteHeader from '../../../components/WebPageComponents/Header';
|
||||||
|
import WebSiteFooter from '../../../components/WebPageComponents/Footer';
|
||||||
|
|
||||||
|
interface Flight {
|
||||||
|
id: number;
|
||||||
|
flight_number: string;
|
||||||
|
price: number;
|
||||||
|
partner: { id: number; name: string };
|
||||||
|
}
|
||||||
|
interface Hotel {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
price_per_night: number;
|
||||||
|
partner: { id: number; name: string };
|
||||||
|
}
|
||||||
|
interface Review {
|
||||||
|
id: number;
|
||||||
|
partner: { id: number };
|
||||||
|
rating: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ExclusiveOffers() {
|
||||||
|
const [flights, setFlights] = useState<Flight[]>([]);
|
||||||
|
const [hotels, setHotels] = useState<Hotel[]>([]);
|
||||||
|
const [reviews, setReviews] = useState<Review[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch('/api/flights')
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => setFlights(data || []));
|
||||||
|
fetch('/api/hotels')
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => setHotels(data || []));
|
||||||
|
fetch('/api/reviews')
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => setReviews(data || []));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const getAvgRating = (partnerId: number) => {
|
||||||
|
const list = reviews.filter(r => r.partner.id === partnerId);
|
||||||
|
if (!list.length) return '-';
|
||||||
|
const sum = list.reduce((acc, r) => acc + r.rating, 0);
|
||||||
|
return (sum / list.length).toFixed(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>عروض حصرية | Royal Aurora</title>
|
||||||
|
</Head>
|
||||||
|
<WebSiteHeader projectName="Royal Aurora" />
|
||||||
|
<main className="container mx-auto py-8 px-4">
|
||||||
|
<section className="mb-12">
|
||||||
|
<h2 className="text-2xl font-bold mb-4">عروض طيران حصرية</h2>
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
|
{flights.map(f => (
|
||||||
|
<div key={f.id} className="border rounded-lg shadow p-4 flex flex-col">
|
||||||
|
<div className="h-40 bg-gray-200 mb-4 flex items-center justify-center text-gray-500">
|
||||||
|
صورة طيران
|
||||||
|
</div>
|
||||||
|
<h3 className="text-xl font-semibold">{f.flight_number}</h3>
|
||||||
|
<p className="mt-2">السعر: ${f.price}</p>
|
||||||
|
<p>تقييم: {getAvgRating(f.partner.id)}</p>
|
||||||
|
<a
|
||||||
|
href={`/bookings/new?flightId=${f.id}`}
|
||||||
|
className="mt-auto bg-purple-600 text-white text-center py-2 rounded hover:bg-purple-700 transition"
|
||||||
|
>
|
||||||
|
احجز الآن
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2 className="text-2xl font-bold mb-4">عروض فنادق حصرية</h2>
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
|
{hotels.map(h => (
|
||||||
|
<div key={h.id} className="border rounded-lg shadow p-4 flex flex-col">
|
||||||
|
<div className="h-40 bg-gray-200 mb-4 flex items-center justify-center text-gray-500">
|
||||||
|
صورة فندق
|
||||||
|
</div>
|
||||||
|
<h3 className="text-xl font-semibold">{h.name}</h3>
|
||||||
|
<p className="mt-2">السعر: ${h.price_per_night} / الليلة</p>
|
||||||
|
<p>تقييم: {getAvgRating(h.partner.id)}</p>
|
||||||
|
<a
|
||||||
|
href={`/bookings/new?hotelId=${h.id}`}
|
||||||
|
className="mt-auto bg-purple-600 text-white text-center py-2 rounded hover:bg-purple-700 transition"
|
||||||
|
>
|
||||||
|
احجز الآن
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
<WebSiteFooter projectName="Royal Aurora" />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign guest layout for public access
|
||||||
|
ExclusiveOffers.getLayout = function getLayout(page: ReactElement) {
|
||||||
|
return <LayoutGuest>{page}</LayoutGuest>;
|
||||||
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user