fixd larg lines in HomePage , add cookies rather than localStorage,make canvas background Login page
This commit is contained in:
605
app/page.js
605
app/page.js
@ -1,600 +1,9 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import {
|
||||
ShieldCheck,
|
||||
Lock,
|
||||
Zap,
|
||||
Star,
|
||||
Rocket,
|
||||
Search,
|
||||
MapPin,
|
||||
Home,
|
||||
DollarSign,
|
||||
ChevronDown,
|
||||
Shield,
|
||||
Award,
|
||||
Sparkles,
|
||||
UserCircle,
|
||||
LogOut,
|
||||
Calendar,
|
||||
Building,
|
||||
PlusCircle,
|
||||
Heart,
|
||||
MessageCircle
|
||||
} from 'lucide-react';
|
||||
import HeroSearch from './components/home/HeroSearch';
|
||||
import PropertyMapWithMarkers from './components/PropertyMapWithMarkers';
|
||||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
import { getRentProperties, getSaleProperties } from './utils/api';
|
||||
import { BuildingTypeKeys, PropertyStatusKeys, extractCity } from './enums';
|
||||
import AuthService from './services/AuthService';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
function mapApiProperty(t, item, index) {
|
||||
const info = item.propertyInformation || {};
|
||||
|
||||
const dailyPrice = item.dailyRent ?? 0;
|
||||
const monthlyPrice = item.monthlyRent ?? 0;
|
||||
const salePrice = item.price ?? 0;
|
||||
const isRentListing = Boolean(item.dailyRent != null || item.monthlyRent != null);
|
||||
|
||||
const price = isRentListing ? (dailyPrice || monthlyPrice || 0) : salePrice;
|
||||
const priceUnit = isRentListing ? (monthlyPrice ? 'monthly' : 'daily') : 'sale';
|
||||
|
||||
const propType = BuildingTypeKeys[info.buildingType] ?? BuildingTypeKeys[item.type] ?? (item.type || 'apartment');
|
||||
const status = PropertyStatusKeys[info.status] ?? PropertyStatusKeys[item.status] ?? 'available';
|
||||
|
||||
const features = [];
|
||||
if (item.isSmokeAllow) features.push(t('smoke-allowed'));
|
||||
if (item.isVisitorAllow) features.push(t('visitors-allowed'));
|
||||
if (item.specializedFor) features.push(t('specialized'));
|
||||
if (info.numberOfBedRooms) features.push(`${info.numberOfBedRooms} ${t('rooms')}`);
|
||||
if (info.numberOfBathRooms) features.push(`${info.numberOfBathRooms} ${t('bathrooms')}`);
|
||||
|
||||
const apiBase = typeof window !== 'undefined' ? (process.env.NEXT_PUBLIC_API_URL || 'https://45.93.137.91.nip.io/api') : '';
|
||||
const rawImages = Array.isArray(info.images) ? info.images : [];
|
||||
const images = rawImages.length > 0
|
||||
? rawImages.map(img => img.startsWith('http') ? img : `${apiBase}${img.startsWith('/') ? '' : '/Pictures/'}${img}`)
|
||||
: ['/property-placeholder.jpg'];
|
||||
|
||||
const ownerSource = info.ownerType == null && item.ownerType == null
|
||||
? 'all'
|
||||
: [info.ownerType, item.ownerType].find((value) => value != null) === 1
|
||||
? 'agency'
|
||||
: 'owner';
|
||||
|
||||
return {
|
||||
id: item.id ?? index + 1,
|
||||
title: info.address || t('propertyWithId', { id: item.id || index + 1 }),
|
||||
description: info.description || '',
|
||||
type: propType,
|
||||
price: price,
|
||||
priceUSD: price,
|
||||
priceUnit,
|
||||
listingType: isRentListing ? 'rent' : 'sale',
|
||||
location: {
|
||||
city: extractCity(info.address) || 'damascus',
|
||||
district: info.address || '',
|
||||
address: info.address || '',
|
||||
lat: parseFloat(info.cordsX) || 0,
|
||||
lng: parseFloat(info.cordsY) || 0,
|
||||
},
|
||||
bedrooms: info.numberOfBedRooms || 0,
|
||||
bathrooms: info.numberOfBathRooms || 0,
|
||||
area: info.space || 0,
|
||||
features,
|
||||
images,
|
||||
status,
|
||||
rating: item.rating || 4.5,
|
||||
isNew: false,
|
||||
allowedIdentities: ['syrian', 'passport'],
|
||||
priceDisplay: {
|
||||
daily: dailyPrice,
|
||||
monthly: monthlyPrice,
|
||||
sale: salePrice,
|
||||
},
|
||||
ownerSource,
|
||||
bookings: [],
|
||||
_raw: item,
|
||||
};
|
||||
}
|
||||
import HomeClient from "./components/home/HomeClient";
|
||||
|
||||
export const metadata = {
|
||||
title: "الصفحة الرئيسية | عقاراتك",
|
||||
description: "ابحث عن العقارات والبيوت للإيجار والبيع بأسهل الطرق.",
|
||||
};
|
||||
export default function HomePage() {
|
||||
const { t } = useTranslation();
|
||||
const mapSectionRef = useRef(null);
|
||||
const [searchFilters, setSearchFilters] = useState(null);
|
||||
const [showMap, setShowMap] = useState(false);
|
||||
const [filteredProperties, setFilteredProperties] = useState([]);
|
||||
const [isScrolling, setIsScrolling] = useState(false);
|
||||
const [user, setUser] = useState(null);
|
||||
const [showUserMenu, setShowUserMenu] = useState(false);
|
||||
const menuRef = useRef(null);
|
||||
const pathname = usePathname();
|
||||
|
||||
const [allProperties, setAllProperties] = useState([]);
|
||||
const [rentProperties, setRentProperties] = useState([]);
|
||||
const [saleProperties, setSaleProperties] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const authUser = AuthService.getUser();
|
||||
if (authUser) {
|
||||
setUser({
|
||||
name: authUser.name || authUser.email,
|
||||
email: authUser.email,
|
||||
role: AuthService.isOwner() ? 'owner' : 'customer',
|
||||
});
|
||||
} else {
|
||||
setUser(null);
|
||||
}
|
||||
}, [pathname]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
async function fetchProperties() {
|
||||
try {
|
||||
const [rentData, saleData] = await Promise.all([
|
||||
getRentProperties().catch(() => []),
|
||||
getSaleProperties().catch(() => []),
|
||||
]);
|
||||
|
||||
const rentList = Array.isArray(rentData) ? rentData : [];
|
||||
const saleList = Array.isArray(saleData) ? saleData : [];
|
||||
|
||||
const mappedRent = rentList.map((p, i) => mapApiProperty(t, p, i));
|
||||
const mappedSale = saleList.map((p, i) => mapApiProperty(t, p, rentList.length + i));
|
||||
|
||||
setRentProperties(mappedRent);
|
||||
setSaleProperties(mappedSale);
|
||||
setAllProperties([...mappedRent, ...mappedSale]);
|
||||
} catch (err) {
|
||||
console.error('[Home] Failed to fetch properties:', err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
fetchProperties();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (searchFilters) {
|
||||
applyFilters(searchFilters);
|
||||
}
|
||||
}, [rentProperties, saleProperties, searchFilters]);
|
||||
|
||||
const logout = () => {
|
||||
AuthService.deleteToken();
|
||||
setUser(null);
|
||||
setShowUserMenu(false);
|
||||
};
|
||||
|
||||
const applyFilters = (filters) => {
|
||||
setSearchFilters(filters);
|
||||
|
||||
let propertiesToFilter = [];
|
||||
if (filters.mode === 'rent') {
|
||||
propertiesToFilter = rentProperties;
|
||||
} else if (filters.mode === 'buy' || filters.mode === 'sell') {
|
||||
propertiesToFilter = saleProperties;
|
||||
} else {
|
||||
propertiesToFilter = allProperties;
|
||||
}
|
||||
|
||||
const filtered = propertiesToFilter.filter(property => {
|
||||
if (filters.city && filters.city !== 'all' && property.location.city !== filters.city) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filters.propertyType && filters.propertyType !== 'all' && property.type !== filters.propertyType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filters.priceRange && filters.priceRange !== 'all') {
|
||||
const priceUSD = property.priceUSD;
|
||||
switch (filters.priceRange) {
|
||||
case '0-500': if (priceUSD > 50) return false; break;
|
||||
case '500-1000': if (priceUSD < 51 || priceUSD > 100) return false; break;
|
||||
case '1000-2000': if (priceUSD < 101 || priceUSD > 200) return false; break;
|
||||
case '2000-3000': if (priceUSD < 201 || priceUSD > 300) return false; break;
|
||||
case '3000+': if (priceUSD < 301) return false; break;
|
||||
}
|
||||
}
|
||||
|
||||
if (filters.ownerSource && filters.ownerSource !== 'all') {
|
||||
if (filters.ownerSource === 'owner' && property.ownerSource !== 'owner') return false;
|
||||
if (filters.ownerSource === 'agency' && property.ownerSource !== 'agency') return false;
|
||||
}
|
||||
|
||||
if (filters.rentPeriod && filters.rentPeriod !== 'all' && property.listingType === 'rent') {
|
||||
if (filters.rentPeriod === 'daily' && !property.priceDisplay.daily) return false;
|
||||
if (filters.rentPeriod === 'monthly' && !property.priceDisplay.monthly) return false;
|
||||
}
|
||||
|
||||
if (filters.availableToday) {
|
||||
if (property.status !== 'available') return false;
|
||||
}
|
||||
|
||||
if (filters.identityType && property.allowedIdentities) {
|
||||
if (!property.allowedIdentities.includes(filters.identityType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
setFilteredProperties(filtered);
|
||||
|
||||
if (!showMap) {
|
||||
setShowMap(true);
|
||||
|
||||
setTimeout(() => {
|
||||
if (mapSectionRef.current) {
|
||||
setIsScrolling(true);
|
||||
mapSectionRef.current.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'center'
|
||||
});
|
||||
|
||||
setTimeout(() => setIsScrolling(false), 1000);
|
||||
}
|
||||
}, 300);
|
||||
} else {
|
||||
if (mapSectionRef.current) {
|
||||
setIsScrolling(true);
|
||||
mapSectionRef.current.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'center'
|
||||
});
|
||||
setTimeout(() => setIsScrolling(false), 1000);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const resetSearch = () => {
|
||||
setShowMap(false);
|
||||
setSearchFilters(null);
|
||||
setFilteredProperties([]);
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
};
|
||||
|
||||
const getUserInitial = () => {
|
||||
if (user?.name) {
|
||||
return user.name.charAt(0).toUpperCase();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const isOwner = user?.role === 'owner';
|
||||
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
<section className="relative min-h-screen flex items-center justify-center overflow-hidden">
|
||||
<div className="relative z-10 container mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<motion.div
|
||||
className="text-center mb-12"
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
variants={{
|
||||
hidden: { opacity: 0 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
transition: { staggerChildren: 0.2 }
|
||||
}
|
||||
}}
|
||||
>
|
||||
<motion.h1
|
||||
className="text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-bold text-amber-50 mb-6 leading-tight tracking-tight"
|
||||
variants={{
|
||||
hidden: { opacity: 0, y: 20 },
|
||||
visible: { opacity: 1, y: 0 }
|
||||
}}
|
||||
>
|
||||
{t("heroTitleLine1")}<br />
|
||||
<motion.span
|
||||
className="text-amber-300"
|
||||
animate={{
|
||||
y: [0, -10, 0],
|
||||
}}
|
||||
transition={{
|
||||
duration: 2,
|
||||
repeat: Infinity,
|
||||
ease: "easeInOut"
|
||||
}}
|
||||
>
|
||||
{t("heroTitleLine2")}
|
||||
</motion.span>
|
||||
</motion.h1>
|
||||
<motion.p
|
||||
className="text-base sm:text-lg text-amber-50 max-w-2xl mx-auto leading-relaxed"
|
||||
variants={{
|
||||
hidden: { opacity: 0, y: 20 },
|
||||
visible: { opacity: 1, y: 0 }
|
||||
}}
|
||||
>
|
||||
{t("heroSubtitle")}
|
||||
</motion.p>
|
||||
</motion.div>
|
||||
|
||||
{!isOwner && <HeroSearch onSearch={applyFilters} isAuthenticated={!!user} />}
|
||||
|
||||
{isOwner && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.5 }}
|
||||
className="bg-white/10 backdrop-blur-lg rounded-2xl p-8 text-center border border-white/20"
|
||||
>
|
||||
<h2 className="text-2xl font-bold text-amber-50 mb-2">
|
||||
{t("ownerGreeting")} {user?.name}!
|
||||
</h2>
|
||||
<p className="text-amber-50/80 mb-4">
|
||||
{t("ownerDescription")}
|
||||
</p>
|
||||
<Link
|
||||
href="/owner/properties"
|
||||
className="inline-flex items-center gap-2 bg-amber-500 text-white px-6 py-3 rounded-xl font-medium hover:bg-amber-600 transition-colors"
|
||||
>
|
||||
<Building className="w-5 h-5" />
|
||||
{t("manageMyProperties")}
|
||||
</Link>
|
||||
</motion.div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!showMap && !isOwner && (
|
||||
<motion.div
|
||||
className="absolute bottom-8 left-1/2 transform -translate-x-1/2 cursor-pointer"
|
||||
animate={{
|
||||
y: [0, 10, 0],
|
||||
}}
|
||||
transition={{
|
||||
duration: 1.5,
|
||||
repeat: Infinity,
|
||||
ease: "easeInOut"
|
||||
}}
|
||||
onClick={() => window.scrollTo({
|
||||
top: window.innerHeight,
|
||||
behavior: 'smooth'
|
||||
})}
|
||||
>
|
||||
<svg className="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 14l-7 7m0 0l-7-7m7 7V3" />
|
||||
</svg>
|
||||
</motion.div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
{!isOwner && (<>
|
||||
<AnimatePresence mode="wait">
|
||||
{showMap && (
|
||||
<motion.section
|
||||
ref={mapSectionRef}
|
||||
initial={{ opacity: 0, y: 50 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -50 }}
|
||||
transition={{
|
||||
type: "spring",
|
||||
damping: 20,
|
||||
stiffness: 100,
|
||||
duration: 0.6
|
||||
}}
|
||||
className="py-12 bg-gray-50 relative"
|
||||
>
|
||||
{isScrolling && (
|
||||
<motion.div
|
||||
className="absolute top-0 left-0 right-0 h-1 bg-amber-500 z-10"
|
||||
initial={{ scaleX: 0 }}
|
||||
animate={{ scaleX: 1 }}
|
||||
transition={{ duration: 1, ease: "easeInOut" }}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="container mx-auto px-4">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.2 }}
|
||||
className="text-center mb-8"
|
||||
>
|
||||
<div className="flex items-center justify-center gap-4 mb-2">
|
||||
<h2 className="text-3xl font-bold text-gray-900">
|
||||
{filteredProperties.length > 0 ? t('searchResults') : t('no-properties')}
|
||||
</h2>
|
||||
<motion.button
|
||||
onClick={resetSearch}
|
||||
className="px-4 py-2 bg-white border border-gray-300 rounded-full text-sm font-medium text-gray-700 hover:bg-gray-50 shadow-sm flex items-center gap-2"
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 19l-7-7m0 0l7-7m-7 7h18" />
|
||||
</svg>
|
||||
{t('newSearch')}
|
||||
</motion.button>
|
||||
</div>
|
||||
|
||||
{filteredProperties.length > 0 ? (
|
||||
<p className="text-gray-600">
|
||||
{t('foundPropertiesCount', { count: filteredProperties.length })}
|
||||
</p>
|
||||
) : (
|
||||
<p className="text-gray-600">
|
||||
{t('noPropertiesMatchFilters')}
|
||||
</p>
|
||||
)}
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
className="bg-white rounded-2xl shadow-xl overflow-hidden border border-gray-200"
|
||||
initial={{ scale: 0.95, opacity: 0 }}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
transition={{ delay: 0.3, type: "spring" }}
|
||||
>
|
||||
{filteredProperties.length > 0 ? (
|
||||
<PropertyMapWithMarkers
|
||||
properties={filteredProperties.map(p => ({
|
||||
...p,
|
||||
lat: p.location.lat,
|
||||
lng: p.location.lng,
|
||||
address: p.location.address
|
||||
}))}
|
||||
onPropertyClick={() => {}}
|
||||
/>
|
||||
) : (
|
||||
<div className="h-[400px] flex flex-col items-center justify-center bg-gray-50">
|
||||
<div className="w-24 h-24 bg-gray-200 rounded-full flex items-center justify-center mb-4">
|
||||
<svg className="w-12 h-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-gray-700 mb-2">{t('no-properties')}</h3>
|
||||
<p className="text-gray-500">{t('try-changing-search-criteria')}</p>
|
||||
</div>
|
||||
)}
|
||||
</motion.div>
|
||||
</div>
|
||||
</motion.section>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</>)}
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
className="text-center mb-12"
|
||||
>
|
||||
<h2 className="text-3xl md:text-4xl font-bold text-gray-900 mb-4 tracking-tight">
|
||||
{t('whyChooseUsTitle')}
|
||||
</h2>
|
||||
<p className="text-gray-600 max-w-2xl mx-auto text-lg">
|
||||
{t('whyChooseUsSubtitle')}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-12">
|
||||
<motion.div
|
||||
className="group bg-white p-6 rounded-xl shadow-sm hover:shadow-md transition-all duration-300 border border-gray-100"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.1 }}
|
||||
whileHover={{ y: -4 }}
|
||||
>
|
||||
<div className="flex items-center gap-4 mb-4">
|
||||
<div className="w-12 h-12 bg-amber-100 rounded-xl flex items-center justify-center group-hover:bg-amber-200 transition-colors duration-300">
|
||||
<ShieldCheck className="w-6 h-6 text-amber-600" />
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-gray-900">
|
||||
{t('feature1Title')}
|
||||
</h3>
|
||||
</div>
|
||||
<p className="text-gray-600 text-sm leading-relaxed">
|
||||
{t('feature1Description')}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
className="group bg-white p-6 rounded-xl shadow-sm hover:shadow-md transition-all duration-300 border border-gray-100"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
whileHover={{ y: -4 }}
|
||||
>
|
||||
<div className="flex items-center gap-4 mb-4">
|
||||
<div className="w-12 h-12 bg-blue-100 rounded-xl flex items-center justify-center group-hover:bg-blue-200 transition-colors duration-300">
|
||||
<Lock className="w-6 h-6 text-blue-600" />
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-gray-900">
|
||||
{t('feature2Title')}
|
||||
</h3>
|
||||
</div>
|
||||
<p className="text-gray-600 text-sm leading-relaxed">
|
||||
{t('feature2Description')}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
className="group bg-white p-6 rounded-xl shadow-sm hover:shadow-md transition-all duration-300 border border-gray-100"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.3 }}
|
||||
whileHover={{ y: -4 }}
|
||||
>
|
||||
<div className="flex items-center gap-4 mb-4">
|
||||
<div className="w-12 h-12 bg-green-100 rounded-xl flex items-center justify-center group-hover:bg-green-200 transition-colors duration-300">
|
||||
<Zap className="w-6 h-6 text-green-600" />
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-gray-900">
|
||||
{t('feature3Title')}
|
||||
</h3>
|
||||
</div>
|
||||
<p className="text-gray-600 text-sm leading-relaxed">
|
||||
{t('feature3Description')}
|
||||
</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{filteredProperties.length > 0 && searchFilters && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.4, type: "spring" }}
|
||||
className="flex flex-wrap items-center justify-center gap-3"
|
||||
>
|
||||
<div className="bg-white px-4 py-2 rounded-full shadow-sm border border-gray-200 text-sm">
|
||||
<span className="text-gray-600">{t('filterPropertyTypeLabel')}</span>
|
||||
<span className="font-bold text-gray-900">
|
||||
{searchFilters.propertyType === 'all' ? t('all') :
|
||||
searchFilters.propertyType === 'apartment' ? t('buildingType.apartment') :
|
||||
searchFilters.propertyType === 'villa' ? t('buildingType.villa') : t('house')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="bg-white px-4 py-2 rounded-full shadow-sm border border-gray-200 text-sm">
|
||||
<span className="text-gray-600">{t('filterPriceRangeLabel')}</span>
|
||||
<span className="font-bold text-gray-900">
|
||||
{searchFilters.priceRange === 'all' ? t('allPrices') :
|
||||
searchFilters.priceRange === '0-500' ? t('priceRangeUnder500') :
|
||||
searchFilters.priceRange === '500-1000' ? t('priceRange50to100') :
|
||||
searchFilters.priceRange === '1000-2000' ? t('priceRange100to200') :
|
||||
searchFilters.priceRange === '2000-3000' ? t('priceRange200to300') : t('priceRangeOver300')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="bg-white px-4 py-2 rounded-full shadow-sm border border-gray-200 text-sm">
|
||||
<span className="text-gray-600">{t('filterSourceLabel')}</span>
|
||||
<span className="font-bold text-gray-900">
|
||||
{searchFilters.ownerSource === 'all' ? t('all') :
|
||||
searchFilters.ownerSource === 'owner' ? t('fromOwner') : t('fromAgency')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="bg-white px-4 py-2 rounded-full shadow-sm border border-gray-200 text-sm">
|
||||
<span className="text-gray-600">{t('filterRentTypeLabel')}</span>
|
||||
<span className="font-bold text-gray-900">
|
||||
{searchFilters.rentPeriod === 'all' ? t('all') :
|
||||
searchFilters.rentPeriod === 'daily' ? t('dailyRent') : t('monthlyRent')}
|
||||
</span>
|
||||
</div>
|
||||
{searchFilters.availableToday && (
|
||||
<div className="bg-white px-4 py-2 rounded-full shadow-sm border border-gray-200 text-sm">
|
||||
<span className="font-bold text-gray-900">{t('availableFromToday')}</span>
|
||||
</div>
|
||||
)}
|
||||
</motion.div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return <HomeClient />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user