loading issue fixed new structure updayed

This commit is contained in:
akash 2025-12-11 23:01:02 +05:30
parent 5bb4efd6b8
commit 2e2b776085
17 changed files with 354 additions and 202 deletions

View File

@ -1,4 +1,3 @@
import Header from "@/components/Header";
import InnerBanner from "@/components/InnerBanner";
import About from "@/components/About";
import WhyChooseUs from "@/components/WhyChooseUs";
@ -14,7 +13,6 @@ export const metadata: Metadata = {
export default function AboutPage() {
return (
<main className="min-h-screen bg-white dark:bg-black">
<Header />
<InnerBanner
title="About Us"
subtitle="Discover our journey in creating exceptional living spaces"

View File

@ -0,0 +1,16 @@
import BengaluruPropertiesClientWrapper from "@/components/BengaluruPropertiesClientWrapper";
import { Metadata } from "next";
import { Suspense } from "react";
export const metadata: Metadata = {
title: "Property for Sale in Bengaluru | Sky and Soil",
description: "Browse our exclusive collection of premium apartments, villas, and plots in Bengaluru.",
};
export default function PropertyForSalePage() {
return (
<Suspense fallback={<div className="min-h-screen bg-gray-50 flex items-center justify-center">Loading...</div>}>
<BengaluruPropertiesClientWrapper />
</Suspense>
);
}

View File

@ -1,4 +1,3 @@
import Header from "@/components/Header";
import InnerBanner from "@/components/InnerBanner";
import ContactCTA from "@/components/ContactCTA";
import Footer from "@/components/Footer";
@ -12,7 +11,6 @@ export const metadata: Metadata = {
export default function ContactPage() {
return (
<main className="min-h-screen bg-white dark:bg-black">
<Header />
<InnerBanner
title="Contact Us"
subtitle="Get in touch with our team for any inquiries"

View File

@ -33,6 +33,7 @@ import { ThemeProvider } from "@/components/ThemeProvider";
import { CompareProvider } from "@/context/CompareContext";
import CompareBar from "@/components/CompareBar";
import MouseAnimation from "@/components/MouseAnimation";
import HeaderWrapper from "@/components/HeaderWrapper";
export default function RootLayout({
children,
@ -59,6 +60,7 @@ export default function RootLayout({
disableTransitionOnChange
>
<CompareProvider>
<HeaderWrapper />
{children}
<CompareBar />
{/* <MouseAnimation /> */}

View File

@ -1,4 +1,3 @@
import Header from "@/components/Header";
import InnerBanner from "@/components/InnerBanner";
import Lifestyle from "@/components/Lifestyle";
import Footer from "@/components/Footer";
@ -12,7 +11,6 @@ export const metadata: Metadata = {
export default function LifestylePage() {
return (
<main className="min-h-screen bg-white dark:bg-black">
<Header />
<InnerBanner
title="Lifestyle"
subtitle="Experience the perfect blend of comfort and luxury"

View File

@ -1,4 +1,3 @@
import Header from "@/components/Header";
import Hero from "@/components/Hero";
import About from "@/components/About";
import WhyChooseUs from "@/components/WhyChooseUs";
@ -19,7 +18,6 @@ export const metadata: Metadata = {
export default function Home() {
return (
<main className="min-h-screen bg-white dark:bg-black">
<Header />
<Hero />
<About />
<WhyChooseUs />

View File

@ -1,5 +1,4 @@
import Link from "next/link";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import InnerBanner from "@/components/InnerBanner";
import { Metadata } from "next";
@ -12,9 +11,6 @@ export const metadata: Metadata = {
export default function PrivacyPolicy() {
return (
<div className="min-h-screen bg-white dark:bg-black">
{/* Header */}
<Header />
{/* Inner Banner */}
<InnerBanner
title="Privacy Policy"

View File

@ -1,4 +1,3 @@
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import InnerBanner from "@/components/InnerBanner";
import ProjectsContent from "@/components/ProjectsContent";
@ -12,7 +11,6 @@ export const metadata: Metadata = {
export default function ProjectsPage() {
return (
<div className="min-h-screen bg-gray-50 dark:bg-black">
<Header />
<InnerBanner
title="Our Projects"

View File

@ -1,5 +1,4 @@
import Link from "next/link";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import InnerBanner from "@/components/InnerBanner";
import { Metadata } from "next";
@ -12,7 +11,6 @@ export const metadata: Metadata = {
export default function TermsOfService() {
return (
<div className="min-h-screen bg-white dark:bg-black">
<Header />
<InnerBanner
title="Terms of Service"

View File

@ -0,0 +1,65 @@
"use client";
import { useState } from "react";
import { useRouter, useSearchParams } from "next/navigation";
interface BengaluruHeaderProps {
filteredCount: number;
totalCount: number;
}
export default function BengaluruHeader({ filteredCount, totalCount }: BengaluruHeaderProps) {
const [isExpanded, setIsExpanded] = useState(false);
const router = useRouter();
const searchParams = useSearchParams();
const currentSearch = searchParams.get("search");
const fullText = "Browse real estate for sale in Bengaluru. Properties including flats, villas, and plots from trusted builders. Whether you're looking for luxurious or budget-friendly property in Bengaluru, Propsoch offers a wide range of options to suit every need. Find properties for sale in Bengaluru. Residential options with verified details, photos, and competitive pricing. Prices ₹1.5Cr - ₹5.0Cr. RERA-registered homes in gated communities, with amenities such as Swimming Pool, Gym, Clubhouse, Kids' Play Area, 24x7 Security, and Power Backup. Legal due diligence support available. 356+ options across top communities. Prime locations with IT connectivity and social infrastructure. View accurate prices, configurations, photos, and neighbourhood insights to compare options and book confidently with Propsoch.";
const toggleReadMore = () => setIsExpanded(!isExpanded);
const handleModernSpaacesClick = () => {
// Toggle: if already selected, clear it (optional, but good UX). Or just set it.
// User asked for "click that button morden this ... show morden space preprtis only filter"
router.push("?search=Modern+Spaaces", { scroll: false });
};
const isModernSpaacesActive = currentSearch === "Modern Spaaces";
return (
<div className="mb-8">
<div className="flex flex-col md:flex-row md:items-center justify-between mb-4">
<h1 className="text-3xl font-bold text-gray-900 dark:text-white">
Property for Sale in Bengaluru
</h1>
<div className="text-sm text-gray-500 dark:text-gray-400 mt-2 md:mt-0">
Showing 1-{filteredCount} of {totalCount} Properties | Last Updated: {new Date().toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' })}
</div>
</div>
<div className="mb-6">
<p className="text-gray-600 dark:text-gray-300 text-sm leading-relaxed inline">
{isExpanded ? fullText : `${fullText.substring(0, 230)}...`}
</p>
<button
onClick={toggleReadMore}
className="ml-2 text-orange-500 hover:text-orange-600 font-medium text-sm inline-block focus:outline-none"
>
{isExpanded ? "Show Less" : "Read more"}
</button>
</div>
<div className="flex gap-4 justify-center">
<button
onClick={handleModernSpaacesClick}
className={`px-6 py-2 rounded-full border transition-all text-sm font-medium ${isModernSpaacesActive
? "bg-gray-900 text-white border-gray-900 dark:bg-white dark:text-black"
: "bg-white text-gray-700 border-gray-300 hover:border-gray-900 dark:bg-black dark:text-gray-300 dark:border-gray-700"
}`}
>
Modern Spaaces
</button>
</div>
</div>
);
}

View File

@ -0,0 +1,12 @@
"use client";
import PropertiesClient from "@/components/PropertiesClient";
import BengaluruHeader from "@/components/BengaluruHeader";
export default function BengaluruPropertiesClientWrapper() {
return (
<PropertiesClient
headerRenderer={(props) => <BengaluruHeader {...props} />}
/>
);
}

View File

@ -2,7 +2,6 @@
import { useCompare } from "@/context/CompareContext";
import { properties } from "@/data/properties";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import InnerBanner from "@/components/InnerBanner";
import Image from "next/image";
@ -25,7 +24,6 @@ export default function CompareClient() {
return (
<div className="min-h-screen bg-gray-50 dark:bg-black">
<Header />
<InnerBanner
title="Compare Properties"

View File

@ -0,0 +1,6 @@
"use client";
import Header from "./Header";
export default function HeaderWrapper() {
return <Header />;
}

View File

@ -1,7 +1,7 @@
"use client";
import { useState } from "react";
import Header from "@/components/Header";
import { useState, useEffect } from "react";
import { useSearchParams } from "next/navigation";
import Footer from "@/components/Footer";
import InnerBanner from "@/components/InnerBanner";
import PropertyFilters, { FilterState } from "@/components/PropertyFilters";
@ -9,8 +9,45 @@ import PropertyCard from "@/components/PropertyCard";
import { properties } from "@/data/properties";
import { motion } from "framer-motion";
export default function PropertiesClient() {
interface PropertiesClientProps {
initialFilters?: Partial<FilterState>;
headerRenderer?: (props: { filteredCount: number; totalCount: number }) => React.ReactNode;
}
export default function PropertiesClient({ initialFilters, headerRenderer }: PropertiesClientProps = {}) {
const searchParams = useSearchParams();
const [filteredProperties, setFilteredProperties] = useState(properties);
const [initializedFilters, setInitializedFilters] = useState<Partial<FilterState> | undefined>(undefined);
const [isInitialized, setIsInitialized] = useState(false);
useEffect(() => {
const search = searchParams.get("search") || "";
const type = searchParams.get("type") || "all";
const initial: Partial<FilterState> = {
search,
type,
budgetMin: "",
budgetMax: "",
bhk: "all",
sortBy: "popularity"
};
setInitializedFilters(initial);
// Initial filter run
const filters = {
search,
type,
budgetMin: "",
budgetMax: "",
bhk: "all",
sortBy: "popularity"
};
handleFilterChange(filters);
setIsInitialized(true);
}, [searchParams]);
const handleFilterChange = (filters: FilterState) => {
let filtered = [...properties];
@ -19,7 +56,8 @@ export default function PropertiesClient() {
if (filters.search) {
filtered = filtered.filter(p =>
p.title.toLowerCase().includes(filters.search.toLowerCase()) ||
p.location.toLowerCase().includes(filters.search.toLowerCase())
p.location.toLowerCase().includes(filters.search.toLowerCase()) ||
p.builder?.name.toLowerCase().includes(filters.search.toLowerCase())
);
}
@ -62,9 +100,10 @@ export default function PropertiesClient() {
setFilteredProperties(filtered);
};
if (!isInitialized) return null; // Logic to wait for params
return (
<div className="min-h-screen bg-gray-50 dark:bg-black">
<Header />
<InnerBanner
title="Our Properties"
@ -77,9 +116,12 @@ export default function PropertiesClient() {
/>
<div>
<PropertyFilters onFilterChange={handleFilterChange} />
<PropertyFilters onFilterChange={handleFilterChange} initialFilters={initializedFilters} />
<div className="max-w-7xl mx-auto px-6 py-12">
{headerRenderer ? (
headerRenderer({ filteredCount: filteredProperties.length, totalCount: properties.length })
) : (
<motion.h2
className="text-3xl font-bold text-foreground mb-8"
initial={{ opacity: 0, y: 20 }}
@ -89,6 +131,7 @@ export default function PropertiesClient() {
>
Browse Our Properties
</motion.h2>
)}
{/* Property Grid */}
{filteredProperties.length > 0 ? (

View File

@ -4,8 +4,6 @@
import { useState, ChangeEvent, FormEvent, useEffect } from "react";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import PropertyGallery from "@/components/PropertyGallery";
@ -21,6 +19,7 @@ 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" }) => {
@ -504,8 +503,6 @@ export default function PropertyDetailClient({ property }: { property: Property
<div className="min-h-screen bg-gray-50 dark:bg-black">
<Header />
<InnerBanner
@ -552,89 +549,121 @@ export default function PropertyDetailClient({ property }: { property: Property
</AnimateSection>
<div className="bg-white dark:bg-gray-900 rounded-2xl p-8 shadow-sm border border-gray-200 dark:border-gray-800">
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4 mb-6">
<div>
<div className="flex flex-col items-start gap-2 md:flex-row md:items-center md:gap-3 mb-3">
<h1 className="text-2xl md:text-3xl font-bold text-foreground">{property.title}</h1>
<span className="px-4 py-1.5 bg-gradient-to-r from-green-500 to-emerald-500 text-white rounded-full text-sm font-semibold shadow-md">
{property.status}
</span>
</div>
<div className="flex items-center text-gray-600 dark:text-gray-400">
<svg className="w-5 h-5 mr-2" 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>
{property.location}
</div>
</div>
<div className="text-right">
<div className="text-sm text-gray-500 dark:text-gray-400 mb-1">Starting from</div>
<div className="text-4xl md:text-2xl font-bold bg-gradient-to-r from-primary to-blue-600 bg-clip-text text-transparent">
{property.price}
</div>
</div>
</div>
{/* Quick Stats */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-2 md:gap-4 pt-6 border-t border-gray-200 dark:border-gray-800">
<div className="text-center p-2 md:p-4 bg-gray-50 dark:bg-gray-800 rounded-xl">
<div className="text-2xl md:text-1xl font-bold text-primary mb-1">{property.overview.bhk}</div>
<div className="text-sm text-gray-600 dark:text-gray-400">Configuration</div>
</div>
<div className="text-center p-2 md:p-4 bg-gray-50 dark:bg-gray-800 rounded-xl">
<div className="text-2xl md:text-1xl font-bold text-primary mb-1">{property.overview.size}</div>
<div className="text-sm text-gray-600 dark:text-gray-400">Area</div>
</div>
<div className="text-center p-2 md:p-4 bg-gray-50 dark:bg-gray-800 rounded-xl">
<div className="text-2xl font-bold text-primary mb-1">{property.overview.possession}</div>
<div className="text-sm text-gray-600 dark:text-gray-400">Possession</div>
</div>
<div className="text-center p-2 md:p-4 bg-gray-50 dark:bg-gray-800 rounded-xl">
<div className="text-2xl font-bold text-primary mb-1">{property.overview.totalUnits}</div>
<div className="text-sm text-gray-600 dark:text-gray-400">Total Units</div>
</div>
</div>
</div>
<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>
@ -644,45 +673,40 @@ export default function PropertyDetailClient({ property }: { property: Property
<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 with title and badges */}
<div className="flex flex-col md:flex-row md:items-start md:justify-between gap-4 mb-6">
<div className="flex-1">
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-2">Overview</h2>
{/* 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-2 sm:px-4 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" :
badge === "Average" || badge === "Premium" || badge === "Luxury" || badge === "Elite" ? "bg-blue-100 text-blue-700" :
"bg-purple-100 text-purple-700"
<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>
</div>
{/* Subtitle */}
<p className="text-gray-500 dark:text-gray-400 text-sm mb-8">
{/* 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>
@ -742,7 +766,7 @@ export default function PropertyDetailClient({ property }: { property: Property
<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}.`}
{property.overview.description || property.connectivity?.description || `${property.title} is located in ${property.location}.`}
</p>
{!isConnectivityExpanded && (
@ -1083,8 +1107,6 @@ export default function PropertyDetailClient({ property }: { property: Property
300+ families found safer homes with Sky & Soil. You could be next
<span className="text-2xl"></span>
</p>
</div>
@ -1095,7 +1117,7 @@ export default function PropertyDetailClient({ property }: { property: Property
{/* Card 1: Compare */}
<div className="bg-orange-50 dark:bg-orange-900/10 rounded-2xl p-8 flex flex-col items-center text-center relative overflow-hidden group hover:shadow-lg transition-shadow duration-300">
<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 */}
@ -1125,7 +1147,7 @@ export default function PropertyDetailClient({ property }: { property: Property
<h3 className="text-xl font-bold text-gray-900 dark:text-white mb-2">Like this project?</h3>
<h3 className="text-xl font-bold text-gray-900 dark:text-white mb-4">Compare it with the rest</h3>
<h3 className="text-md font-bold text-gray-900 dark:text-white mb-4">Compare it with the rest</h3>
@ -1149,12 +1171,12 @@ export default function PropertyDetailClient({ property }: { property: Property
{/* Card 2: Negotiate (Green) */}
<div className="bg-green-50 dark:bg-green-900/10 rounded-2xl p-8 pt-12 flex flex-col items-center text-center relative overflow-hidden group hover:shadow-lg transition-shadow duration-300 border-t-4 border-green-500">
<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">
TRUSTED BY {property.skyandsoilClarity?.familiesHelped || "300+"} FAMILIES WITH SKY & SOIL
{property.skyandsoilClarity?.familiesHelped}
</div>
@ -1173,12 +1195,8 @@ export default function PropertyDetailClient({ property }: { property: Property
{/* Trophy Icon */}
<svg viewBox="0 0 200 200" className="w-full h-full drop-shadow-xl text-green-500">
<path fill="currentColor" d="M160 40h-20c0-11-9-20-20-20H80c-11 0-20 9-20 20H40c-11 0-20 9-20 20v20c0 22 18 40 40 40h5c6 19 24 33 45 33s39-14 45-33h5c22 0 40-18 40-40V60c0-11-9-20-20-20zm-100 40H40V60h20v20zm100 0h-20V60h20v20z" />
<path fill="#22543D" d="M100 140v40H70v20h60v-20h-30v-40" />
<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>
@ -1187,7 +1205,7 @@ export default function PropertyDetailClient({ property }: { property: Property
<h3 className="text-xl font-bold text-gray-900 dark:text-white mb-2">Pay less. Get more.</h3>
<h3 className="text-xl font-bold text-gray-900 dark:text-white mb-4">Let us negotiate for you.</h3>
<h3 className="text-md font-bold text-gray-900 dark:text-white mb-4">Let us negotiate for you.</h3>
@ -1211,7 +1229,7 @@ export default function PropertyDetailClient({ property }: { property: Property
{/* Card 3: Report (Purple) */}
<div className="bg-purple-50 dark:bg-purple-900/10 rounded-2xl p-8 flex flex-col items-center text-center relative overflow-hidden group hover:shadow-lg transition-shadow duration-300">
<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 */}
@ -1241,7 +1259,7 @@ export default function PropertyDetailClient({ property }: { property: Property
<h3 className="text-xl font-bold text-gray-900 dark:text-white mb-2">Almost convinced?</h3>
<h3 className="text-xl font-bold text-gray-900 dark:text-white mb-4">See what we uncovered</h3>
<h3 className="text-md font-bold text-gray-900 dark:text-white mb-4">See what we uncovered</h3>
@ -1606,22 +1624,16 @@ export default function PropertyDetailClient({ property }: { property: Property
{/* Approvals List - Two Columns */}
{/* Approvals List - Two Column Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-12 gap-y-4 mb-12">
<ul className="grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-3 mb-12">
{property.approvals?.map((approval, index) => (
<div key={index} className="flex items-start gap-3">
<div className="w-10 h-10 bg-gray-100 dark:bg-gray-800 rounded-lg flex items-center justify-center flex-shrink-0 mt-0.5">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-5 h-5 text-gray-700 dark:text-gray-300">
<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>
<div>
<h4 className="font-semibold text-gray-900 dark:text-white mb-1">{approval.name}</h4>
</div>
</div>
<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>
))}
</div>
</ul>
@ -1637,7 +1649,13 @@ export default function PropertyDetailClient({ property }: { property: Property
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{property.documents?.map((doc, index) => (
<div key={index} 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">
<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" />
@ -1652,10 +1670,10 @@ export default function PropertyDetailClient({ property }: { property: Property
<p className="text-sm text-gray-500 dark:text-gray-400 mb-8 flex-grow leading-relaxed">
{doc.description}
</p>
<button className="text-sm font-bold text-gray-900 dark:text-white hover:underline text-left">
<span className="text-sm font-bold text-gray-900 dark:text-white hover:underline text-left inline-block">
See Document
</button>
</div>
</span>
</a>
))}
</div>
@ -1681,11 +1699,11 @@ export default function PropertyDetailClient({ property }: { property: Property
{property.builder?.description}
</p>
<a href="/residential-real-estate/" className="text-blue-600 dark:text-blue-400 font-bold hover:underline">
<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"}
</a>
</Link>
</div>

View File

@ -4,6 +4,7 @@ import { useState } from "react";
interface PropertyFiltersProps {
onFilterChange: (filters: FilterState) => void;
initialFilters?: Partial<FilterState>;
}
export interface FilterState {
@ -15,14 +16,14 @@ export interface FilterState {
sortBy: string;
}
export default function PropertyFilters({ onFilterChange }: PropertyFiltersProps) {
export default function PropertyFilters({ onFilterChange, initialFilters }: PropertyFiltersProps) {
const [filters, setFilters] = useState<FilterState>({
search: "",
type: "all",
budgetMin: "",
budgetMax: "",
bhk: "all",
sortBy: "popularity"
search: initialFilters?.search || "",
type: initialFilters?.type || "all",
budgetMin: initialFilters?.budgetMin || "",
budgetMax: initialFilters?.budgetMax || "",
bhk: initialFilters?.bhk || "all",
sortBy: initialFilters?.sortBy || "popularity"
});
const [showFilters, setShowFilters] = useState(false);

View File

@ -47,6 +47,7 @@ export type Property = {
amenities: string[]; // Keep for backward compatibility if needed, or deprecate
// New dynamic fields
skyAndSoilScore?: number; // Score from 0-5 for the property rating
detailedOverview?: {
landArea: string;
landAreaAvg: string;
@ -87,6 +88,7 @@ export type Property = {
documents?: {
title: string;
description: string;
pdfUrl?: string; // PDF URL to open in new tab
}[];
builder?: {
name: string;
@ -130,7 +132,7 @@ export const properties: Property[] = [
metaDescription: "Experience luxury living at BARCA, Godrej MSR City. These 3 BHK apartments blend modern architecture with natural serenity for a truly refined lifestyle.",
category: "Apartments",
location: "Shettigere Road, Bangalore",
price: "₹ 1.9 CR*",
price: "₹ 1.9 CR - ₹ 2.5 CR",
description: "Experience the pinnacle of luxury living at BARCA, Godrej MSR City. These 3 BHK apartments are designed for those who appreciate the finer things in life, offering a perfect blend of modern architecture and natural serenity.",
status: "New Launch",
image: "/assets/images/projects/barca.webp",
@ -140,7 +142,7 @@ export const properties: Property[] = [
size: "1500 - 2200 Sq.Ft",
possession: "Dec 2027",
totalUnits: "400",
badges: ["Better", "Average", "Subpar"],
badges: ["Better", "Average", "Badge"],
stats: [
{ label: "Land Area", value: "50.00 Acres", subtext: "Avg is 50.00 acres", icon: "land" },
{ label: "Closest Metro", value: "3.62 kms", subtext: "Avg is 0.00 kms", icon: "metro" },
@ -167,7 +169,7 @@ export const properties: Property[] = [
landType: "Residential"
},
skyandsoilClarity: {
familiesHelped: "300+",
familiesHelped: "Trusted by 300+ Families",
},
pricingUpdateDate: "03 Nov 2025",
reraInfo: {
@ -191,7 +193,8 @@ export const properties: Property[] = [
{ id: "50 x 60", price: "₹3.10 Crores", area: "3000 sqft", direction: "North" },
],
approvals: [
{ name: "Approved by BDA", available: true },
{ name: "Approved by BBMP", available: true },
{ name: "Approved by BDA", available: false },
{ name: "Approved by BWSSB", available: false },
{ name: "Approved by MOEF", available: false },
],
@ -285,7 +288,7 @@ export const properties: Property[] = [
metaDescription: "Godrej Woods offers premium 2 & 3 BHK apartments in Thanisandra. Enjoy a forest-themed lifestyle with world-class amenities and excellent connectivity.",
category: "Premium Homes",
location: "Thanisandra, North Bangalore",
price: "₹ 1.62 CR*",
price: "₹ 1.62 CR - ₹ 2.5 CR",
description: "Godrej Woods offers premium 2 & 3 BHK apartments nestled in the lush greenery of Thanisandra. Enjoy a forest-themed lifestyle with world-class amenities and excellent connectivity to the airport and tech parks.",
status: "Best Seller",
image: "/assets/images/projects/godrej-woods.webp",
@ -295,7 +298,7 @@ export const properties: Property[] = [
size: "1500 - 2200 Sq.Ft",
possession: "Dec 2027",
totalUnits: "400",
badges: ["Better", "Average", "Subpar"],
badges: ["Better", "Average", "Badge"],
stats: [
{ label: "Land Area", value: "50.00 Acres", subtext: "Avg is 50.00 acres", icon: "land" },
{ label: "Closest Metro", value: "3.62 kms", subtext: "Avg is 0.00 kms", icon: "metro" },
@ -322,7 +325,7 @@ export const properties: Property[] = [
landType: "Residential"
},
skyandsoilClarity: {
familiesHelped: "300+",
familiesHelped: "Trusted by 300+ Families",
},
pricingUpdateDate: "15 Oct 2025",
reraInfo: {
@ -346,7 +349,8 @@ export const properties: Property[] = [
{ id: "50 x 60", price: "₹3.10 Crores", area: "3000 sqft", direction: "North" },
],
approvals: [
{ name: "Approved by BDA", available: true },
{ name: "Approved by BBMP", available: true },
{ name: "Approved by BDA", available: false },
{ name: "Approved by BWSSB", available: false },
{ name: "Approved by MOEF", available: false },
],
@ -437,7 +441,7 @@ export const properties: Property[] = [
metaDescription: "Discover community living at Godrej Hoskote. Premium 2 & 3 BHK homes designed for comfort and convenience, with easy access to industrial hubs and schools.",
category: "Apartments",
location: "Hoskote, Soukya Road Ext",
price: "₹ 1.17 Cr*",
price: "₹ 1.17 CR - ₹ 1.5 CR",
description: "Discover the joy of community living at Godrej Hoskote. These premium 2 & 3 BHK homes are designed to provide a comfortable and convenient lifestyle, with easy access to industrial hubs and educational institutions.",
status: "New Launch",
image: "/assets/images/projects/godrej-hoskote.webp",
@ -447,7 +451,7 @@ export const properties: Property[] = [
size: "1500 - 2200 Sq.Ft",
possession: "Dec 2027",
totalUnits: "400",
badges: ["Better", "Average", "Subpar"],
badges: ["Better", "Average", "Badge"],
stats: [
{ label: "Land Area", value: "50.00 Acres", subtext: "Avg is 50.00 acres", icon: "land" },
{ label: "Closest Metro", value: "3.62 kms", subtext: "Avg is 0.00 kms", icon: "metro" },
@ -474,7 +478,7 @@ export const properties: Property[] = [
landType: "Residential"
},
skyandsoilClarity: {
familiesHelped: "300+",
familiesHelped: "Trusted by 300+ Families",
},
pricingUpdateDate: "20 Oct 2025",
reraInfo: {
@ -498,7 +502,8 @@ export const properties: Property[] = [
{ id: "50 x 60", price: "₹3.10 Crores", area: "3000 sqft", direction: "North" },
],
approvals: [
{ name: "Approved by BDA", available: true },
{ name: "Approved by BBMP", available: true },
{ name: "Approved by BDA", available: false },
{ name: "Approved by BWSSB", available: false },
{ name: "Approved by MOEF", available: false },
],
@ -589,7 +594,7 @@ export const properties: Property[] = [
metaDescription: "Wake up to serene lake views at Godrej Lakeside Orchard. Luxury 2, 3, 3.5 & 4.5 BHK apartments offer a resort-like living experience on Sarjapur Road.",
category: "Luxury",
location: "Sarjapur Road, Bangalore",
price: "₹ 1.89 Cr*",
price: "₹ 1.89 CR - ₹ 2.5 CR",
description: "Wake up to the serene views of a lake and orchard at Godrej Lakeside Orchard. These luxury 2, 3, 3.5 & 4.5 BHK apartments offer a resort-like living experience on Sarjapur Road.",
status: "Trending",
image: "/assets/images/projects/godrej-lakeside.webp",
@ -599,7 +604,7 @@ export const properties: Property[] = [
size: "1500 - 2200 Sq.Ft",
possession: "Dec 2027",
totalUnits: "400",
badges: ["Better", "Average", "Subpar"],
badges: ["Better", "Average", "Badge"],
stats: [
{ label: "Land Area", value: "50.00 Acres", subtext: "Avg is 50.00 acres", icon: "land" },
{ label: "Closest Metro", value: "3.62 kms", subtext: "Avg is 0.00 kms", icon: "metro" },
@ -626,7 +631,7 @@ export const properties: Property[] = [
landType: "Residential"
},
skyandsoilClarity: {
familiesHelped: "300+",
familiesHelped: "Trusted by 300+ Families",
},
pricingUpdateDate: "25 Oct 2025",
reraInfo: {
@ -650,7 +655,8 @@ export const properties: Property[] = [
{ id: "50 x 60", price: "₹3.10 Crores", area: "3000 sqft", direction: "North" },
],
approvals: [
{ name: "Approved by BDA", available: true },
{ name: "Approved by BBMP", available: true },
{ name: "Approved by BDA", available: false },
{ name: "Approved by BWSSB", available: false },
{ name: "Approved by MOEF", available: false },
],
@ -741,7 +747,7 @@ export const properties: Property[] = [
metaDescription: "Godrej Tiara presents exclusive 3 & 4 BHK apartments in Yeshwanthpur. Experience unmatched luxury, sophisticated design, and panoramic city skyline views.",
category: "Luxury",
location: "Yeshwanthpur, Bangalore",
price: "₹ 1.89 Cr*",
price: "₹ 1.89 CR - ₹ 2.5 CR",
description: "Godrej Tiara presents exclusive 3 & 4 BHK apartments in the heart of Yeshwanthpur. Experience unmatched luxury, sophisticated design, and panoramic views of the city skyline.",
status: "Premium",
image: "/assets/images/projects/godrej-tiara.webp",
@ -751,7 +757,7 @@ export const properties: Property[] = [
size: "1500 - 2200 Sq.Ft",
possession: "Dec 2027",
totalUnits: "400",
badges: ["Better", "Average", "Subpar"],
badges: ["Better", "Average", "Badge"],
stats: [
{ label: "Land Area", value: "50.00 Acres", subtext: "Avg is 50.00 acres", icon: "land" },
{ label: "Closest Metro", value: "3.62 kms", subtext: "Avg is 0.00 kms", icon: "metro" },
@ -778,7 +784,7 @@ export const properties: Property[] = [
landType: "Residential"
},
skyandsoilClarity: {
familiesHelped: "300+",
familiesHelped: "Trusted by 300+ Families",
},
pricingUpdateDate: "01 Nov 2025",
reraInfo: {
@ -802,7 +808,8 @@ export const properties: Property[] = [
{ id: "50 x 60", price: "₹3.10 Crores", area: "3000 sqft", direction: "North" },
],
approvals: [
{ name: "Approved by BDA", available: true },
{ name: "Approved by BBMP", available: true },
{ name: "Approved by BDA", available: false },
{ name: "Approved by BWSSB", available: false },
{ name: "Approved by MOEF", available: false },
],