06/18 Phase 7

This commit is contained in:
2026-06-18 09:29:33 -04:00
parent d1383a9835
commit d54e5e2127
16 changed files with 563 additions and 8 deletions
+7 -1
View File
@@ -1,5 +1,11 @@
{% extends "base.html" %}
{% block title %}{{ _('Browse listings') }}{% endblock %}
{% block title %}
{% if filters.get('q') %}{{ _('Results for "%(q)s"', q=filters.get('q')) }} — Classifieds
{% elif filters.get('category') %}{{ _('Browse listings') }} — Classifieds
{% else %}{{ _('Browse listings') }} — Classifieds
{% endif %}
{% endblock %}
{% block meta_description %}<meta name="description" content="{{ _('Browse local classifieds — for sale, jobs, services, and more.') }}">{% endblock %}
{% block content %}
<div class="browse">
<aside class="filters card">
+58 -2
View File
@@ -1,5 +1,27 @@
{% extends "base.html" %}
{% block title %}{{ listing.title }}{% endblock %}
{% block title %}{{ listing.title }} — Classifieds{% endblock %}
{% block meta_description %}<meta name="description" content="{{ (listing.body[:155] | replace('"', '&quot;') | replace('\n', ' ')) }}...">{% endblock %}
{% block og_title %}{{ listing.title }}{% endblock %}
{% block og_description %}{{ listing.body[:200] | replace('\n', ' ') }}{% endblock %}
{% block og_type %}product{% endblock %}
{% block og_image %}
{% if listing.images %}
<meta property="og:image" content="{{ request.host_url }}{{ url_for('listings.media', rel=listing.images[0].path)[1:] }}">
{% endif %}
{% endblock %}
{% block head %}
<script type="application/ld+json">
{
"@context": "https://schema.org/",
"@type": "Product",
"name": {{ listing.title | tojson }},
"description": {{ (listing.body[:500] | replace('\n', ' ')) | tojson }},
{% if listing.images %}"image": "{{ request.host_url }}{{ url_for('listings.media', rel=listing.images[0].path)[1:] }}",{% endif %}
{% if listing.price_cents %}"offers": {"@type": "Offer", "price": "{{ '%.2f'|format(listing.price_cents/100) }}", "priceCurrency": "USD"},{% endif %}
"seller": {"@type": "Person", "name": {{ listing.user.display_name | tojson }}}
}
</script>
{% endblock %}
{% block content %}
<article class="detail">
<div class="detail-main card">
@@ -17,7 +39,7 @@
{% if listing.images %}
<div class="gallery">
{% for img in listing.images %}
<img src="{{ url_for('listings.media', rel=img.path) }}" alt="">
<img src="{{ url_for('listings.media', rel=img.path) }}" alt="{{ listing.title }}" loading="{{ 'eager' if loop.first else 'lazy' }}">
{% endfor %}
</div>
{% endif %}
@@ -39,6 +61,12 @@
<div class="seller">
<strong>{{ listing.user.display_name }}</strong>
{% if listing.user.verified %}<span class="badge ok">{{ _('Verified') }}</span>{% endif %}
{% if listing_rating and listing_rating.avg %}
<div class="review-stars" style="font-size:14px">
{{ '★' * (listing_rating.avg | round | int) }}{{ '☆' * (5 - (listing_rating.avg | round | int)) }}
<span class="muted" style="font-size:12px">({{ listing_rating.count }})</span>
</div>
{% endif %}
</div>
{% if is_owner %}
<a class="btn" href="{{ url_for('listings.edit', listing_id=listing.id) }}">{{ _('Edit') }}</a>
@@ -78,7 +106,35 @@
{{ _('Sign in to contact') }}
</a>
{% endif %}
{# Leave-review button for sold listings when viewer had a conversation #}
{% if current_user.is_authenticated and listing.status.value == 'sold'
and not is_owner and can_review %}
<a class="btn ghost" href="{{ url_for('listings.leave_review', listing_id=listing.id) }}">
{{ _('Leave a review') }}
</a>
{% endif %}
{% endif %}
</aside>
</article>
{# Reviews section #}
{% if listing.reviews.count() %}
<section class="card" style="margin-top:16px">
<h3>{{ _('Reviews') }} ({{ listing.reviews.count() }})</h3>
{% set rating_info = listing_rating %}
{% if rating_info and rating_info.avg %}
<div class="rating-summary">
<span class="review-stars">{{ '★' * (rating_info.avg | round | int) }}{{ '☆' * (5 - (rating_info.avg | round | int)) }}</span>
{{ '%.1f'|format(rating_info.avg) }} / 5 &nbsp;<span class="muted small">({{ rating_info.count }})</span>
</div>
{% endif %}
{% for review in listing.reviews.order_by(none).all() %}
<div class="review-block">
<div class="review-stars">{{ '★' * review.rating }}{{ '☆' * (5 - review.rating) }}</div>
<div class="muted small">{{ review.author.display_name }} · {{ review.created_at.strftime('%Y-%m-%d') }}</div>
{% if review.body %}<p>{{ review.body }}</p>{% endif %}
</div>
{% endfor %}
</section>
{% endif %}
{% endblock %}
+32
View File
@@ -0,0 +1,32 @@
{% extends "base.html" %}
{% block title %}{{ _('Leave a review') }} — {{ listing.title }}{% endblock %}
{% block content %}
<div class="card narrow">
<h2>{{ _('Leave a review for "%(t)s"', t=listing.title) }}</h2>
<p class="muted">{{ _('Sold by %(name)s', name=listing.user.display_name) }}</p>
<form method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<div class="field">
<label>{{ _('Rating') }}</label>
<div class="rating-picker">
{% for v in [5, 4, 3, 2, 1] %}
<label>
<input type="radio" name="rating" value="{{ v }}" required>
{{ '★' * v }}{{ '☆' * (5 - v) }}
</label>
{% endfor %}
</div>
</div>
<div class="field">
<label>{{ _('Comment (optional)') }}</label>
<textarea class="input" name="body" rows="4" maxlength="1000"></textarea>
</div>
<button class="btn" type="submit">{{ _('Submit review') }}</button>
<a class="btn ghost" href="{{ url_for('listings.detail', listing_id=listing.id) }}">{{ _('Cancel') }}</a>
</form>
</div>
{% endblock %}