2502 lines
112 KiB
TypeScript
2502 lines
112 KiB
TypeScript
"use client";
|
|
|
|
|
|
|
|
import { useState, ChangeEvent, FormEvent, useEffect } from "react";
|
|
|
|
import Footer from "@/components/Footer";
|
|
|
|
import PropertyGallery from "@/components/PropertyGallery";
|
|
|
|
import PropertyNav from "@/components/PropertyNav";
|
|
|
|
import InnerBanner from "@/components/InnerBanner";
|
|
|
|
import { Property } from "@/data/properties";
|
|
|
|
import axios from "axios";
|
|
|
|
import { useCompare } from "@/context/CompareContext";
|
|
|
|
import dynamic from 'next/dynamic';
|
|
import Link from 'next/link';
|
|
import { motion } from "framer-motion";
|
|
|
|
const AnimateSection = ({ children, className, id, direction = "left" }: { children: React.ReactNode, className?: string, id?: string, direction?: "left" | "right" | "up" | "down" }) => {
|
|
const variants = {
|
|
hidden: {
|
|
opacity: 0,
|
|
scale: direction === "left" || direction === "right" ? 0.95 : 1,
|
|
y: direction === "up" ? 30 : direction === "down" ? -30 : 0
|
|
},
|
|
visible: { opacity: 1, scale: 1, y: 0 }
|
|
};
|
|
|
|
return (
|
|
<motion.div
|
|
id={id}
|
|
className={className}
|
|
initial="hidden"
|
|
whileInView="visible"
|
|
viewport={{ once: true, margin: "-50px" }}
|
|
transition={{ duration: 0.6, ease: "easeOut" }}
|
|
variants={variants}
|
|
>
|
|
{children}
|
|
</motion.div>
|
|
);
|
|
};
|
|
|
|
const getOverviewIcon = (type: string) => {
|
|
switch (type) {
|
|
case "land":
|
|
return (
|
|
<svg className="w-5 h-5 text-gray-700 dark:text-gray-300" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4" />
|
|
</svg>
|
|
);
|
|
case "metro":
|
|
return (
|
|
<svg className="w-5 h-5 text-gray-700 dark:text-gray-300" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" />
|
|
</svg>
|
|
);
|
|
case "road":
|
|
return (
|
|
<svg className="w-5 h-5 text-gray-700 dark:text-gray-300" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7" />
|
|
</svg>
|
|
);
|
|
case "openArea":
|
|
return (
|
|
<svg className="w-5 h-5 text-gray-700 dark:text-gray-300" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
|
</svg>
|
|
);
|
|
case "density":
|
|
return (
|
|
<svg className="w-5 h-5 text-gray-700 dark:text-gray-300" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 5a1 1 0 011-1h4a1 1 0 011 1v7a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM14 5a1 1 0 011-1h4a1 1 0 011 1v7a1 1 0 01-1 1h-4a1 1 0 01-1-1V5zM4 16a1 1 0 011-1h4a1 1 0 011 1v3a1 1 0 01-1 1H5a1 1 0 01-1-1v-3zM14 16a1 1 0 011-1h4a1 1 0 011 1v3a1 1 0 01-1 1h-4a1 1 0 01-1-1v-3z" />
|
|
</svg>
|
|
);
|
|
case "clubhouse":
|
|
return (
|
|
<svg className="w-5 h-5 text-gray-700 dark:text-gray-300" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
|
|
</svg>
|
|
);
|
|
default:
|
|
return null;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
const ConnectivityMap = dynamic(() => import('./ConnectivityMap'), {
|
|
|
|
ssr: false,
|
|
|
|
loading: () => <div className="h-[450px] w-full bg-gray-100 animate-pulse rounded-xl" />
|
|
|
|
});
|
|
|
|
|
|
|
|
interface FormData {
|
|
|
|
name: string;
|
|
|
|
email: string;
|
|
|
|
phone: string;
|
|
|
|
message: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface FormErrors {
|
|
|
|
name?: string;
|
|
|
|
email?: string;
|
|
|
|
phone?: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
const sections = [
|
|
|
|
{ id: "overview", label: "Overview" },
|
|
|
|
{ id: "pricing", label: "Pricing" },
|
|
|
|
{ id: "connectivity", label: "Connectivity" },
|
|
|
|
{ id: "master-plan", label: "Master Plan" },
|
|
|
|
{ id: "floor-plans", label: "Floor Plans" },
|
|
|
|
{ id: "amenities", label: "Amenities" },
|
|
|
|
// { id: "challenges", label: "Challenges" },
|
|
|
|
{ id: "approvals", label: "Approvals" },
|
|
|
|
{ id: "builder", label: "Builder" },
|
|
|
|
{ id: "locality", label: "Locality" },
|
|
|
|
{ id: "faq", label: "FAQ's" },
|
|
|
|
];
|
|
|
|
|
|
|
|
export default function PropertyDetailClient({ property }: { property: Property }) {
|
|
|
|
const [formData, setFormData] = useState<FormData>({
|
|
|
|
name: "",
|
|
|
|
email: "",
|
|
|
|
phone: "",
|
|
|
|
message: "",
|
|
|
|
});
|
|
|
|
|
|
|
|
const [formErrors, setFormErrors] = useState<FormErrors>({});
|
|
|
|
const [alert, setAlert] = useState<{ show: boolean; type: string; message: string }>({
|
|
|
|
show: false,
|
|
|
|
type: "",
|
|
|
|
message: "",
|
|
|
|
});
|
|
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
|
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
|
|
const [isConnectivityExpanded, setIsConnectivityExpanded] = useState(false);
|
|
|
|
const [activeMapTab, setActiveMapTab] = useState("Transit");
|
|
|
|
const [isMapEnabled, setIsMapEnabled] = useState(false);
|
|
|
|
const [isLightboxOpen, setIsLightboxOpen] = useState(false);
|
|
|
|
|
|
|
|
const [isAmenitiesModalOpen, setIsAmenitiesModalOpen] = useState(false);
|
|
|
|
const [isAmenitiesLoading, setIsAmenitiesLoading] = useState(false);
|
|
|
|
const [activeFaqTab, setActiveFaqTab] = useState('Services');
|
|
|
|
const [expandedFaq, setExpandedFaq] = useState<number | null>(null);
|
|
|
|
|
|
|
|
const { addToCompare, removeFromCompare, isInCompare } = useCompare();
|
|
|
|
const isCompared = isInCompare(property.id);
|
|
|
|
const [isWishlisted, setIsWishlisted] = useState(false);
|
|
|
|
|
|
|
|
const handleShare = async () => {
|
|
|
|
if (navigator.share) {
|
|
|
|
try {
|
|
|
|
await navigator.share({
|
|
|
|
title: property.title,
|
|
|
|
text: `Check out this property: ${property.title} in ${property.location}`,
|
|
|
|
url: window.location.href,
|
|
|
|
});
|
|
|
|
} catch (error) {
|
|
|
|
console.log('Error sharing:', error);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Fallback to clipboard
|
|
|
|
navigator.clipboard.writeText(window.location.href);
|
|
|
|
setAlert({
|
|
|
|
show: true,
|
|
|
|
type: 'success',
|
|
|
|
message: 'Link copied to clipboard!'
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleWishlist = () => {
|
|
|
|
setIsWishlisted(!isWishlisted);
|
|
|
|
setAlert({
|
|
|
|
show: true,
|
|
|
|
type: 'success',
|
|
|
|
message: !isWishlisted ? 'Added to your wishlist!' : 'Removed from your wishlist'
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
const handlePrint = () => {
|
|
|
|
window.print();
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleCompareToggle = () => {
|
|
if (isCompared) {
|
|
removeFromCompare(property.id);
|
|
setAlert({
|
|
show: true,
|
|
type: 'success',
|
|
message: 'Removed from compare list'
|
|
});
|
|
} else {
|
|
addToCompare(property);
|
|
setAlert({
|
|
show: true,
|
|
type: 'success',
|
|
message: 'Added to compare list'
|
|
});
|
|
}
|
|
};
|
|
|
|
// Helper for icons
|
|
const getAmenityIcon = (name: string) => {
|
|
switch (name) {
|
|
case "Gym - Indoor":
|
|
return <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 6l3 1m0 0l-3 9a5.002 5.002 0 006.001 0M6 7l3 9M6 7l6-2m6 2l3-1m-3 1l-3 9a5.002 5.002 0 006.001 0M18 7l3 9m-3-9l-6-2m0-2v2m0 16V5m0 16H9m3 0h3" /></svg>;
|
|
case "Gym - Outdoor":
|
|
case "Running Track":
|
|
return <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>;
|
|
case "Kids Play Area":
|
|
return <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M14.828 14.828a4 4 0 01-5.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>;
|
|
case "Amphitheatre":
|
|
return <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z" /></svg>;
|
|
case "Cafe/Restaurant":
|
|
return <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" /></svg>;
|
|
case "Badminton":
|
|
return <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" /></svg>;
|
|
case "Basketball":
|
|
return <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9" /></svg>;
|
|
case "Cricket Ground":
|
|
return <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>;
|
|
case "Cricket Pitch":
|
|
return <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" /></svg>;
|
|
default:
|
|
return <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" /></svg>;
|
|
}
|
|
};
|
|
|
|
const faqData = property.faq || {};
|
|
|
|
const floorPlans = property.floorPlans || [];
|
|
// Initialize activeFloorPlan with the first plan's ID if available
|
|
const [activeFloorPlan, setActiveFloorPlan] = useState<string>("");
|
|
|
|
useEffect(() => {
|
|
if (floorPlans.length > 0 && !activeFloorPlan) {
|
|
setActiveFloorPlan(floorPlans[0].id);
|
|
}
|
|
}, [floorPlans, activeFloorPlan]);
|
|
|
|
const activePlanDetails = floorPlans.find(plan => plan.id === activeFloorPlan) || (floorPlans.length > 0 ? floorPlans[0] : null);
|
|
|
|
const handleSeeAllAmenities = () => {
|
|
setIsAmenitiesLoading(true);
|
|
setTimeout(() => {
|
|
setIsAmenitiesLoading(false);
|
|
setIsAmenitiesModalOpen(true);
|
|
}, 500);
|
|
};
|
|
|
|
const handleChange = (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
|
|
|
const { name, value } = e.target;
|
|
|
|
setFormData((prev) => ({ ...prev, [name]: value }));
|
|
|
|
// Clear error when user types
|
|
|
|
if (formErrors[name as keyof FormErrors]) {
|
|
|
|
setFormErrors(prev => ({ ...prev, [name]: undefined }));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleSubmit = async (e: FormEvent) => {
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
const errors: FormErrors = {};
|
|
|
|
if (!formData.name.trim()) errors.name = "Name is required.";
|
|
|
|
if (!formData.phone.trim()) errors.phone = "Phone number is required.";
|
|
|
|
if (!formData.email.trim()) errors.email = "Email is required.";
|
|
|
|
|
|
|
|
setFormErrors(errors);
|
|
|
|
if (Object.keys(errors).length > 0) return;
|
|
|
|
|
|
|
|
setIsSubmitting(true);
|
|
|
|
|
|
|
|
const emailData = {
|
|
|
|
name: formData.name,
|
|
|
|
phone: formData.phone,
|
|
|
|
email: formData.email,
|
|
|
|
subject: `Property Inquiry: ${property?.title} from ${formData.name}`,
|
|
|
|
message: `<strong>Property Inquiry</strong><br /><br />
|
|
|
|
<strong>Property:</strong> ${property?.title}<br />
|
|
|
|
<strong>Location:</strong> ${property?.location}<br />
|
|
|
|
<strong>Price:</strong> ${property?.price}<br /><br />
|
|
|
|
<strong>Customer Details:</strong><br />
|
|
|
|
Name: ${formData.name}<br />
|
|
|
|
Phone: ${formData.phone}<br />
|
|
|
|
Email: ${formData.email}<br />
|
|
|
|
Message: ${formData.message || 'N/A'}`,
|
|
|
|
to: "hello@skyandsoil.com",
|
|
|
|
senderName: "Sky and Soil Property Inquiry",
|
|
|
|
};
|
|
|
|
|
|
|
|
try {
|
|
|
|
const res = await axios.post("https://mailserver.metatronnest.com/send", emailData, {
|
|
|
|
headers: { "Content-Type": "application/json" },
|
|
|
|
});
|
|
|
|
|
|
|
|
setAlert({
|
|
|
|
show: true,
|
|
|
|
type: "success",
|
|
|
|
message: res?.data?.message || "Request sent successfully! We will contact you soon.",
|
|
|
|
});
|
|
|
|
|
|
|
|
setFormData({ name: "", email: "", phone: "", message: "" });
|
|
|
|
setFormErrors({});
|
|
|
|
} catch (error) {
|
|
|
|
setAlert({
|
|
|
|
show: true,
|
|
|
|
type: "danger",
|
|
|
|
message: "Failed to send request. Please try again later.",
|
|
|
|
});
|
|
|
|
} finally {
|
|
|
|
setIsSubmitting(false);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (alert.show) {
|
|
|
|
const timer = setTimeout(() => {
|
|
|
|
setAlert(prev => ({ ...prev, show: false }));
|
|
|
|
}, 5000);
|
|
|
|
return () => clearTimeout(timer);
|
|
|
|
}
|
|
|
|
}, [alert.show]);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div className="min-h-screen bg-gray-50 dark:bg-black">
|
|
|
|
|
|
|
|
<InnerBanner
|
|
|
|
title={property.title}
|
|
|
|
subtitle={property.location}
|
|
|
|
breadcrumbs={[
|
|
|
|
{ label: "Home", href: "/" },
|
|
|
|
{ label: "Projects", href: "/projects" },
|
|
|
|
{ label: "Residential Real Estate", href: "/residential-real-estate" },
|
|
|
|
{ label: property.title }
|
|
|
|
]}
|
|
|
|
backgroundImage={property.image}
|
|
|
|
/>
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
{/* Sticky Navigation */}
|
|
|
|
<PropertyNav sections={sections} />
|
|
|
|
|
|
|
|
<div className="max-w-7xl mx-auto px-6 py-8">
|
|
|
|
{/* Image Gallery */}
|
|
|
|
<AnimateSection direction="up">
|
|
|
|
<PropertyGallery images={property.images} title={property.title} />
|
|
|
|
</AnimateSection>
|
|
|
|
|
|
|
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8 mt-8">
|
|
|
|
|
|
{/* Main Content */}
|
|
|
|
<div className="lg:col-span-2 space-y-8">
|
|
|
|
{/* Property Header */}
|
|
<div className="bg-white dark:bg-gray-900 rounded-2xl p-6 md:p-8 shadow-sm border border-gray-200 dark:border-gray-800">
|
|
|
|
{/* Title and Price Row */}
|
|
<div className="flex flex-col space-y-4 mb-6">
|
|
{/* Title */}
|
|
<h1 className="text-2xl md:text-3xl lg:text-4xl font-bold text-gray-900 dark:text-white">
|
|
{property.title}
|
|
</h1>
|
|
|
|
{/* Location and Price */}
|
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-content-center gap-9">
|
|
<div className="flex items-center text-gray-600 dark:text-gray-400">
|
|
<svg className="w-5 h-5 mr-2 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
|
|
</svg>
|
|
<span className="text-base text-md md:text-lg">{property.location}</span>
|
|
</div>
|
|
|
|
<div className="text-xl md:text-2xl lg:text-3xl font-bold text-gray-900 dark:text-white">
|
|
{property.price}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Stats Row with Icons */}
|
|
<div className="flex flex-wrap items-center gap-4 md:gap-6 pt-6 border-t border-gray-200 dark:border-gray-800">
|
|
|
|
{/* Possession */}
|
|
<div className="flex items-center gap-2">
|
|
<svg className="w-5 h-5 text-gray-500 dark:text-gray-400 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
|
</svg>
|
|
<div className="flex items-baseline gap-1">
|
|
<span className="text-sm text-gray-600 dark:text-gray-400">Possession:</span>
|
|
<span className="text-sm font-semibold text-gray-900 dark:text-white">{property.overview.possession}</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Area/Plot */}
|
|
<div className="flex items-center gap-2">
|
|
<svg className="w-5 h-5 text-gray-500 dark:text-gray-400 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
|
</svg>
|
|
<div className="flex items-baseline gap-1">
|
|
<span className="text-sm text-gray-600 dark:text-gray-400">{property.category === "Plots" ? "Plot" : property.overview.bhk} -</span>
|
|
<span className="text-sm font-semibold text-gray-900 dark:text-white">{property.overview.size}</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Sky&Soil Score */}
|
|
<div className="flex items-center gap-2">
|
|
<svg className="w-5 h-5 text-gray-500 dark:text-gray-400 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
</svg>
|
|
<div className="flex items-center gap-2">
|
|
<span className="text-sm text-gray-600 dark:text-gray-400">Sky&Soil Score:</span>
|
|
<div className="flex items-center gap-1">
|
|
{[1, 2, 3, 4, 5].map((star) => {
|
|
const score = property.skyAndSoilScore || 4;
|
|
const isFilled = star <= Math.floor(score);
|
|
const isHalf = !isFilled && star === Math.ceil(score) && score % 1 !== 0;
|
|
|
|
return (
|
|
<svg
|
|
key={star}
|
|
className={`w-4 h-4 ${isFilled ? 'text-orange-400' : isHalf ? 'text-orange-400' : 'text-gray-300 dark:text-gray-600'
|
|
}`}
|
|
fill={isFilled || isHalf ? 'currentColor' : 'none'}
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
{isHalf ? (
|
|
<>
|
|
<defs>
|
|
<linearGradient id={`half-${star}`}>
|
|
<stop offset="50%" stopColor="currentColor" />
|
|
<stop offset="50%" stopColor="transparent" />
|
|
</linearGradient>
|
|
</defs>
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
strokeWidth={1.5}
|
|
d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z"
|
|
fill={`url(#half-${star})`}
|
|
/>
|
|
</>
|
|
) : (
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
strokeWidth={isFilled ? 0 : 1.5}
|
|
d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z"
|
|
/>
|
|
)}
|
|
</svg>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Overview Section */}
|
|
|
|
<AnimateSection id="overview" className="bg-white dark:bg-gray-900 rounded-2xl p-8 shadow-sm border border-gray-200 dark:border-gray-800 scroll-mt-32" direction="left">
|
|
|
|
{/* Header Row: Title Left, Badges Right, Subtitle Right */}
|
|
<div className="flex flex-col lg:flex-row lg:items-start lg:justify-between gap-4 mb-8">
|
|
|
|
{/* Left Side: Title and Builder */}
|
|
<div>
|
|
<h2 className="text-2xl md:text-3xl font-bold text-gray-900 dark:text-white mb-2">Overview</h2>
|
|
<p className="text-gray-600 dark:text-gray-400">
|
|
{property.title} by <span className="text-primary font-medium">{property.builder?.name || "Builder"}</span>
|
|
</p>
|
|
</div>
|
|
|
|
{/* Right Side: Badges and Subtitle */}
|
|
<div className="flex flex-col items-start lg:items-end gap-3">
|
|
{/* Badges Row */}
|
|
<div className="flex items-center gap-2 flex-wrap">
|
|
{property.overview.badges?.map((badge, index) => (
|
|
<span key={index} className={`px-3 py-1.5 rounded-full text-sm font-medium ${badge === "Better" || badge === "Green Living" || badge === "Lake View" || badge === "Skyline Views"
|
|
? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" :
|
|
badge === "Average" || badge === "Premium" || badge === "Luxury" || badge === "Elite"
|
|
? "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400" :
|
|
"bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400"
|
|
}`}>
|
|
{badge}
|
|
</span>
|
|
))}
|
|
</div>
|
|
|
|
{/* Subtitle Text */}
|
|
<p className="text-gray-500 dark:text-gray-400 text-sm lg:text-right">
|
|
Average is based on comparable projects in {property.locality?.name || "this area"}
|
|
</p>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{/* Info Grid - 2 rows x 3 columns */}
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
{property.overview.stats?.map((stat, index) => {
|
|
const getBgColor = (index: number) => {
|
|
const colors = [
|
|
"bg-green-50 dark:bg-green-900/10 border-green-100 dark:border-green-800/30 text-green-600 dark:text-green-400",
|
|
"bg-blue-50 dark:bg-blue-900/10 border-blue-100 dark:border-blue-800/30 text-blue-600 dark:text-blue-400",
|
|
"bg-blue-50 dark:bg-blue-900/10 border-blue-100 dark:border-blue-800/30 text-blue-600 dark:text-blue-400",
|
|
"bg-red-50 dark:bg-red-900/10 border-red-100 dark:border-red-800/30 text-red-600 dark:text-red-400",
|
|
"bg-blue-50 dark:bg-blue-900/10 border-blue-100 dark:border-blue-800/30 text-blue-600 dark:text-blue-400",
|
|
"bg-blue-50 dark:bg-blue-900/10 border-blue-100 dark:border-blue-800/30 text-blue-600 dark:text-blue-400"
|
|
];
|
|
return colors[index % colors.length];
|
|
};
|
|
|
|
const bgColorClass = getBgColor(index).split(' ').slice(0, 2).join(' '); // Extract bg classes
|
|
const borderColorClass = getBgColor(index).split(' ').slice(2, 4).join(' '); // Extract border classes
|
|
const textColorClass = getBgColor(index).split(' ').slice(4).join(' '); // Extract text classes
|
|
// Simplified color logic to avoid complex parsing, just use alternating or specific based on type if needed.
|
|
// For now, let's use a simpler approach based on the original code
|
|
let bgClass = "bg-blue-50 dark:bg-blue-900/10 border border-blue-100 dark:border-blue-800/30";
|
|
let textClass = "text-blue-600 dark:text-blue-400";
|
|
|
|
if (stat.icon === 'land') {
|
|
bgClass = "bg-green-50 dark:bg-green-900/10 border border-green-100 dark:border-green-800/30";
|
|
textClass = "text-blue-600 dark:text-blue-400";
|
|
} else if (stat.icon === 'openArea') {
|
|
bgClass = "bg-red-50 dark:bg-red-900/10 border border-red-100 dark:border-red-800/30";
|
|
textClass = "text-red-600 dark:text-red-400";
|
|
}
|
|
|
|
return (
|
|
<div key={index} className={`${bgClass} rounded-xl p-6`}>
|
|
<div className="flex items-center gap-3 mb-3">
|
|
<div className="w-10 h-10 bg-white dark:bg-gray-800 rounded-lg flex items-center justify-center">
|
|
{getOverviewIcon(stat.icon)}
|
|
</div>
|
|
</div>
|
|
<div className="text-2xl font-bold text-gray-900 dark:text-white mb-1">{stat.value}</div>
|
|
<div className="text-sm font-medium text-gray-900 dark:text-white mb-1">{stat.label}</div>
|
|
<div className={`text-xs ${textClass}`}>{stat.subtext}</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
</AnimateSection>
|
|
|
|
|
|
|
|
<AnimateSection className="space-y-4" direction="left">
|
|
|
|
<div className={`relative ${!isConnectivityExpanded ? 'max-h-[60px] overflow-hidden' : ''}`}>
|
|
<p className="text-gray-700 dark:text-gray-300 leading-relaxed">
|
|
<span className="text-blue-500 mr-2">✦</span>
|
|
{property.overview.description || property.connectivity?.description || `${property.title} is located in ${property.location}.`}
|
|
</p>
|
|
|
|
{!isConnectivityExpanded && (
|
|
|
|
<div className="absolute bottom-0 left-0 right-0 h-10 bg-gradient-to-t from-white dark:from-gray-900 to-transparent"></div>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
<button
|
|
|
|
onClick={() => setIsConnectivityExpanded(!isConnectivityExpanded)}
|
|
|
|
className="text-blue-500 font-medium hover:text-blue-600 transition-colors mt-2 text-sm underline"
|
|
|
|
>
|
|
|
|
{isConnectivityExpanded ? "Read less" : "Read more"}
|
|
|
|
</button>
|
|
|
|
</AnimateSection>
|
|
|
|
|
|
|
|
{/* Info Cards Grid */}
|
|
|
|
<AnimateSection className="grid grid-cols-1 md:grid-cols-3 gap-6 mt-8" direction="right">
|
|
|
|
{/* RERA Card */}
|
|
|
|
<div className="bg-cyan-50 dark:bg-cyan-900/20 rounded-2xl p-6 flex flex-col items-center text-center border border-cyan-100 dark:border-cyan-800/30">
|
|
|
|
<div className="w-12 h-12 mb-4 relative">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className="w-12 h-12 text-blue-600">
|
|
|
|
<path fillRule="evenodd" d="M12.516 2.17a.75.75 0 00-1.032 0 11.209 11.209 0 01-7.877 3.08.75.75 0 00-.722.515A12.74 12.74 0 002.25 9.75c0 5.942 4.064 10.933 9.563 12.348a.749.749 0 00.374 0c5.499-1.415 9.563-6.406 9.563-12.348 0-1.352-.272-2.636-.759-3.808a.75.75 0 00-.722-.515 11.209 11.209 0 01-7.877-3.08zM12 13.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z" clipRule="evenodd" />
|
|
|
|
</svg>
|
|
|
|
<div className="absolute bottom-0 right-0 bg-white rounded-full p-0.5">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className="w-4 h-4 text-green-500">
|
|
|
|
<path fillRule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12zm13.36-1.814a.75.75 0 10-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 00-1.06 1.06l2.25 2.25a.75.75 0 001.14-.094l3.75-5.25z" clipRule="evenodd" />
|
|
|
|
</svg>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<h3 className="font-bold text-gray-900 dark:text-white mb-2">{property.title} is {property.reraInfo?.isApproved ? 'approved by RERA' : 'pending RERA approval'}</h3>
|
|
|
|
<p className="text-sm text-gray-500 dark:text-gray-400 mb-6">{property.reraInfo?.isApproved && `It was approved on ${property.reraInfo.approvalDate}`}</p>
|
|
|
|
<button className="w-full py-2.5 px-4 bg-white dark:bg-transparent border border-gray-300 dark:border-gray-600 rounded-lg text-sm font-semibold text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors mt-auto">
|
|
|
|
View RERA details
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{/* Brochure Card */}
|
|
|
|
<div className="bg-purple-50 dark:bg-purple-900/20 rounded-2xl p-6 flex flex-col items-center text-center border border-purple-100 dark:border-purple-800/30">
|
|
|
|
<div className="w-12 h-12 mb-4 text-purple-400">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className="w-12 h-12">
|
|
|
|
<path d="M5.625 1.5c-1.036 0-1.875.84-1.875 1.875v17.25c0 1.035.84 1.875 1.875 1.875h12.75c1.035 0 1.875-.84 1.875-1.875V12.75A3.75 3.75 0 0016.5 9h-1.875a1.875 1.875 0 01-1.875-1.875V5.25A3.75 3.75 0 009 1.5H5.625z" />
|
|
|
|
<path d="M12.971 1.816A5.23 5.23 0 0114.25 5.25v1.875c0 .207.168.375.375.375H16.5a5.23 5.23 0 013.434 1.279 9.768 9.768 0 00-6.963-6.963z" />
|
|
|
|
</svg>
|
|
|
|
</div>
|
|
|
|
<h3 className="font-bold text-gray-900 dark:text-white mb-2">{property.brochureCard?.title || `See ${property.title} brochure`}</h3>
|
|
|
|
<p className="text-sm text-gray-500 dark:text-gray-400 mb-6">{property.brochureCard?.description || "Download the detailed brochure"}</p>
|
|
|
|
<button className="w-full py-2.5 px-4 bg-white dark:bg-transparent border border-gray-300 dark:border-gray-600 rounded-lg text-sm font-semibold text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors mt-auto">
|
|
|
|
Download Brochure
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{/* Pros & Cons Card */}
|
|
|
|
<div className="bg-blue-50 dark:bg-blue-900/20 rounded-2xl p-6 flex flex-col items-center text-center border border-blue-100 dark:border-blue-800/30">
|
|
|
|
<div className="w-12 h-12 mb-4 text-purple-500 relative">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className="w-12 h-12">
|
|
|
|
<path d="M11.25 4.533A9.707 9.707 0 006 3.75a9.753 9.753 0 00-3.255.555.75.75 0 00-.575.69v9.12c0 .266.183.505.435.58 1.86.556 3.867.556 5.722 0 .253-.075.436-.314.436-.58v-9.12a.75.75 0 00-.575-.69zM12.75 4.533c.58.194 1.177.34 1.78.435v9.12c0 .266.183.505.435.58 1.86.556 3.867.556 5.722 0 .253-.075.436-.314.436-.58v-9.12a.75.75 0 00-.575-.69 9.753 9.753 0 00-3.255-.555 9.707 9.707 0 00-5.25.784v-9.12c0-.266.183-.505.435-.58a9.709 9.709 0 015.25-.784z" />
|
|
|
|
</svg>
|
|
|
|
<div className="absolute -bottom-1 -right-1">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className="w-6 h-6 text-gray-700">
|
|
|
|
<path d="M8.25 10.875a2.625 2.625 0 115.25 0 2.625 2.625 0 01-5.25 0z" />
|
|
|
|
<path fillRule="evenodd" d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25zm-1.125 4.5a4.125 4.125 0 102.338 7.524l2.007 2.006a.75.75 0 101.06-1.06l-2.006-2.007a4.125 4.125 0 00-3.399-6.463z" clipRule="evenodd" />
|
|
|
|
</svg>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<h3 className="font-bold text-gray-900 dark:text-white mb-2">{property.prosConsCard?.title || "See the truth beyond the brochures"}</h3>
|
|
|
|
<p className="text-sm text-gray-500 dark:text-gray-400 mb-6">{property.prosConsCard?.description || "See every risk & potential clearly with our experts"}</p>
|
|
|
|
<button className="w-full py-2.5 px-4 bg-blue-500 hover:bg-blue-600 text-white rounded-lg text-sm font-semibold transition-colors mt-auto shadow-md shadow-blue-500/20">
|
|
|
|
{property.prosConsCard?.buttonText || "Get Pros & Cons"}
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</AnimateSection>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Pricing Section - Moved and Redesigned */}
|
|
|
|
<AnimateSection id="pricing" className="bg-white dark:bg-gray-900 rounded-2xl p-8 shadow-sm border border-gray-200 dark:border-gray-800 scroll-mt-32" direction="right">
|
|
|
|
<h2 className="text-3xl font-bold text-foreground mb-2">Pricing</h2>
|
|
|
|
<p className="text-gray-500 dark:text-gray-400 mb-8">Last updated on {property.pricingUpdateDate || "Recently"}</p>
|
|
|
|
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-4 gap-4 md:gap-8">
|
|
<div>
|
|
<div className="text-sm text-gray-500 dark:text-gray-400 mb-1">Total Range</div>
|
|
<div className="text-2xl font-bold text-foreground whitespace-nowrap">{property.pricingDetails?.totalRange || property.price}</div>
|
|
</div>
|
|
<div>
|
|
<div className="flex items-center gap-1 text-sm text-gray-500 dark:text-gray-400 mb-1">
|
|
Monthly EMI
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="w-4 h-4 text-blue-500">
|
|
<path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clipRule="evenodd" />
|
|
</svg>
|
|
</div>
|
|
<div className="text-2xl font-bold text-foreground whitespace-nowrap">{property.pricingDetails?.emi || "N/A"}</div>
|
|
</div>
|
|
<div>
|
|
<div className="flex items-center gap-1 text-sm text-gray-500 dark:text-gray-400 mb-1">
|
|
Project's Avg.
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="w-4 h-4 text-blue-500">
|
|
<path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clipRule="evenodd" />
|
|
</svg>
|
|
</div>
|
|
<div className="text-2xl font-bold text-foreground">{property.pricingDetails?.projectAvg || "N/A"}</div>
|
|
</div>
|
|
<div>
|
|
<div className="flex items-center gap-1 text-sm text-gray-500 dark:text-gray-400 mb-1">
|
|
Market Avg.
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="w-4 h-4 text-blue-500">
|
|
<path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clipRule="evenodd" />
|
|
</svg>
|
|
</div>
|
|
<div className="text-2xl font-bold text-foreground">{property.pricingDetails?.marketAvg || "N/A"}</div>
|
|
</div>
|
|
</div>
|
|
|
|
</AnimateSection>
|
|
|
|
|
|
|
|
{/* Connectivity Section (Formerly Location) */}
|
|
|
|
<AnimateSection id="connectivity" className="bg-white dark:bg-gray-900 rounded-2xl p-8 shadow-sm border border-gray-200 dark:border-gray-800 scroll-mt-32" direction="left">
|
|
|
|
<h2 className="text-3xl font-bold text-foreground mb-2">Connectivity</h2>
|
|
|
|
<p className="text-gray-500 dark:text-gray-400 mb-6">{property.title}</p>
|
|
|
|
|
|
|
|
<div className="mb-8">
|
|
|
|
<div className={`relative ${!isConnectivityExpanded ? 'max-h-[60px] overflow-hidden' : ''}`}>
|
|
<p className="text-gray-700 dark:text-gray-300 leading-relaxed">
|
|
<span className="text-blue-500 mr-2">✦</span>
|
|
{property.connectivity?.description || `${property.title} is located in ${property.location}.`}
|
|
</p>
|
|
|
|
{!isConnectivityExpanded && (
|
|
|
|
<div className="absolute bottom-0 left-0 right-0 h-10 bg-gradient-to-t from-white dark:from-gray-900 to-transparent"></div>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
<button
|
|
|
|
onClick={() => setIsConnectivityExpanded(!isConnectivityExpanded)}
|
|
|
|
className="text-blue-500 font-medium hover:text-blue-600 transition-colors mt-2 text-sm underline"
|
|
|
|
>
|
|
|
|
{isConnectivityExpanded ? "Read less" : "Read more"}
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{/* Map Interface */}
|
|
|
|
<ConnectivityMap />
|
|
|
|
</AnimateSection>
|
|
|
|
|
|
|
|
{/* Master Plan Section */}
|
|
|
|
<AnimateSection id="master-plan" className="bg-white dark:bg-gray-900 rounded-2xl p-8 shadow-sm border border-gray-200 dark:border-gray-800 scroll-mt-32" direction="right">
|
|
|
|
<div className="flex flex-col md:flex-row justify-between items-start md:items-center mb-6 gap-4">
|
|
|
|
<div>
|
|
|
|
<h2 className="text-3xl font-bold text-foreground mb-2">Master Plan</h2>
|
|
|
|
<p className="text-gray-500 dark:text-gray-400">
|
|
{property.masterPlan?.description || "Master plan details unavailable."}
|
|
</p>
|
|
|
|
</div>
|
|
|
|
<button className="bg-orange-500 hover:bg-orange-600 text-white px-4 py-3 rounded-xl font-semibold text-base transition-colors shadow-md whitespace-nowrap md:ml-6">
|
|
Compare Sanction Plan
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{/* Master Plan Image */}
|
|
|
|
<div
|
|
|
|
className="relative rounded-xl overflow-hidden border border-gray-100 dark:border-gray-700 cursor-zoom-in group mb-8"
|
|
|
|
onClick={() => setIsLightboxOpen(true)}
|
|
|
|
>
|
|
|
|
<img
|
|
src={property.masterPlan?.image || "/assets/images/image.png"}
|
|
alt="Master Plan"
|
|
className="w-full h-auto object-cover hover:scale-105 transition-transform duration-500"
|
|
/>
|
|
|
|
<div className="absolute inset-0 bg-black/0 group-hover:bg-black/10 transition-colors flex items-center justify-center opacity-0 group-hover:opacity-100">
|
|
|
|
<span className="bg-white/90 text-gray-900 px-4 py-2 rounded-full text-sm font-medium shadow-lg backdrop-blur-sm">
|
|
|
|
Click to zoom
|
|
|
|
</span>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{/* Stats Grid */}
|
|
|
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 border-t border-gray-100 dark:border-gray-800 pt-8">
|
|
|
|
<div>
|
|
<div className="text-sm text-gray-500 dark:text-gray-400 mb-1">Total Units</div>
|
|
<div className="text-lg font-semibold text-foreground">{property.masterPlan?.totalUnits || "N/A"}</div>
|
|
</div>
|
|
|
|
<div>
|
|
<div className="text-sm text-gray-500 dark:text-gray-400 mb-1">Water Source</div>
|
|
<div className="flex items-center gap-2">
|
|
<span className="text-lg font-semibold text-foreground">{property.masterPlan?.waterSource || "N/A"}</span>
|
|
<span className="bg-orange-100 text-orange-600 text-xs px-2 py-0.5 rounded-full font-medium">+1</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<div className="text-sm text-gray-500 dark:text-gray-400 mb-1">Park Area</div>
|
|
<div className="text-lg font-semibold text-foreground">{property.masterPlan?.parkArea || "N/A"}</div>
|
|
</div>
|
|
|
|
<div>
|
|
<div className="text-sm text-gray-500 dark:text-gray-400 mb-1">Land type</div>
|
|
<div className="text-lg font-semibold text-foreground">{property.masterPlan?.landType || "N/A"}</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</AnimateSection>
|
|
|
|
|
|
|
|
{/* Sky & Soil Clarity Engine Section */}
|
|
|
|
<AnimateSection className="py-12" direction="up">
|
|
|
|
<div className="text-center mb-12">
|
|
|
|
<h2 className="text-3xl md:text-4xl font-bold text-foreground mb-3">The Sky & Soil Clarity Engine</h2>
|
|
|
|
<p className="text-gray-500 dark:text-gray-400 text-lg flex items-center justify-center gap-2">
|
|
|
|
300+ families found safer homes with Sky & Soil. You could be next
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
|
|
{/* Card 1: Compare */}
|
|
|
|
<div className="bg-orange-50 dark:bg-orange-900/10 rounded-2xl p-6 flex flex-col items-center text-center relative overflow-hidden group hover:shadow-lg transition-shadow duration-300">
|
|
|
|
{/* Decorative Circle */}
|
|
|
|
<div className="absolute -top-10 -right-10 w-32 h-32 bg-orange-100 dark:bg-orange-800/20 rounded-full blur-2xl"></div>
|
|
|
|
|
|
|
|
<div className="w-24 h-24 mb-6 relative z-10">
|
|
|
|
{/* House Icon Placeholder - Using SVG for 3D feel */}
|
|
|
|
<svg viewBox="0 0 200 200" className="w-full h-full drop-shadow-xl">
|
|
|
|
<path fill="#FDBA74" d="M100 20L20 90h20v90h40v-60h40v60h40V90h20L100 20z" />
|
|
|
|
<path fill="#FB923C" d="M120 180v-60H80v60H40V90H20L100 20l80 70h-20v90h-40z" opacity="0.5" />
|
|
|
|
<circle cx="140" cy="140" r="30" fill="#22C55E" />
|
|
|
|
<path fill="#FFF" d="M130 140l10 10 20-20" stroke="#FFF" strokeWidth="5" strokeLinecap="round" strokeLinejoin="round" />
|
|
|
|
</svg>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<h3 className="text-xl font-bold text-gray-900 dark:text-white mb-2">Like this project?</h3>
|
|
|
|
<h3 className="text-md font-bold text-gray-900 dark:text-white mb-4">Compare it with the rest</h3>
|
|
|
|
|
|
|
|
<p className="text-gray-600 dark:text-gray-400 mb-8 leading-relaxed">
|
|
|
|
Compare 2 properties side by side on 40+ parameters like connectivity, layout, price, specs and more
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<button className="w-full py-3 px-6 bg-transparent border-2 border-gray-900 dark:border-white rounded-lg text-sm font-bold text-gray-900 dark:text-white hover:bg-gray-900 hover:text-white dark:hover:bg-white dark:hover:text-gray-900 transition-colors mt-auto">
|
|
|
|
Compare With Peers
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{/* Card 2: Negotiate (Green) */}
|
|
|
|
<div className="bg-green-50 dark:bg-green-900/10 rounded-2xl p-6 pt-12 flex flex-col items-center text-center relative overflow-hidden group hover:shadow-lg transition-shadow duration-300 border-green-500">
|
|
|
|
{/* Trusted Badge */}
|
|
|
|
<div className="absolute top-0 left-0 right-0 bg-green-500 text-white text-xs font-bold py-1.5 uppercase tracking-wider">
|
|
{property.skyandsoilClarity?.familiesHelped}
|
|
</div>
|
|
|
|
|
|
|
|
{/* Sparkles */}
|
|
|
|
<div className="absolute top-10 left-4 text-green-300">
|
|
|
|
<svg className="w-8 h-8" fill="currentColor" viewBox="0 0 24 24"><path d="M12 2L14.5 9.5L22 12L14.5 14.5L12 22L9.5 14.5L2 12L9.5 9.5L12 2Z" /></svg>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className="w-24 h-24 mb-6 relative z-10">
|
|
|
|
{/* Trophy Icon */}
|
|
|
|
<svg viewBox="0 0 24 24" className="w-full h-full drop-shadow-xl text-green-500" fill="currentColor">
|
|
<path d="M20 2H4C2.9 2 2 2.9 2 4V6C2 8.21 3.79 10 6 10H6.17C6.58 11.86 7.84 13.42 9.5 14.24V16H8C6.9 16 6 16.9 6 18V20C6 21.1 6.9 22 8 22H16C17.1 22 18 21.1 18 20V18C18 16.9 17.1 16 16 16H14.5V14.24C16.16 13.42 17.42 11.86 17.83 10H18C20.21 10 22 8.21 22 6V4C22 2.9 21.1 2 20 2ZM6 8C4.9 8 4 7.1 4 6V4H6V8ZM18 8V4H20V6C20 7.1 19.1 8 18 8Z" />
|
|
</svg>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<h3 className="text-xl font-bold text-gray-900 dark:text-white mb-2">Pay less. Get more.</h3>
|
|
|
|
<h3 className="text-md font-bold text-gray-900 dark:text-white mb-4">Let us negotiate for you.</h3>
|
|
|
|
|
|
|
|
<p className="text-gray-600 dark:text-gray-400 mb-8 leading-relaxed">
|
|
|
|
Get a free Peace of Mind report, solid negotiations & a loyalty reward up to ₹3.29 Lakhs when you work with us
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<button className="w-full py-3 px-6 bg-green-500 hover:bg-green-600 text-white rounded-lg text-sm font-bold transition-colors mt-auto shadow-lg shadow-green-500/30">
|
|
|
|
Get Pros & Cons
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{/* Card 3: Report (Purple) */}
|
|
|
|
<div className="bg-purple-50 dark:bg-purple-900/10 rounded-2xl p-6 flex flex-col items-center text-center relative overflow-hidden group hover:shadow-lg transition-shadow duration-300">
|
|
|
|
{/* Decorative Triangle */}
|
|
|
|
<div className="absolute top-0 right-0 w-24 h-24 bg-purple-200 dark:bg-purple-800/30 transform rotate-45 translate-x-12 -translate-y-12"></div>
|
|
|
|
|
|
|
|
<div className="w-24 h-24 mb-6 relative z-10">
|
|
|
|
{/* Report Icon */}
|
|
|
|
<svg viewBox="0 0 200 200" className="w-full h-full drop-shadow-xl text-purple-500">
|
|
|
|
<path fill="#E9D5FF" d="M60 20h80l40 40v120H60V20z" />
|
|
|
|
<path fill="currentColor" d="M80 60h60v10H80zM80 90h60v10H80zM80 120h40v10H80z" />
|
|
|
|
<circle cx="140" cy="140" r="30" fill="#A855F7" />
|
|
|
|
<path fill="none" stroke="#FFF" strokeWidth="4" d="M130 140l5 5 15-15" />
|
|
|
|
</svg>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<h3 className="text-xl font-bold text-gray-900 dark:text-white mb-2">Almost convinced?</h3>
|
|
|
|
<h3 className="text-md font-bold text-gray-900 dark:text-white mb-4">See what we uncovered</h3>
|
|
|
|
|
|
|
|
<p className="text-gray-600 dark:text-gray-400 mb-8 leading-relaxed">
|
|
|
|
Ideal if you've already visited and close to deciding. Our Peace of Mind report shows you what builders won't.
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<button className="w-full py-3 px-6 bg-transparent border-2 border-gray-900 dark:border-white rounded-lg text-sm font-bold text-gray-900 dark:text-white hover:bg-gray-900 hover:text-white dark:hover:bg-white dark:hover:text-gray-900 transition-colors mt-auto">
|
|
|
|
See Sample Report
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</AnimateSection>
|
|
|
|
|
|
|
|
{/* Floor Plans Section */}
|
|
|
|
<AnimateSection id="floor-plans" className="bg-white dark:bg-gray-900 rounded-2xl p-8 shadow-sm border border-gray-200 dark:border-gray-800 scroll-mt-32" direction="right">
|
|
|
|
<h2 className="text-3xl font-bold text-foreground mb-2">Floor Plans</h2>
|
|
|
|
<p className="text-gray-500 dark:text-gray-400 mb-8">
|
|
|
|
{property.floorPlans && property.floorPlans.length > 0
|
|
? `${property.title} has configurations ranging from ${property.floorPlans[0].area} to ${property.floorPlans[property.floorPlans.length - 1].area}.`
|
|
: "Floor plans details coming soon."}
|
|
</p>
|
|
|
|
|
|
|
|
{/* Tabs */}
|
|
|
|
<div className="flex items-center gap-4 mb-6">
|
|
|
|
<div className="flex-1 overflow-x-auto flex gap-3 pb-2 scrollbar-hide">
|
|
|
|
{floorPlans.map((plan) => (
|
|
|
|
<button
|
|
|
|
key={plan.id}
|
|
|
|
onClick={() => setActiveFloorPlan(plan.id)}
|
|
|
|
className={`px-6 py-2 rounded-full text-sm font-medium whitespace-nowrap transition-all ${activeFloorPlan === plan.id
|
|
|
|
? "bg-orange-50 text-orange-500 border border-orange-200"
|
|
|
|
: "bg-white text-gray-600 border border-gray-200 hover:border-gray-300 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-700"
|
|
|
|
}`}
|
|
|
|
>
|
|
|
|
{plan.id}
|
|
|
|
</button>
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
<div className="flex gap-2">
|
|
|
|
<button
|
|
|
|
onClick={() => {
|
|
|
|
const currentIndex = floorPlans.findIndex(p => p.id === activeFloorPlan);
|
|
|
|
if (currentIndex > 0) {
|
|
|
|
setActiveFloorPlan(floorPlans[currentIndex - 1].id);
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
disabled={floorPlans.findIndex(p => p.id === activeFloorPlan) === 0}
|
|
|
|
className="p-2 rounded-full bg-gray-100 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
|
|
|
>
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="w-5 h-5 text-gray-600 dark:text-gray-400">
|
|
|
|
<path fillRule="evenodd" d="M12.79 5.23a.75.75 0 01-.02 1.06L8.832 10l3.938 3.71a.75.75 0 11-1.04 1.08l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 011.06.02z" clipRule="evenodd" />
|
|
|
|
</svg>
|
|
|
|
</button>
|
|
|
|
<button
|
|
|
|
onClick={() => {
|
|
|
|
const currentIndex = floorPlans.findIndex(p => p.id === activeFloorPlan);
|
|
|
|
if (currentIndex < floorPlans.length - 1) {
|
|
|
|
setActiveFloorPlan(floorPlans[currentIndex + 1].id);
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
disabled={floorPlans.findIndex(p => p.id === activeFloorPlan) === floorPlans.length - 1}
|
|
|
|
className="p-2 rounded-full bg-gray-100 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
|
|
|
>
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="w-5 h-5 text-gray-600 dark:text-gray-400">
|
|
|
|
<path fillRule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clipRule="evenodd" />
|
|
|
|
</svg>
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{/* Active Plan Details Card */}
|
|
|
|
{activePlanDetails && (
|
|
<div className="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl p-8 flex flex-col md:flex-row justify-between items-start md:items-center gap-6">
|
|
|
|
<div className="space-y-6 flex-1">
|
|
|
|
<div className="flex items-baseline gap-4">
|
|
|
|
<h3 className="text-3xl font-medium text-gray-900 dark:text-white">{activePlanDetails.id}</h3>
|
|
|
|
<span className="text-2xl font-bold text-gray-900 dark:text-white">{activePlanDetails.price}</span>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div>
|
|
|
|
<div className="text-sm text-gray-500 dark:text-gray-400 mb-1">Saleable Area</div>
|
|
|
|
<div className="text-lg font-medium text-gray-900 dark:text-white">{activePlanDetails.area}</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div>
|
|
|
|
<div className="text-sm text-gray-500 dark:text-gray-400 mb-1">Direction(s)</div>
|
|
|
|
<div className="text-lg font-medium text-gray-900 dark:text-white">{activePlanDetails.direction}</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
)}
|
|
|
|
</AnimateSection>
|
|
|
|
|
|
|
|
{/* Amenities Section */}
|
|
|
|
<AnimateSection id="amenities" className="bg-white dark:bg-gray-900 rounded-2xl p-8 shadow-sm border border-gray-200 dark:border-gray-800 scroll-mt-32" direction="left">
|
|
|
|
<h2 className="text-3xl font-bold text-foreground mb-2">Amenities</h2>
|
|
|
|
<p className="text-gray-500 dark:text-gray-400 mb-8">{property.title}</p>
|
|
|
|
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-12">
|
|
|
|
{/* Lifestyle */}
|
|
|
|
<div>
|
|
|
|
<h3 className="text-xl font-medium text-gray-900 dark:text-white mb-6">Lifestyle Amenities</h3>
|
|
|
|
<ul className="space-y-4">
|
|
|
|
{property.detailedAmenities?.Lifestyle?.map((item, idx) => (
|
|
|
|
<li key={idx} className={`flex items-center gap-3 ${!item.available ? 'opacity-40 grayscale' : ''}`}>
|
|
|
|
<div className={`w-6 h-6 ${item.available ? 'text-purple-600' : 'text-gray-400'}`}>
|
|
|
|
{/* Icon rendering logic can be improved here if needed, for now assuming icons are handled or passed */}
|
|
{getOverviewIcon(item.name.toLowerCase().replace(" ", "")) || (
|
|
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
|
</svg>
|
|
)}
|
|
|
|
</div>
|
|
|
|
<span className={`text-base ${!item.available ? 'text-gray-400 line-through decoration-gray-400' : 'text-purple-600 font-medium'}`}>
|
|
|
|
{item.name}
|
|
|
|
</span>
|
|
|
|
</li>
|
|
|
|
))}
|
|
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{/* Sports */}
|
|
|
|
<div>
|
|
|
|
<h3 className="text-xl font-medium text-gray-900 dark:text-white mb-6">Sports Amenities</h3>
|
|
|
|
<ul className="space-y-4">
|
|
|
|
{property.detailedAmenities?.Sports?.map((item, idx) => (
|
|
|
|
<li key={idx} className={`flex items-center gap-3 ${!item.available ? 'opacity-40 grayscale' : ''}`}>
|
|
|
|
<div className={`w-6 h-6 ${item.available ? 'text-purple-600' : 'text-gray-400'}`}>
|
|
|
|
{/* Icon rendering logic can be improved here if needed, for now assuming icons are handled or passed */}
|
|
{getOverviewIcon(item.name.toLowerCase().replace(" ", "")) || (
|
|
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
|
</svg>
|
|
)}
|
|
|
|
</div>
|
|
|
|
<span className={`text-base ${!item.available ? 'text-gray-400 line-through decoration-gray-400' : 'text-purple-600 font-medium'}`}>
|
|
|
|
{item.name}
|
|
|
|
</span>
|
|
|
|
</li>
|
|
|
|
))}
|
|
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{/* Natural */}
|
|
|
|
<div>
|
|
|
|
<h3 className="text-xl font-medium text-gray-900 dark:text-white mb-6">Natural Amenities</h3>
|
|
|
|
<ul className="space-y-4">
|
|
|
|
{property.detailedAmenities?.Natural?.map((item, idx) => (
|
|
|
|
<li key={idx} className={`flex items-center gap-3 ${!item.available ? 'opacity-40 grayscale' : ''}`}>
|
|
|
|
<div className={`w-6 h-6 ${item.available ? 'text-purple-600' : 'text-gray-400'}`}>
|
|
|
|
{getOverviewIcon(item.name.toLowerCase().replace(" ", "")) || (
|
|
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
|
</svg>
|
|
)}
|
|
|
|
</div>
|
|
|
|
<span className={`text-base ${!item.available ? 'text-gray-400 line-through decoration-gray-400' : 'text-purple-600 font-medium'}`}>
|
|
|
|
{item.name}
|
|
|
|
</span>
|
|
|
|
</li>
|
|
|
|
))}
|
|
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<button
|
|
|
|
onClick={handleSeeAllAmenities}
|
|
|
|
disabled={isAmenitiesLoading}
|
|
|
|
className="mt-10 px-6 py-3 border border-gray-300 dark:border-gray-600 rounded-lg text-sm font-bold text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors min-w-[160px] flex items-center justify-center"
|
|
|
|
>
|
|
|
|
{isAmenitiesLoading ? (
|
|
|
|
<span className="flex items-center gap-2">
|
|
|
|
<svg className="animate-spin h-4 w-4 text-gray-700 dark:text-gray-300" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
|
|
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
|
|
|
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
|
|
</svg>
|
|
|
|
Loading...
|
|
|
|
</span>
|
|
|
|
) : (
|
|
|
|
"See all amenities"
|
|
|
|
)}
|
|
|
|
</button>
|
|
|
|
</AnimateSection>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Approvals Section */}
|
|
|
|
<AnimateSection id="approvals" className="bg-white dark:bg-gray-900 rounded-2xl p-8 shadow-sm border border-gray-200 dark:border-gray-800 scroll-mt-32" direction="right">
|
|
|
|
<h2 className="text-3xl font-bold text-foreground mb-2">Approvals</h2>
|
|
|
|
<p className="text-gray-500 dark:text-gray-400 mb-8">
|
|
|
|
{property.title} by <span className="text-primary font-medium">Modern Spaaces</span> has received 4 out of 4 important approvals as per RERA
|
|
|
|
</p>
|
|
|
|
|
|
|
|
{/* Approvals List - Two Column Grid */}
|
|
|
|
<ul className="grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-3 mb-12">
|
|
{property.approvals?.map((approval, index) => (
|
|
<li key={index} className="flex items-start gap-3">
|
|
<span className="inline-block w-1.5 h-1.5 rounded-full bg-gray-700 dark:bg-gray-300 mt-2.5 flex-shrink-0"></span>
|
|
<span className="text-gray-900 dark:text-white font-medium">{approval.name}</span>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
|
|
|
|
|
|
{/* Documents Subsection */}
|
|
|
|
<h3 className="text-2xl font-bold text-foreground mb-2">Documents</h3>
|
|
|
|
<p className="text-gray-500 dark:text-gray-400 mb-8">
|
|
Curated documents from various sources for {property.title} by {property.builder?.name || "Builder"}
|
|
</p>
|
|
|
|
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
{property.documents?.map((doc, index) => (
|
|
<a
|
|
key={index}
|
|
href={doc.pdfUrl || "#"}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="bg-blue-50 dark:bg-blue-900/10 rounded-xl p-6 flex flex-col h-full relative group hover:shadow-md transition-all cursor-pointer"
|
|
>
|
|
<div className="absolute top-6 right-6 text-gray-400">
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor" className="w-4 h-4">
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" />
|
|
</svg>
|
|
</div>
|
|
<div className="w-12 h-12 mb-4 text-gray-700 dark:text-gray-300">
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-10 h-10">
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" />
|
|
</svg>
|
|
</div>
|
|
<h4 className="font-bold text-gray-900 dark:text-white mb-2">{doc.title}</h4>
|
|
<p className="text-sm text-gray-500 dark:text-gray-400 mb-8 flex-grow leading-relaxed">
|
|
{doc.description}
|
|
</p>
|
|
<span className="text-sm font-bold text-gray-900 dark:text-white hover:underline text-left inline-block">
|
|
See Document
|
|
</span>
|
|
</a>
|
|
))}
|
|
</div>
|
|
|
|
</AnimateSection>
|
|
|
|
|
|
|
|
{/* About the Builder Section */}
|
|
|
|
<AnimateSection id="builder" className="bg-white dark:bg-gray-900 rounded-2xl p-8 shadow-sm border border-gray-200 dark:border-gray-800 scroll-mt-32" direction="left">
|
|
|
|
<h2 className="text-3xl font-bold text-foreground mb-2">About the builder</h2>
|
|
|
|
<p className="text-gray-500 dark:text-gray-400 mb-8">{property.builder?.name || "Builder"}</p>
|
|
|
|
|
|
|
|
<div className="flex flex-col md:flex-row gap-12">
|
|
|
|
<div className="flex-1">
|
|
|
|
<p className="text-gray-600 dark:text-gray-300 leading-relaxed mb-8">
|
|
{property.builder?.description}
|
|
</p>
|
|
|
|
<Link href={`/buy/property-for-sale-in-bengaluru?search=${encodeURIComponent(property.builder?.name || "")}`} className="text-blue-600 dark:text-blue-400 font-bold hover:underline">
|
|
|
|
See all properties by {property.builder?.name || "this builder"}
|
|
|
|
</Link>
|
|
|
|
</div>
|
|
|
|
<div className="w-full md:w-1/3 space-y-8">
|
|
|
|
<div>
|
|
|
|
<h4 className="text-sm text-gray-500 dark:text-gray-400 mb-1">Established On</h4>
|
|
|
|
<p className="text-xl font-bold text-gray-900 dark:text-white">{property.builder?.establishedYear || "N/A"}</p>
|
|
|
|
</div>
|
|
|
|
<div>
|
|
|
|
<h4 className="text-sm text-gray-500 dark:text-gray-400 mb-1">Completed Projects</h4>
|
|
|
|
<p className="text-xl font-bold text-gray-900 dark:text-white">{property.builder?.completedProjects || "N/A"}</p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</AnimateSection>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{/* Locality Section */}
|
|
|
|
<AnimateSection id="locality" className="bg-white dark:bg-gray-900 rounded-2xl p-8 shadow-sm border border-gray-200 dark:border-gray-800 scroll-mt-32" direction="right">
|
|
|
|
<h2 className="text-3xl font-bold text-foreground mb-2">Locality</h2>
|
|
|
|
<p className="text-gray-500 dark:text-gray-400 mb-8">{property.locality?.name || "Bangalore"}</p>
|
|
|
|
|
|
|
|
{/* Large Image */}
|
|
|
|
<div className="relative rounded-2xl overflow-hidden mb-8 h-[300px] group">
|
|
|
|
<img
|
|
|
|
src={property.locality?.mapImage || "/assets/images/map-placeholder.webp"}
|
|
|
|
alt={`Map of ${property.locality?.name} Locality`}
|
|
|
|
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105"
|
|
|
|
/>
|
|
|
|
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/20 to-transparent flex flex-col justify-end p-8">
|
|
|
|
<h3 className="text-3xl font-bold text-white">{property.locality?.name || "Locality"}</h3>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<p className="text-gray-600 dark:text-gray-300 leading-relaxed mb-12">
|
|
{property.locality?.description}
|
|
</p>
|
|
|
|
|
|
|
|
{/* Gauges */}
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 mb-4">
|
|
|
|
{/* Developing */}
|
|
|
|
<div className="flex flex-col items-center text-center">
|
|
|
|
<div className="relative w-48 h-24 mb-4 overflow-hidden">
|
|
|
|
<div className="absolute top-0 left-0 w-full h-full bg-orange-100 rounded-t-full"></div>
|
|
|
|
<div className="absolute top-0 left-0 w-full h-full bg-gradient-to-r from-orange-200 to-orange-500 rounded-t-full origin-bottom transform rotate-[-45deg]" style={{ clipPath: 'polygon(0 0, 100% 0, 100% 100%, 0 100%)' }}></div>
|
|
|
|
<div className="absolute bottom-0 left-1/2 w-1 h-12 bg-orange-600 origin-bottom transform -translate-x-1/2 rotate-[-45deg]"></div>
|
|
|
|
</div>
|
|
|
|
<h4 className="text-xl font-bold text-gray-900 dark:text-white">Developing</h4>
|
|
|
|
<p className="text-sm text-gray-500 dark:text-gray-400">Living Experience</p>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{/* Medium */}
|
|
|
|
<div className="flex flex-col items-center text-center">
|
|
|
|
<div className="relative w-48 h-24 mb-4 overflow-hidden">
|
|
|
|
<div className="absolute top-0 left-0 w-full h-full bg-orange-100 rounded-t-full"></div>
|
|
|
|
<div className="absolute top-0 left-0 w-full h-full bg-gradient-to-r from-orange-200 to-orange-500 rounded-t-full origin-bottom transform rotate-[0deg]" style={{ clipPath: 'polygon(0 0, 100% 0, 100% 100%, 0 100%)' }}></div>
|
|
|
|
<div className="absolute bottom-0 left-1/2 w-1 h-12 bg-orange-600 origin-bottom transform -translate-x-1/2 rotate-[0deg]"></div>
|
|
|
|
</div>
|
|
|
|
<h4 className="text-xl font-bold text-gray-900 dark:text-white">Medium</h4>
|
|
|
|
<p className="text-sm text-gray-500 dark:text-gray-400">Investment Potential</p>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{/* Moderate */}
|
|
|
|
<div className="flex flex-col items-center text-center">
|
|
|
|
<div className="relative w-48 h-24 mb-4 overflow-hidden">
|
|
|
|
<div className="absolute top-0 left-0 w-full h-full bg-orange-100 rounded-t-full"></div>
|
|
|
|
<div className="absolute top-0 left-0 w-full h-full bg-gradient-to-r from-orange-200 to-orange-500 rounded-t-full origin-bottom transform rotate-[45deg]" style={{ clipPath: 'polygon(0 0, 100% 0, 100% 100%, 0 100%)' }}></div>
|
|
|
|
<div className="absolute bottom-0 left-1/2 w-1 h-12 bg-orange-600 origin-bottom transform -translate-x-1/2 rotate-[45deg]"></div>
|
|
|
|
</div>
|
|
|
|
<h4 className="text-xl font-bold text-gray-900 dark:text-white">Moderate</h4>
|
|
|
|
<p className="text-sm text-gray-500 dark:text-gray-400">Price Range</p>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
{/* Checkout Nearby Properties Section */}
|
|
|
|
<AnimateSection id="nearby" className="bg-white dark:bg-gray-900 rounded-2xl p-8 shadow-sm border border-gray-200 dark:border-gray-800 scroll-mt-32" direction="left">
|
|
|
|
<h2 className="text-3xl font-bold text-foreground mb-2">Checkout Nearby Properties</h2>
|
|
|
|
<p className="text-gray-500 dark:text-gray-400 mb-8">
|
|
|
|
See properties similar to {property.title} by {property.builder?.name || "Builder"} in the same neighbourhood
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 mb-8">
|
|
|
|
{/* Property 1 */}
|
|
|
|
<div className="group cursor-pointer">
|
|
|
|
<div className="rounded-xl overflow-hidden mb-4 h-48">
|
|
|
|
<img src="/assets/images/map-placeholder.webp" alt="Nearby property Prestige Raintree Park" className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110" />
|
|
|
|
</div>
|
|
|
|
<h4 className="text-lg font-bold text-gray-900 dark:text-white mb-1">Prestige Raintree Park</h4>
|
|
|
|
<p className="text-sm text-gray-500 dark:text-gray-400 mb-1">3.07 Crores - 5.67 Crores</p>
|
|
|
|
<p className="text-sm text-gray-500 dark:text-gray-400 mb-4">Varthur</p>
|
|
|
|
<button className="text-sm font-medium text-gray-500 dark:text-gray-400 underline hover:text-gray-900 dark:hover:text-white transition-colors">
|
|
|
|
Compare with this property
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{/* Property 2 */}
|
|
|
|
<div className="group cursor-pointer">
|
|
|
|
<div className="rounded-xl overflow-hidden mb-4 h-48">
|
|
|
|
<img src="/assets/images/image.png" alt="Nearby property Adarsh Park Heights" className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110" />
|
|
|
|
</div>
|
|
|
|
<h4 className="text-lg font-bold text-gray-900 dark:text-white mb-1">Adarsh Park Heights</h4>
|
|
|
|
<p className="text-sm text-gray-500 dark:text-gray-400 mb-1">1.63 Crores - 2.32 Crores</p>
|
|
|
|
<p className="text-sm text-gray-500 dark:text-gray-400 mb-4">Varthur</p>
|
|
|
|
<button className="text-sm font-medium text-gray-500 dark:text-gray-400 underline hover:text-gray-900 dark:hover:text-white transition-colors">
|
|
|
|
Compare with this property
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{/* Property 3 */}
|
|
|
|
<div className="group cursor-pointer">
|
|
|
|
<div className="rounded-xl overflow-hidden mb-4 h-48">
|
|
|
|
<img src="/assets/images/map-placeholder.webp" alt="Nearby property Tru Aquapolis" className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110" />
|
|
|
|
</div>
|
|
|
|
<h4 className="text-lg font-bold text-gray-900 dark:text-white mb-1">Tru Aquapolis</h4>
|
|
|
|
<p className="text-sm text-gray-500 dark:text-gray-400 mb-1">2.22 Crores - 3.51 Crores</p>
|
|
|
|
<p className="text-sm text-gray-500 dark:text-gray-400 mb-4">Varthur</p>
|
|
|
|
<button className="text-sm font-medium text-gray-500 dark:text-gray-400 underline hover:text-gray-900 dark:hover:text-white transition-colors">
|
|
|
|
Compare with this property
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<a href="#" className="text-blue-600 dark:text-blue-400 font-bold hover:underline">
|
|
|
|
See all properties in Varthur
|
|
|
|
</a>
|
|
|
|
</AnimateSection>
|
|
|
|
</AnimateSection>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{/* FAQ Section */}
|
|
|
|
<AnimateSection id="faq" className="bg-white dark:bg-gray-900 rounded-2xl p-4 md:p-8 shadow-sm border border-gray-200 dark:border-gray-800 scroll-mt-32" direction="up">
|
|
|
|
<h2 className="text-xl md:text-3xl font-bold text-foreground mb-2">Frequently Asked Questions</h2>
|
|
|
|
<p className="text-gray-500 dark:text-gray-400 mb-8">
|
|
|
|
99% of your queries should get answered here, for others, you can always talk to us
|
|
|
|
</p>
|
|
|
|
|
|
|
|
{/* Tabs */}
|
|
|
|
<div className="bg-gray-100 dark:bg-gray-800 p-1 rounded-lg flex flex-wrap mb-8">
|
|
|
|
{Object.keys(faqData).map((tab) => (
|
|
|
|
<button
|
|
|
|
key={tab}
|
|
|
|
onClick={() => setActiveFaqTab(tab)}
|
|
|
|
className={`flex-1 py-3 px-4 text-sm font-medium rounded-md transition-all ${activeFaqTab === tab
|
|
|
|
? 'bg-white dark:bg-gray-700 text-orange-500 shadow-sm'
|
|
|
|
: 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200'
|
|
|
|
}`}
|
|
|
|
>
|
|
|
|
{tab}
|
|
|
|
</button>
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{/* Accordion List */}
|
|
|
|
<div className="space-y-4">
|
|
|
|
{faqData[activeFaqTab]?.map((item, index) => (
|
|
|
|
<div key={index} className="border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden">
|
|
|
|
<button
|
|
onClick={() => setExpandedFaq(expandedFaq === index ? null : index)}
|
|
className="w-full flex items-center justify-between p-3 md:p-6 text-left bg-white dark:bg-gray-800
|
|
hover:bg-gray-50 dark:hover:bg-gray-750 transition-colors group"
|
|
>
|
|
<span className="font-medium text-gray-900 dark:text-white pr-8 group-hover:text-blue-600">
|
|
{item.question}
|
|
</span>
|
|
|
|
<span className="text-gray-400 group-hover:text-blue-600">
|
|
{expandedFaq === index ? (
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
|
strokeWidth={1.5} stroke="currentColor" className="w-6 h-6">
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M19.5 12h-15" />
|
|
</svg>
|
|
) : (
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
|
strokeWidth={1.5} stroke="currentColor" className="w-6 h-6">
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
|
|
</svg>
|
|
)}
|
|
</span>
|
|
</button>
|
|
|
|
|
|
{expandedFaq === index && (
|
|
|
|
<div className="p-3 pt-0 md:p-6 md:pt-0 bg-white dark:bg-gray-800 text-gray-600 dark:text-gray-300 leading-relaxed border-t border-gray-100 dark:border-gray-700">
|
|
|
|
{item.answer}
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
</AnimateSection>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{/* Sidebar */}
|
|
|
|
<div className="lg:col-span-1">
|
|
|
|
<div className="sticky top-32 space-y-6">
|
|
|
|
{/* Contact Form */}
|
|
|
|
<AnimateSection direction="up" className="bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 rounded-2xl p-6 shadow-lg">
|
|
|
|
<h3 className="text-xl font-bold text-foreground mb-4">Get in Touch</h3>
|
|
|
|
|
|
|
|
{alert.show && (
|
|
|
|
<div className={`mb-4 p-3 rounded-lg text-sm font-medium ${alert.type === 'success' ? 'bg-green-50 text-green-700 border border-green-200' : 'bg-red-50 text-red-700 border border-red-200'}`}>
|
|
|
|
{alert.message}
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
|
|
|
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
|
|
<div>
|
|
|
|
<input
|
|
|
|
type="text"
|
|
|
|
name="name"
|
|
|
|
value={formData.name}
|
|
|
|
onChange={handleChange}
|
|
|
|
placeholder="Your Name"
|
|
|
|
className={`w-full px-4 py-3 border ${formErrors.name ? 'border-red-500' : 'border-gray-300 dark:border-gray-700'} rounded-lg focus:ring-2 focus:ring-primary focus:border-transparent bg-white dark:bg-gray-800 text-foreground transition-all`}
|
|
|
|
/>
|
|
|
|
{formErrors.name && <small className="text-red-500 text-xs ml-1">{formErrors.name}</small>}
|
|
|
|
</div>
|
|
|
|
<div>
|
|
|
|
<input
|
|
|
|
type="email"
|
|
|
|
name="email"
|
|
|
|
value={formData.email}
|
|
|
|
onChange={handleChange}
|
|
|
|
placeholder="Email Address"
|
|
|
|
className={`w-full px-4 py-3 border ${formErrors.email ? 'border-red-500' : 'border-gray-300 dark:border-gray-700'} rounded-lg focus:ring-2 focus:ring-primary focus:border-transparent bg-white dark:bg-gray-800 text-foreground transition-all`}
|
|
|
|
/>
|
|
|
|
{formErrors.email && <small className="text-red-500 text-xs ml-1">{formErrors.email}</small>}
|
|
|
|
</div>
|
|
|
|
<div>
|
|
|
|
<input
|
|
|
|
type="tel"
|
|
|
|
name="phone"
|
|
|
|
value={formData.phone}
|
|
|
|
onChange={handleChange}
|
|
|
|
placeholder="Phone Number"
|
|
|
|
className={`w-full px-4 py-3 border ${formErrors.phone ? 'border-red-500' : 'border-gray-300 dark:border-gray-700'} rounded-lg focus:ring-2 focus:ring-primary focus:border-transparent bg-white dark:bg-gray-800 text-foreground transition-all`}
|
|
|
|
/>
|
|
|
|
{formErrors.phone && <small className="text-red-500 text-xs ml-1">{formErrors.phone}</small>}
|
|
|
|
</div>
|
|
|
|
<textarea
|
|
|
|
rows={4}
|
|
|
|
name="message"
|
|
|
|
value={formData.message}
|
|
|
|
onChange={handleChange}
|
|
|
|
placeholder="Message (Optional)"
|
|
|
|
className="w-full px-4 py-3 border border-gray-300 dark:border-gray-700 rounded-lg focus:ring-2 focus:ring-primary focus:border-transparent bg-white dark:bg-gray-800 text-foreground transition-all"
|
|
|
|
/>
|
|
|
|
<button
|
|
|
|
type="submit"
|
|
|
|
disabled={isSubmitting}
|
|
|
|
className={`w-full bg-gradient-to-r from-primary to-blue-600 text-white py-3 rounded-lg font-semibold hover:shadow-lg transition-all transform hover:scale-105 ${isSubmitting ? 'opacity-70 cursor-not-allowed' : ''}`}
|
|
|
|
>
|
|
|
|
{isSubmitting ? 'Sending...' : 'Request Callback'}
|
|
|
|
</button>
|
|
|
|
</form>
|
|
|
|
</AnimateSection>
|
|
|
|
|
|
|
|
{/* Quick Actions */}
|
|
|
|
<div className="bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 rounded-2xl p-6 border border-gray-200 dark:border-gray-700">
|
|
|
|
<h3 className="text-lg font-bold text-foreground mb-4">Quick Actions</h3>
|
|
|
|
<div className="space-y-3">
|
|
|
|
<button
|
|
|
|
onClick={handleShare}
|
|
|
|
className="w-full flex items-center justify-center gap-2 px-4 py-3 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-lg hover:shadow-md transition-all text-foreground"
|
|
|
|
>
|
|
|
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z" />
|
|
|
|
</svg>
|
|
|
|
Share Property
|
|
|
|
</button>
|
|
|
|
<button
|
|
|
|
onClick={handleWishlist}
|
|
|
|
className={`w-full flex items-center justify-center gap-2 px-4 py-3 border rounded-lg hover:shadow-md transition-all ${isWishlisted
|
|
|
|
? 'bg-red-50 border-red-200 text-red-600'
|
|
|
|
: 'bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-700 text-foreground'
|
|
|
|
}`}
|
|
|
|
>
|
|
|
|
<svg
|
|
|
|
className={`w-5 h-5 ${isWishlisted ? 'fill-current' : 'fill-none'}`}
|
|
|
|
stroke="currentColor"
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
>
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
|
|
|
|
</svg>
|
|
|
|
{isWishlisted ? 'Saved to Wishlist' : 'Save to Wishlist'}
|
|
|
|
</button>
|
|
|
|
<button
|
|
|
|
onClick={handlePrint}
|
|
|
|
className="w-full flex items-center justify-center gap-2 px-4 py-3 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-lg hover:shadow-md transition-all text-foreground"
|
|
|
|
>
|
|
|
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z" />
|
|
|
|
</svg>
|
|
|
|
Print Details
|
|
|
|
</button>
|
|
|
|
<button
|
|
|
|
onClick={handleCompareToggle}
|
|
|
|
className={`w-full flex items-center justify-center gap-2 px-4 py-3 border rounded-lg hover:shadow-md transition-all ${isCompared
|
|
|
|
? 'bg-blue-50 border-blue-200 text-blue-600'
|
|
|
|
: 'bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-700 text-foreground'
|
|
|
|
}`}
|
|
|
|
>
|
|
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
className="w-5 h-5"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
>
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
d="M8 7h8v10H8zM4 11h8v10H4z"
|
|
/>
|
|
</svg>
|
|
|
|
{isCompared ? 'Added to Compare' : 'Add to Compare'}
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div >
|
|
|
|
|
|
|
|
|
|
|
|
<Footer />
|
|
|
|
{/* Lightbox */}
|
|
|
|
{
|
|
|
|
isLightboxOpen && (
|
|
|
|
<div
|
|
|
|
className="fixed inset-0 z-[100] bg-black/90 backdrop-blur-sm flex items-center justify-center p-4 animate-in fade-in duration-200"
|
|
|
|
onClick={() => setIsLightboxOpen(false)}
|
|
|
|
>
|
|
|
|
<button
|
|
|
|
className="absolute top-4 right-4 text-white/70 hover:text-white p-2 rounded-full hover:bg-white/10 transition-colors"
|
|
|
|
onClick={() => setIsLightboxOpen(false)}
|
|
|
|
>
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor" className="w-8 h-8">
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
|
|
</svg>
|
|
|
|
</button>
|
|
|
|
<img
|
|
|
|
src="/assets/images/image.png"
|
|
|
|
alt="Master Plan Full View"
|
|
|
|
className="max-w-full max-h-[90vh] object-contain rounded-lg shadow-2xl scale-in-95 duration-200"
|
|
|
|
onClick={(e) => e.stopPropagation()}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
{/* Amenities Modal */}
|
|
|
|
{
|
|
|
|
isAmenitiesModalOpen && (
|
|
|
|
<div
|
|
|
|
className="fixed inset-0 z-[100] bg-black/50 backdrop-blur-sm flex items-center justify-center p-4 animate-in fade-in duration-200"
|
|
|
|
onClick={() => setIsAmenitiesModalOpen(false)}
|
|
|
|
>
|
|
|
|
<div
|
|
|
|
className="bg-white dark:bg-gray-900 w-full max-w-4xl max-h-[90vh] rounded-2xl shadow-2xl overflow-hidden flex flex-col scale-in-95 duration-200"
|
|
|
|
onClick={(e) => e.stopPropagation()}
|
|
|
|
>
|
|
|
|
{/* Header */}
|
|
|
|
<div className="p-6 border-b border-gray-100 dark:border-gray-800 flex justify-between items-center">
|
|
|
|
<div>
|
|
|
|
<h2 className="text-2xl font-bold text-gray-900 dark:text-white">Amenities</h2>
|
|
|
|
<p className="text-sm text-gray-500 dark:text-gray-400">Lifestyle</p>
|
|
|
|
</div>
|
|
|
|
<button
|
|
|
|
onClick={() => setIsAmenitiesModalOpen(false)}
|
|
|
|
className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 transition-colors"
|
|
|
|
>
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor" className="w-6 h-6">
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
|
|
</svg>
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{/* Content */}
|
|
|
|
<div className="p-8 overflow-y-auto">
|
|
|
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-8">
|
|
|
|
{property.detailedAmenities && Object.entries(property.detailedAmenities).flatMap(([category, items]) =>
|
|
|
|
items.map((item, idx) => (
|
|
|
|
<div key={`${category}-${idx}`} className="flex flex-col items-center text-center gap-3">
|
|
|
|
<div className="relative">
|
|
|
|
<div className={`w-12 h-12 ${item.available ? 'text-purple-600' : 'text-gray-300'} mb-1`}>
|
|
|
|
{getOverviewIcon(item.name.toLowerCase().replace(" ", "")) || (
|
|
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
|
</svg>
|
|
)}
|
|
|
|
</div>
|
|
|
|
{(item as any).rare && (
|
|
|
|
<span className="absolute -top-2 -right-4 bg-purple-100 text-purple-600 text-[10px] font-bold px-1.5 py-0.5 rounded uppercase tracking-wider">
|
|
|
|
Rare
|
|
|
|
</span>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
<span className={`text-sm ${item.available ? 'text-gray-700 dark:text-gray-300' : 'text-gray-400 line-through'}`}>
|
|
|
|
{item.name}
|
|
|
|
</span>
|
|
|
|
</div>
|
|
|
|
))
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
</div >
|
|
|
|
);
|
|
|
|
}
|