diff --git a/src/components/details/amenities.tsx b/src/components/details/amenities.tsx
new file mode 100644
index 0000000..ba51c66
--- /dev/null
+++ b/src/components/details/amenities.tsx
@@ -0,0 +1,79 @@
+"use client"
+
+import { useState, useEffect } from "react";
+import { X } from "lucide-react";
+import { Amenity } from "./types";
+
+interface AmenitiesProps {
+ amenities: Amenity[];
+}
+
+export function Amenities({ amenities }: AmenitiesProps) {
+ const [showAmenities, setShowAmenities] = useState(false);
+
+ useEffect(() => {
+ if (showAmenities) {
+ document.body.style.overflow = 'hidden';
+ } else {
+ document.body.style.overflow = 'unset';
+ }
+ return () => {
+ document.body.style.overflow = 'unset';
+ };
+ }, [showAmenities]);
+
+ return (
+
+
What this place offers
+
+ {amenities.slice(0, 6).map((item, idx) => (
+
+
+ {item.label}
+
+ ))}
+
+
+
+ {/* Amenities Modal */}
+ {showAmenities && (
+
+
setShowAmenities(false)} />
+
+
+
What this place offers
+
+
+
+
+ {Array.from(new Set(amenities.map(a => a.category))).map(category => (
+
+
{category}
+
+ {amenities.filter(a => a.category === category).map((item, idx) => (
+
+
+ {item.label}
+
+ ))}
+
+
+ ))}
+
+
+
+
+ )}
+
+ );
+}
diff --git a/src/components/details/booking-card.tsx b/src/components/details/booking-card.tsx
new file mode 100644
index 0000000..d0fa90f
--- /dev/null
+++ b/src/components/details/booking-card.tsx
@@ -0,0 +1,72 @@
+import { Star } from "lucide-react";
+
+interface BookingCardProps {
+ price: number;
+ rating: number;
+ reviewCount: number;
+}
+
+export function BookingCard({ price, rating, reviewCount }: BookingCardProps) {
+ return (
+
+
+
+
+ ₨{price.toLocaleString()}
+ / night
+
+
+
+ {rating} · {reviewCount} reviews
+
+
+
+
+
+
+
+
+
+
+
+
+
+
You won't be charged yet
+
+
+
+ ₨{price.toLocaleString()} x 5 nights
+ ₨{(price * 5).toLocaleString()}
+
+
+ Cleaning fee
+ ₨5,000
+
+
+ Service fee
+ ₨8,000
+
+
+ Total
+ ₨{(price * 5 + 13000).toLocaleString()}
+
+
+
+
+ );
+}
diff --git a/src/components/details/description.tsx b/src/components/details/description.tsx
new file mode 100644
index 0000000..bc72db4
--- /dev/null
+++ b/src/components/details/description.tsx
@@ -0,0 +1,14 @@
+interface DescriptionProps {
+ description: string;
+}
+
+export function Description({ description }: DescriptionProps) {
+ return (
+
+
About this place
+
+ {description}
+
+
+ );
+}
diff --git a/src/components/details/features.tsx b/src/components/details/features.tsx
index 99cbbc8..2c0b4bd 100644
--- a/src/components/details/features.tsx
+++ b/src/components/details/features.tsx
@@ -1,9 +1,16 @@
"use client"
-import { Star, MapPin, Share, Heart, Wifi, Car, Utensils, Wind, Monitor, X, Waves, Dumbbell, Coffee, Briefcase, Droplets, Tv } from "lucide-react";
-import { useState, useEffect } from "react";
+import { Wifi, Car, Utensils, Wind, Monitor, Waves, Dumbbell, Coffee, Briefcase, Droplets, Tv } from "lucide-react";
import localFont from "next/font/local";
-import Image from "next/image";
+import { Header } from "./header";
+import { ImageGrid } from "./image-grid";
+import { HostInfo } from "./host-info";
+import { Highlights } from "./highlights";
+import { Description } from "./description";
+import { Amenities } from "./amenities";
+import { BookingCard } from "./booking-card";
+import { Reviews } from "./reviews";
+import { Property } from "./types";
const figtree = localFont({
src: [
@@ -13,7 +20,7 @@ const figtree = localFont({
],
})
-const property = {
+const property: Property = {
title: "Luxury Villa with Panoramic Mountain Views",
location: "Islamabad, Pakistan",
guests: 4,
@@ -95,368 +102,40 @@ const property = {
}
function Features() {
- const [showAmenities, setShowAmenities] = useState(false);
- const [showReviews, setShowReviews] = useState(false);
-
- // Prevent body scroll when modal is open
- useEffect(() => {
- if (showAmenities || showReviews) {
- document.body.style.overflow = 'hidden';
- } else {
- document.body.style.overflow = 'unset';
- }
- return () => {
- document.body.style.overflow = 'unset';
- };
- }, [showAmenities, showReviews]);
-
return (
- {/* Header Section */}
-
-
-
{property.title}
-
-
-
-
- {property.rating} · {property.reviewCount} reviews
-
- |
-
-
- {property.location}
-
-
-
-
-
-
-
-
-
-
- {/* Image Grid */}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
{/* Main Content */}
-
{/* Left Column */}
-
- {/* Host Info & Stats */}
-
-
-
Hosted by {property.host.name}
-
- {property.guests} guests · {property.bedrooms} bedrooms · {property.beds} beds · {property.baths} baths
-
-
-
-
-
-
-
- {/* Highlights */}
-
-
-
-
-
-
-
Top rated host
-
John has received 5-star ratings from 95% of recent guests.
-
-
-
-
-
-
-
-
Great location
-
100% of recent guests gave the location a 5-star rating.
-
-
-
-
- {/* Description */}
-
-
About this place
-
- {property.description}
-
-
-
- {/* Amenities */}
-
-
What this place offers
-
- {property.amenities.slice(0, 6).map((item, idx) => (
-
-
- {item.label}
-
- ))}
-
-
-
-
+
+
+
+
{/* Right Column - Sticky Booking Card */}
-
-
-
-
- ₨{property.price.toLocaleString()}
- / night
-
-
-
- {property.rating} · {property.reviewCount} reviews
-
-
-
-
-
-
-
-
-
-
-
-
-
-
You won't be charged yet
-
-
-
- ₨{property.price.toLocaleString()} x 5 nights
- ₨{(property.price * 5).toLocaleString()}
-
-
- Cleaning fee
- ₨5,000
-
-
- Service fee
- ₨8,000
-
-
- Total
- ₨{(property.price * 5 + 13000).toLocaleString()}
-
-
-
-
+
- {/* Reviews Section */}
-
-
-
-
{property.rating} · {property.reviewCount} reviews
-
-
-
- {property.reviews.slice(0, 4).map((review) => (
-
-
-
-
-
{review.user}
-
{review.date}
-
-
-
- "{review.comment}"
-
-
- ))}
-
-
-
-
- {/* Amenities Modal */}
- {showAmenities && (
-
-
setShowAmenities(false)} />
-
-
-
What this place offers
-
-
-
-
- {Array.from(new Set(property.amenities.map(a => a.category))).map(category => (
-
-
{category}
-
- {property.amenities.filter(a => a.category === category).map((item, idx) => (
-
-
- {item.label}
-
- ))}
-
-
- ))}
-
-
-
-
- )}
-
- {/* Reviews Modal */}
- {showReviews && (
-
-
setShowReviews(false)} />
-
-
-
-
-
{property.rating} · {property.reviewCount} reviews
-
-
-
-
-
- {property.reviews.map((review) => (
-
-
-
-
-
{review.user}
-
{review.date}
-
-
-
- {[...Array(5)].map((_, i) => (
-
- ))}
-
-
- {review.comment}
-
-
- ))}
-
-
-
-
- )}
+
)
}
diff --git a/src/components/details/header.tsx b/src/components/details/header.tsx
new file mode 100644
index 0000000..7429b05
--- /dev/null
+++ b/src/components/details/header.tsx
@@ -0,0 +1,39 @@
+import { Star, MapPin, Share, Heart } from "lucide-react";
+import { Property } from "./types";
+
+interface HeaderProps {
+ property: Property;
+}
+
+export function Header({ property }: HeaderProps) {
+ return (
+
+
+
{property.title}
+
+
+
+
+ {property.rating} · {property.reviewCount} reviews
+
+ |
+
+
+ {property.location}
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/details/highlights.tsx b/src/components/details/highlights.tsx
new file mode 100644
index 0000000..8fe7051
--- /dev/null
+++ b/src/components/details/highlights.tsx
@@ -0,0 +1,26 @@
+import { Star, MapPin } from "lucide-react";
+
+export function Highlights() {
+ return (
+
+
+
+
+
+
+
Top rated host
+
John has received 5-star ratings from 95% of recent guests.
+
+
+
+
+
+
+
+
Great location
+
100% of recent guests gave the location a 5-star rating.
+
+
+
+ );
+}
diff --git a/src/components/details/host-info.tsx b/src/components/details/host-info.tsx
new file mode 100644
index 0000000..d1ac7a9
--- /dev/null
+++ b/src/components/details/host-info.tsx
@@ -0,0 +1,31 @@
+import Image from "next/image";
+import { Host } from "./types";
+
+interface HostInfoProps {
+ host: Host;
+ guests: number;
+ bedrooms: number;
+ beds: number;
+ baths: number;
+}
+
+export function HostInfo({ host, guests, bedrooms, beds, baths }: HostInfoProps) {
+ return (
+
+
+
Hosted by {host.name}
+
+ {guests} guests · {bedrooms} bedrooms · {beds} beds · {baths} baths
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/details/image-grid.tsx b/src/components/details/image-grid.tsx
new file mode 100644
index 0000000..bcf64d4
--- /dev/null
+++ b/src/components/details/image-grid.tsx
@@ -0,0 +1,61 @@
+import Image from "next/image";
+
+interface ImageGridProps {
+ images: string[];
+}
+
+export function ImageGrid({ images }: ImageGridProps) {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/details/reviews.tsx b/src/components/details/reviews.tsx
new file mode 100644
index 0000000..5e1ea3c
--- /dev/null
+++ b/src/components/details/reviews.tsx
@@ -0,0 +1,118 @@
+"use client"
+
+import { useState, useEffect } from "react";
+import { Star, X } from "lucide-react";
+import Image from "next/image";
+import { Review } from "./types";
+
+interface ReviewsProps {
+ reviews: Review[];
+ rating: number;
+ reviewCount: number;
+}
+
+export function Reviews({ reviews, rating, reviewCount }: ReviewsProps) {
+ const [showReviews, setShowReviews] = useState(false);
+
+ useEffect(() => {
+ if (showReviews) {
+ document.body.style.overflow = 'hidden';
+ } else {
+ document.body.style.overflow = 'unset';
+ }
+ return () => {
+ document.body.style.overflow = 'unset';
+ };
+ }, [showReviews]);
+
+ return (
+
+
+
+
{rating} · {reviewCount} reviews
+
+
+
+ {reviews.slice(0, 4).map((review) => (
+
+
+
+
+
{review.user}
+
{review.date}
+
+
+
+ "{review.comment}"
+
+
+ ))}
+
+
+
+ {/* Reviews Modal */}
+ {showReviews && (
+
+
setShowReviews(false)} />
+
+
+
+
+
{rating} · {reviewCount} reviews
+
+
+
+
+
+ {reviews.map((review) => (
+
+
+
+
+
{review.user}
+
{review.date}
+
+
+
+ {[...Array(5)].map((_, i) => (
+
+ ))}
+
+
+ {review.comment}
+
+
+ ))}
+
+
+
+
+ )}
+
+ );
+}
diff --git a/src/components/details/types.ts b/src/components/details/types.ts
new file mode 100644
index 0000000..10b4f22
--- /dev/null
+++ b/src/components/details/types.ts
@@ -0,0 +1,40 @@
+import { LucideIcon } from "lucide-react";
+
+export interface Amenity {
+ icon: LucideIcon;
+ label: string;
+ category: string;
+}
+
+export interface Review {
+ id: number;
+ user: string;
+ avatar: string;
+ date: string;
+ rating: number;
+ comment: string;
+}
+
+export interface Host {
+ name: string;
+ image: string;
+ joined: string;
+ isSuperhost: boolean;
+}
+
+export interface Property {
+ title: string;
+ location: string;
+ guests: number;
+ bedrooms: number;
+ beds: number;
+ baths: number;
+ rating: number;
+ reviewCount: number;
+ price: number;
+ description: string;
+ amenities: Amenity[];
+ host: Host;
+ images: string[];
+ reviews: Review[];
+}