141 lines
6.5 KiB
HTML
141 lines
6.5 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}{{ listing.title }} — Classifieds{% endblock %}
|
|
{% block meta_description %}<meta name="description" content="{{ (listing.body[:155] | replace('"', '"') | 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">
|
|
<h1>{{ listing.title }}</h1>
|
|
<div class="detail-meta">
|
|
{% if listing.price_display %}<span class="price big">{{ listing.price_display }}</span>{% endif %}
|
|
<span class="badge cat">{{ listing.category.name }}</span>
|
|
{% if not listing.is_live %}<span class="badge warn">{{ listing.status.value }}</span>{% endif %}
|
|
</div>
|
|
<p class="muted small">
|
|
{% if listing.city %}{{ listing.city }}, {{ listing.state }} {{ listing.zip }}{% endif %}
|
|
· {{ _('%(n)s views', n=listing.view_count) }}
|
|
</p>
|
|
|
|
{% if listing.images %}
|
|
<div class="gallery">
|
|
{% for img in listing.images %}
|
|
<img src="{{ url_for('listings.media', rel=img.path) }}" alt="{{ listing.title }}" loading="{{ 'eager' if loop.first else 'lazy' }}">
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="body">{{ listing.body | replace('\n','<br>') | safe }}</div>
|
|
|
|
{% if listing.attributes %}
|
|
<table class="attrs">
|
|
{% for f in listing.category.fields() %}
|
|
{% if listing.attributes.get(f.name) is not none %}
|
|
<tr><th>{{ f.label }}</th><td>{{ listing.attributes.get(f.name) }}</td></tr>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</table>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<aside class="detail-side card">
|
|
<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>
|
|
<a class="btn ghost" href="{{ url_for('payments.boost_listing', listing_id=listing.id) }}">{{ _('⚡ Boost') }}</a>
|
|
{% if listing.status.value == 'active' %}
|
|
<form method="post" action="{{ url_for('listings.mark_sold', listing_id=listing.id) }}">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"><button class="btn ghost" type="submit">{{ _('Mark sold') }}</button>
|
|
</form>
|
|
{% endif %}
|
|
<form method="post" action="{{ url_for('listings.delete', listing_id=listing.id) }}"
|
|
onsubmit="return confirm('{{ _('Delete this listing?') }}')">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"><button class="btn danger" type="submit">{{ _('Delete') }}</button>
|
|
</form>
|
|
{% else %}
|
|
{% if current_user.is_authenticated %}
|
|
<a class="btn" href="{{ url_for('messaging.start_conversation', listing_id=listing.id) }}">
|
|
{{ _('Contact seller') }}
|
|
</a>
|
|
<form method="post"
|
|
action="{{ url_for('messaging.toggle_favorite', listing_id=listing.id) }}">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
|
<button class="btn ghost" type="submit">{{ _('♥ Save listing') }}</button>
|
|
</form>
|
|
<form method="post" class="report-form"
|
|
action="{{ url_for('listings.report', listing_id=listing.id) }}">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
|
<select class="input" name="reason">
|
|
{% for r in ['spam', 'scam', 'offensive', 'duplicate', 'miscategorized', 'other'] %}
|
|
<option value="{{ r }}">{{ r }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
<input class="input" name="note" placeholder="{{ _('Optional note') }}">
|
|
<button class="btn ghost tiny" type="submit">{{ _('Report listing') }}</button>
|
|
</form>
|
|
{% else %}
|
|
<a class="btn" href="{{ url_for('auth.login') }}?next={{ request.path }}">
|
|
{{ _('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 <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 %}
|