Initial Commit

This commit is contained in:
2026-06-11 15:05:08 +08:00
commit ea8e41e688
21 changed files with 2317 additions and 0 deletions
+140
View File
@@ -0,0 +1,140 @@
{% extends "base.html" %}
{% block title %}Dashboard - NexHome{% endblock %}
{% block content %}
<div class="max-w-7xl mx-auto px-4 py-10">
<div class="flex items-center justify-between mb-8">
<div>
<h1 class="text-3xl font-bold text-primary">Dashboard</h1>
<p class="text-gray-500 mt-1">Welcome back, {{ user.username }}!</p>
</div>
<a href="/properties/new"
class="bg-accent hover:bg-blue-700 text-white font-semibold px-6 py-2.5 rounded-lg transition text-sm">
+ New Listing
</a>
</div>
<!-- Tabs -->
<div class="flex gap-1 bg-gray-100 p-1 rounded-lg w-fit mb-8">
<button id="tab-listings" onclick="showTab('listings')"
class="tab-btn px-6 py-2 rounded-md text-sm font-medium bg-white shadow text-primary transition">
My Listings ({{ my_properties|length }})
</button>
<button id="tab-favorites" onclick="showTab('favorites')"
class="tab-btn px-6 py-2 rounded-md text-sm font-medium text-gray-500 hover:text-gray-700 transition">
My Favorites ({{ favorites|length }})
</button>
</div>
<!-- My Listings -->
<div id="panel-listings">
{% if my_properties %}
<div class="bg-white rounded-xl shadow-md overflow-hidden">
<table class="w-full text-sm">
<thead class="bg-gray-50 text-gray-600 text-left">
<tr>
<th class="px-6 py-3 font-medium">Property</th>
<th class="px-6 py-3 font-medium">Price</th>
<th class="px-6 py-3 font-medium">Status</th>
<th class="px-6 py-3 font-medium">Created</th>
<th class="px-6 py-3 font-medium text-right">Actions</th>
</tr>
</thead>
<tbody class="divide-y">
{% for prop in my_properties %}
<tr class="hover:bg-gray-50">
<td class="px-6 py-4">
<div class="flex items-center space-x-3">
{% if prop.primary_image %}
<img src="/static/{{ prop.primary_image.image_path }}" alt=""
class="w-14 h-10 rounded object-cover">
{% else %}
<div class="w-14 h-10 bg-gray-200 rounded flex items-center justify-center text-gray-400">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3"/>
</svg>
</div>
{% endif %}
<div>
<a href="/properties/{{ prop.id }}" class="font-medium text-gray-800 hover:text-accent transition">
{{ prop.title }}
</a>
<div class="text-xs text-gray-500">{{ prop.city }}, {{ prop.state }}</div>
</div>
</div>
</td>
<td class="px-6 py-4 font-semibold text-primary">${{ "{:,}".format(prop.price) }}</td>
<td class="px-6 py-4">
<span class="px-2.5 py-1 rounded-full text-xs font-medium
{% if prop.status == 'active' %}bg-green-100 text-green-800
{% elif prop.status == 'pending' %}bg-yellow-100 text-yellow-800
{% else %}bg-red-100 text-red-800{% endif %}">
{{ prop.status|capitalize }}
</span>
</td>
<td class="px-6 py-4 text-gray-500">{{ prop.created_at.strftime("%b %d, %Y") }}</td>
<td class="px-6 py-4 text-right space-x-2">
<a href="/properties/{{ prop.id }}/edit" class="text-accent hover:text-blue-700 transition font-medium">Edit</a>
<form method="POST" action="/properties/{{ prop.id }}/delete" class="inline"
onsubmit="return confirm('Delete this listing?')">
<button type="submit" class="text-red-500 hover:text-red-700 transition font-medium">Delete</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center py-16 text-gray-400">
<svg class="w-16 h-16 mx-auto mb-4" 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>
<p class="text-lg mb-4">You haven't listed any properties yet.</p>
<a href="/properties/new" class="bg-accent hover:bg-blue-700 text-white px-6 py-3 rounded-lg font-medium transition inline-block">
Create Your First Listing
</a>
</div>
{% endif %}
</div>
<!-- My Favorites -->
<div id="panel-favorites" class="hidden">
{% if favorites %}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{% from "components/property_card.html" import property_card %}
{% for fav_prop in favorites %}
{{ property_card(fav_prop) }}
{% endfor %}
</div>
{% else %}
<div class="text-center py-16 text-gray-400">
<svg class="w-16 h-16 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"
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>
<p class="text-lg mb-4">No favorites yet.</p>
<a href="/properties" class="bg-accent hover:bg-blue-700 text-white px-6 py-3 rounded-lg font-medium transition inline-block">
Browse Properties
</a>
</div>
{% endif %}
</div>
</div>
<script>
function showTab(tab) {
document.getElementById('panel-listings').classList.toggle('hidden', tab !== 'listings');
document.getElementById('panel-favorites').classList.toggle('hidden', tab !== 'favorites');
document.querySelectorAll('.tab-btn').forEach(btn => {
btn.classList.remove('bg-white', 'shadow', 'text-primary');
btn.classList.add('text-gray-500');
});
const active = document.getElementById('tab-' + tab);
active.classList.add('bg-white', 'shadow', 'text-primary');
active.classList.remove('text-gray-500');
}
</script>
{% endblock %}