259 lines
14 KiB
HTML
259 lines
14 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}{{ prop.title }} - NexHome{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="max-w-7xl mx-auto px-4 py-8">
|
|
<!-- Breadcrumb -->
|
|
<nav class="text-sm text-gray-500 mb-6">
|
|
<a href="/" class="hover:text-accent transition">Home</a>
|
|
<span class="mx-2">/</span>
|
|
<a href="/properties" class="hover:text-accent transition">Properties</a>
|
|
<span class="mx-2">/</span>
|
|
<a href="/properties?city={{ prop.city }}" class="hover:text-accent transition">{{ prop.city }}</a>
|
|
<span class="mx-2">/</span>
|
|
<span class="text-gray-800">{{ prop.title }}</span>
|
|
</nav>
|
|
|
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
|
<!-- Main Content -->
|
|
<div class="lg:col-span-2 space-y-6">
|
|
<!-- Image -->
|
|
<div class="relative rounded-2xl overflow-hidden bg-gray-200 h-96">
|
|
{% if primary_image %}
|
|
<img src="/static/{{ primary_image.image_path }}"
|
|
alt="{{ prop.title }}"
|
|
class="w-full h-full object-cover">
|
|
{% else %}
|
|
<div class="flex items-center justify-center h-full text-gray-400">
|
|
<svg class="w-20 h-20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"
|
|
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-4 0a1 1 0 01-1-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 01-1 1h-2z"/>
|
|
</svg>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if prop.is_featured %}
|
|
<span class="absolute top-4 left-4 bg-warm text-primary text-sm font-bold px-3 py-1 rounded-full">
|
|
Featured
|
|
</span>
|
|
{% endif %}
|
|
|
|
{% if prop.status == 'sold' %}
|
|
<span class="absolute top-4 right-4 bg-red-600 text-white text-sm font-bold px-3 py-1 rounded-full">
|
|
Sold
|
|
</span>
|
|
{% elif prop.status == 'pending' %}
|
|
<span class="absolute top-4 right-4 bg-yellow-500 text-white text-sm font-bold px-3 py-1 rounded-full">
|
|
Pending
|
|
</span>
|
|
{% endif %}
|
|
|
|
<!-- Floating favorite heart (top-right) -->
|
|
{% if user %}
|
|
<form method="POST" action="/properties/{{ prop.id }}/favorite" class="absolute top-4 right-4 {% if prop.status == 'sold' or prop.status == 'pending' %}top-14{% endif %}">
|
|
<button type="submit"
|
|
class="w-12 h-12 rounded-full flex items-center justify-center shadow-lg transition
|
|
{% if is_favorited %}
|
|
bg-red-500 text-white hover:bg-red-600
|
|
{% else %}
|
|
bg-white/90 text-gray-600 hover:bg-white hover:text-red-500
|
|
{% endif %}">
|
|
<svg class="w-6 h-6" fill="{% if is_favorited %}currentColor{% else %}none{% endif %}" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="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>
|
|
</button>
|
|
</form>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Thumbnail gallery -->
|
|
{% if images|length > 1 %}
|
|
<div class="flex gap-2 overflow-x-auto pb-2">
|
|
{% for img in images %}
|
|
<img src="/static/{{ img.image_path }}"
|
|
alt="Property image {{ loop.index }}"
|
|
class="h-20 w-28 object-cover rounded-lg flex-shrink-0 border-2 {% if img.is_primary %}border-accent{% else %}border-transparent{% endif %}">
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Title & Price -->
|
|
<div>
|
|
<div class="flex items-center justify-between flex-wrap gap-4">
|
|
<h1 class="text-3xl font-bold text-primary">{{ prop.title }}</h1>
|
|
<span class="text-3xl font-extrabold text-accent">${{ "{:,}".format(prop.price) }}</span>
|
|
</div>
|
|
<p class="flex items-center text-gray-500 mt-2">
|
|
<svg class="w-5 h-5 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="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"/>
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"/>
|
|
</svg>
|
|
{{ prop.address }}, {{ prop.city }}, {{ prop.state }} {{ prop.zip_code }}
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Key Stats -->
|
|
<div class="grid grid-cols-3 gap-4 bg-white rounded-xl p-6 shadow-sm">
|
|
<div class="text-center">
|
|
<div class="text-2xl font-bold text-primary">{{ prop.bedrooms }}</div>
|
|
<div class="text-sm text-gray-500">Bedrooms</div>
|
|
</div>
|
|
<div class="text-center border-x">
|
|
<div class="text-2xl font-bold text-primary">{{ prop.bathrooms }}</div>
|
|
<div class="text-sm text-gray-500">Bathrooms</div>
|
|
</div>
|
|
<div class="text-center">
|
|
<div class="text-2xl font-bold text-primary">{{ "{:,}".format(prop.area_sqft) }}</div>
|
|
<div class="text-sm text-gray-500">Sq Ft</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Description -->
|
|
<div class="bg-white rounded-xl p-6 shadow-sm">
|
|
<h2 class="text-xl font-bold text-primary mb-4">Description</h2>
|
|
<p class="text-gray-600 leading-relaxed whitespace-pre-line">{{ prop.description }}</p>
|
|
</div>
|
|
|
|
<!-- Details -->
|
|
<div class="bg-white rounded-xl p-6 shadow-sm">
|
|
<h2 class="text-xl font-bold text-primary mb-4">Property Details</h2>
|
|
<div class="grid grid-cols-2 gap-4 text-sm">
|
|
<div class="flex justify-between py-2 border-b">
|
|
<span class="text-gray-500">Property Type</span>
|
|
<span class="font-medium text-gray-800 capitalize">{{ prop.property_type }}</span>
|
|
</div>
|
|
<div class="flex justify-between py-2 border-b">
|
|
<span class="text-gray-500">Year Built</span>
|
|
<span class="font-medium text-gray-800">{{ prop.year_built or 'N/A' }}</span>
|
|
</div>
|
|
<div class="flex justify-between py-2 border-b">
|
|
<span class="text-gray-500">Status</span>
|
|
<span class="font-medium text-gray-800 capitalize">{{ prop.status }}</span>
|
|
</div>
|
|
<div class="flex justify-between py-2 border-b">
|
|
<span class="text-gray-500">Listed</span>
|
|
<span class="font-medium text-gray-800">{{ prop.created_at.strftime("%B %d, %Y") }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sidebar -->
|
|
<div class="space-y-6">
|
|
<!-- Contact Card -->
|
|
<div class="bg-white rounded-xl p-6 shadow-sm border-2 border-accent/20">
|
|
<h3 class="font-bold text-primary mb-4 text-lg">Contact Seller</h3>
|
|
|
|
<!-- Favorite button - large and prominent -->
|
|
{% if user %}
|
|
<form method="POST" action="/properties/{{ prop.id }}/favorite" class="mb-4">
|
|
<button type="submit"
|
|
class="w-full py-3.5 rounded-xl font-semibold transition text-base flex items-center justify-center gap-2
|
|
{% if is_favorited %}
|
|
bg-red-50 text-red-600 border-2 border-red-300 hover:bg-red-100
|
|
{% else %}
|
|
bg-gray-50 text-gray-700 border-2 border-gray-200 hover:border-red-300 hover:text-red-500 hover:bg-red-50
|
|
{% endif %}">
|
|
<svg class="w-6 h-6" fill="{% if is_favorited %}currentColor{% else %}none{% endif %}" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="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>
|
|
{% if is_favorited %}Saved to Favorites{% else %}Add to Favorites{% endif %}
|
|
</button>
|
|
</form>
|
|
{% else %}
|
|
<a href="/auth/login"
|
|
class="block w-full text-center py-3.5 rounded-xl font-semibold transition text-base bg-accent hover:bg-blue-700 text-white mb-4">
|
|
Login to Save
|
|
</a>
|
|
{% endif %}
|
|
|
|
<!-- Contact info -->
|
|
<div class="space-y-3">
|
|
{% if prop.contact_email %}
|
|
<a href="mailto:{{ prop.contact_email }}"
|
|
class="flex items-center gap-3 p-3 bg-gray-50 rounded-lg hover:bg-accent/5 transition group">
|
|
<div class="w-10 h-10 bg-accent/10 rounded-full flex items-center justify-center flex-shrink-0 group-hover:bg-accent/20 transition">
|
|
<svg class="w-5 h-5 text-accent" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
|
|
</svg>
|
|
</div>
|
|
<div class="min-w-0">
|
|
<div class="text-xs text-gray-500">Email</div>
|
|
<div class="text-sm font-medium text-gray-800 truncate">{{ prop.contact_email }}</div>
|
|
</div>
|
|
</a>
|
|
{% endif %}
|
|
|
|
{% if prop.contact_phone %}
|
|
<a href="tel:{{ prop.contact_phone }}"
|
|
class="flex items-center gap-3 p-3 bg-gray-50 rounded-lg hover:bg-accent/5 transition group">
|
|
<div class="w-10 h-10 bg-green-100 rounded-full flex items-center justify-center flex-shrink-0 group-hover:bg-green-200 transition">
|
|
<svg class="w-5 h-5 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z"/>
|
|
</svg>
|
|
</div>
|
|
<div class="min-w-0">
|
|
<div class="text-xs text-gray-500">Phone</div>
|
|
<div class="text-sm font-medium text-gray-800">{{ prop.contact_phone }}</div>
|
|
</div>
|
|
</a>
|
|
{% endif %}
|
|
|
|
{% if not prop.contact_email and not prop.contact_phone %}
|
|
<!-- Fallback to owner info -->
|
|
<div class="flex items-center gap-3 p-3 bg-gray-50 rounded-lg">
|
|
<div class="w-12 h-12 bg-primary rounded-full flex items-center justify-center text-white font-bold text-lg flex-shrink-0">
|
|
{{ prop.owner.username[0]|upper }}
|
|
</div>
|
|
<div class="min-w-0">
|
|
<div class="font-semibold text-gray-800">{{ prop.owner.username }}</div>
|
|
{% if prop.owner.full_name %}
|
|
<div class="text-sm text-gray-500">{{ prop.owner.full_name }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% if user.id == prop.owner_id %}
|
|
<div class="mt-4 pt-4 border-t space-y-2">
|
|
<a href="/properties/{{ prop.id }}/edit"
|
|
class="block w-full text-center py-2.5 bg-accent hover:bg-blue-700 text-white rounded-lg font-medium transition text-sm">
|
|
Edit Listing
|
|
</a>
|
|
<form method="POST" action="/properties/{{ prop.id }}/delete"
|
|
onsubmit="return confirm('Are you sure you want to delete this listing?')">
|
|
<button type="submit"
|
|
class="w-full py-2.5 bg-red-50 hover:bg-red-100 text-red-600 border border-red-200 rounded-lg font-medium transition text-sm">
|
|
Delete Listing
|
|
</button>
|
|
</form>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Listed By -->
|
|
<div class="bg-white rounded-xl p-6 shadow-sm">
|
|
<h3 class="font-bold text-primary mb-4">Listed By</h3>
|
|
<div class="flex items-center space-x-3">
|
|
<div class="w-12 h-12 bg-primary rounded-full flex items-center justify-center text-white font-bold text-lg">
|
|
{{ prop.owner.username[0]|upper }}
|
|
</div>
|
|
<div>
|
|
<div class="font-semibold text-gray-800">{{ prop.owner.username }}</div>
|
|
{% if prop.owner.full_name %}
|
|
<div class="text-sm text-gray-500">{{ prop.owner.full_name }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|