Edit home
This commit is contained in:
@ -1,19 +1,24 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Search, MapPin, Home, DollarSign } from 'lucide-react';
|
||||
import { Search, MapPin, Home, DollarSign, ShieldCheck } from 'lucide-react';
|
||||
|
||||
export default function HeroSearch({ onSearch }) {
|
||||
export default function HeroSearch({ onSearch, isAuthenticated }) {
|
||||
const { t } = useTranslation();
|
||||
const [activeTab, setActiveTab] = useState('rent');
|
||||
const [activeTab, setActiveTab] = useState('buy');
|
||||
const [filters, setFilters] = useState({
|
||||
city: '',
|
||||
propertyType: '',
|
||||
priceRange: '',
|
||||
identityType: 'syrian'
|
||||
city: 'all',
|
||||
propertyType: 'all',
|
||||
priceRange: 'all',
|
||||
identityType: 'syrian',
|
||||
ownerSource: 'all',
|
||||
rentPeriod: 'all',
|
||||
availableToday: false
|
||||
});
|
||||
const [showLoginDialog, setShowLoginDialog] = useState(false);
|
||||
|
||||
const cities = [
|
||||
{ id: 'all', label: 'جميع المدن' },
|
||||
@ -26,10 +31,10 @@ export default function HeroSearch({ onSearch }) {
|
||||
|
||||
const propertyTypes = [
|
||||
{ id: 'all', label: 'الكل' },
|
||||
{ id: 'apartment', label: 'شقة' },
|
||||
{ id: 'villa', label: 'فيلا' },
|
||||
{ id: 'house', label: 'بيت' },
|
||||
{ id: 'studio', label: 'استوديو' }
|
||||
{ id: 'apartment', label: 'شقق سكنية' },
|
||||
{ id: 'studio', label: 'استوديو' },
|
||||
{ id: 'commercial', label: 'عقار تجاري' },
|
||||
{ id: 'villa', label: 'فيلا / مزرعة' }
|
||||
];
|
||||
|
||||
const priceRanges = [
|
||||
@ -46,17 +51,45 @@ export default function HeroSearch({ onSearch }) {
|
||||
{ id: 'passport', label: 'جواز سفر' }
|
||||
];
|
||||
|
||||
const ownerSources = [
|
||||
{ id: 'all', label: 'الكل' },
|
||||
{ id: 'owner', label: 'من المالك' },
|
||||
{ id: 'agency', label: 'من مكتب عقاري' }
|
||||
];
|
||||
|
||||
const rentPeriods = [
|
||||
{ id: 'all', label: 'الكل' },
|
||||
{ id: 'daily', label: 'إيجار يومي' },
|
||||
{ id: 'monthly', label: 'إيجار شهري' }
|
||||
];
|
||||
|
||||
const handleTabClick = (tab) => {
|
||||
setActiveTab(tab);
|
||||
if ((tab === 'rent' || tab === 'sell') && !isAuthenticated) {
|
||||
setShowLoginDialog(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
if ((activeTab === 'rent' || activeTab === 'sell') && !isAuthenticated) {
|
||||
setShowLoginDialog(true);
|
||||
return;
|
||||
}
|
||||
|
||||
onSearch({
|
||||
...filters,
|
||||
propertyType: filters.propertyType || 'all',
|
||||
mode: activeTab,
|
||||
city: filters.city || 'all',
|
||||
priceRange: filters.priceRange || 'all'
|
||||
propertyType: filters.propertyType || 'all',
|
||||
priceRange: filters.priceRange || 'all',
|
||||
ownerSource: filters.ownerSource || 'all',
|
||||
rentPeriod: filters.rentPeriod || 'all'
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
<>
|
||||
<motion.div
|
||||
className="bg-white/10 backdrop-blur-lg rounded-2xl p-6 sm:p-8 border border-white/20 shadow-2xl"
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
@ -66,7 +99,7 @@ export default function HeroSearch({ onSearch }) {
|
||||
{['rent', 'buy', 'sell'].map((tab) => (
|
||||
<motion.button
|
||||
key={tab}
|
||||
onClick={() => setActiveTab(tab)}
|
||||
onClick={() => handleTabClick(tab)}
|
||||
className={`px-4 py-2 rounded-lg font-medium text-sm transition-all ${
|
||||
activeTab === tab
|
||||
? 'bg-amber-500 text-white'
|
||||
@ -176,6 +209,63 @@ export default function HeroSearch({ onSearch }) {
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 mt-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-white mb-2">مصدر العرض</label>
|
||||
<select
|
||||
value={filters.ownerSource}
|
||||
onChange={(e) => setFilters({ ...filters, ownerSource: e.target.value })}
|
||||
className="w-full px-4 py-3 bg-white/90 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500 text-sm appearance-none cursor-pointer"
|
||||
style={{
|
||||
backgroundImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%23666'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M19 9l-7 7-7-7'%3E%3C/path%3E%3C/svg%3E")`,
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundPosition: 'left 1rem center',
|
||||
backgroundSize: '1rem',
|
||||
paddingLeft: '2.5rem'
|
||||
}}
|
||||
>
|
||||
{ownerSources.map((source) => (
|
||||
<option key={source.id} value={source.id}>
|
||||
{source.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-white mb-2">نوع الإيجار</label>
|
||||
<select
|
||||
value={filters.rentPeriod}
|
||||
onChange={(e) => setFilters({ ...filters, rentPeriod: e.target.value })}
|
||||
className="w-full px-4 py-3 bg-white/90 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-500 text-sm appearance-none cursor-pointer"
|
||||
style={{
|
||||
backgroundImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%23666'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M19 9l-7 7-7-7'%3E%3C/path%3E%3C/svg%3E")`,
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundPosition: 'left 1rem center',
|
||||
backgroundSize: '1rem',
|
||||
paddingLeft: '2.5rem'
|
||||
}}
|
||||
>
|
||||
{rentPeriods.map((period) => (
|
||||
<option key={period.id} value={period.id}>
|
||||
{period.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="md:col-span-2 flex flex-col justify-between p-4 rounded-2xl border border-dashed border-white/30 bg-white/5">
|
||||
<label className="mt-4 flex items-center gap-3 text-white text-sm">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={filters.availableToday}
|
||||
onChange={(e) => setFilters({ ...filters, availableToday: e.target.checked })}
|
||||
className="w-5 h-5 text-amber-500 rounded border-gray-300 bg-white"
|
||||
/>
|
||||
<span className="font-medium">عرض فقط العقارات المتاحة من اليوم</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-6">
|
||||
<motion.button
|
||||
onClick={handleSearch}
|
||||
@ -188,5 +278,40 @@ export default function HeroSearch({ onSearch }) {
|
||||
</motion.button>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{showLoginDialog && !isAuthenticated && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 px-4 py-8">
|
||||
<div className="w-full max-w-md rounded-3xl bg-white p-6 shadow-2xl border border-gray-200">
|
||||
<div className="flex items-center gap-3 mb-5">
|
||||
<ShieldCheck className="w-7 h-7 text-amber-500" />
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900">يرجى تسجيل الدخول</h3>
|
||||
<p className="text-sm text-gray-600">للوصول إلى خيارات التأجير والبيع، يجب أن تكون مسجلاً.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div className="rounded-2xl bg-gray-50 p-4">
|
||||
<p className="text-sm text-gray-700">اضغط على تسجيل الدخول لاستكمال البحث أو إدارة عقاراتك.</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:justify-end">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowLoginDialog(false)}
|
||||
className="w-full sm:w-auto px-5 py-3 rounded-xl border border-gray-300 text-gray-700 hover:bg-gray-100 transition-colors"
|
||||
>
|
||||
إغلاق
|
||||
</button>
|
||||
<Link
|
||||
href="/login"
|
||||
className="w-full sm:w-auto px-5 py-3 rounded-xl bg-amber-500 text-white font-semibold text-center hover:bg-amber-600 transition-colors"
|
||||
>
|
||||
تسجيل الدخول
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
69
app/page.js
69
app/page.js
@ -38,10 +38,15 @@ import AuthService from './services/AuthService';
|
||||
function mapApiProperty(item, index) {
|
||||
const info = item.propertyInformation || {};
|
||||
|
||||
const dailyPrice = item.dailyRent ?? item.monthlyRent ?? item.price ?? 0;
|
||||
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 propType = BuildingTypeKeys[info.buildingType] ?? BuildingTypeKeys[item.type] ?? 'apartment';
|
||||
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 = [];
|
||||
@ -58,14 +63,21 @@ function mapApiProperty(item, index) {
|
||||
? 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 || `عقار #${item.id || index + 1}`,
|
||||
description: info.description || '',
|
||||
type: propType,
|
||||
price: dailyPrice,
|
||||
priceUSD: dailyPrice,
|
||||
priceUnit: 'daily',
|
||||
price: price,
|
||||
priceUSD: price,
|
||||
priceUnit,
|
||||
listingType: isRentListing ? 'rent' : 'sale',
|
||||
location: {
|
||||
city: extractCity(info.address) || 'دمشق',
|
||||
district: info.address || '',
|
||||
@ -85,7 +97,9 @@ function mapApiProperty(item, index) {
|
||||
priceDisplay: {
|
||||
daily: dailyPrice,
|
||||
monthly: monthlyPrice,
|
||||
sale: salePrice,
|
||||
},
|
||||
ownerSource,
|
||||
bookings: [],
|
||||
_raw: item,
|
||||
};
|
||||
@ -175,6 +189,16 @@ export default function HomePage() {
|
||||
setSearchFilters(filters);
|
||||
|
||||
const filtered = allProperties.filter(property => {
|
||||
if (filters.mode === 'rent' && property.listingType !== 'rent') {
|
||||
return false;
|
||||
}
|
||||
if (filters.mode === 'sell' && property.listingType !== 'sale') {
|
||||
return false;
|
||||
}
|
||||
if (filters.mode === 'buy' && property.listingType !== 'sale') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filters.city && filters.city !== 'all' && property.location.city !== filters.city) {
|
||||
return false;
|
||||
}
|
||||
@ -194,6 +218,20 @@ export default function HomePage() {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@ -312,7 +350,7 @@ export default function HomePage() {
|
||||
</motion.p>
|
||||
</motion.div>
|
||||
|
||||
{!isOwner && <HeroSearch onSearch={applyFilters} />}
|
||||
{!isOwner && <HeroSearch onSearch={applyFilters} isAuthenticated={!!user} />}
|
||||
|
||||
{isOwner && (
|
||||
<motion.div
|
||||
@ -477,6 +515,25 @@ export default function HomePage() {
|
||||
searchFilters.priceRange === '2000-3000' ? '200$ - 300$' : 'أكثر من 300$'}
|
||||
</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">مصدر العرض: </span>
|
||||
<span className="font-bold text-gray-900">
|
||||
{searchFilters.ownerSource === 'all' ? 'الكل' :
|
||||
searchFilters.ownerSource === 'owner' ? 'من المالك' : 'من مكتب عقاري'}
|
||||
</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">نوع الإيجار: </span>
|
||||
<span className="font-bold text-gray-900">
|
||||
{searchFilters.rentPeriod === 'all' ? 'الكل' :
|
||||
searchFilters.rentPeriod === 'daily' ? 'إيجار يومي' : 'إيجار شهري'}
|
||||
</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">فقط المتاحة من اليوم</span>
|
||||
</div>
|
||||
)}
|
||||
</motion.div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user