feat: classified expiry handling - badges, extend button, homepage filter
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
- Expired classifieds show 'Wygasło' badge on list and detail view - Closed classifieds show 'Zamknięte' badge on list - Author can extend by 30 days with one click - Homepage 'Nowe na portalu' excludes expired classifieds - List shows all classifieds, active first Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9027e4fafc
commit
ca5e7fd0a8
@ -35,9 +35,8 @@ def index():
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
query = db.query(Classified).filter(
|
||||
Classified.is_active == True
|
||||
)
|
||||
now = datetime.now()
|
||||
query = db.query(Classified)
|
||||
|
||||
# Filtry
|
||||
if listing_type:
|
||||
@ -45,8 +44,15 @@ def index():
|
||||
if category:
|
||||
query = query.filter(Classified.category == category)
|
||||
|
||||
# Sortowanie - najnowsze pierwsze
|
||||
query = query.order_by(Classified.created_at.desc())
|
||||
# Sortowanie: aktywne i niewygasłe na górze, potem reszta
|
||||
from sqlalchemy import case
|
||||
query = query.order_by(
|
||||
case(
|
||||
(Classified.is_active == True, 0),
|
||||
else_=1
|
||||
),
|
||||
Classified.created_at.desc()
|
||||
)
|
||||
|
||||
total = query.count()
|
||||
classifieds = query.limit(per_page).offset((page - 1) * per_page).all()
|
||||
@ -310,6 +316,39 @@ def edit(classified_id):
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/<int:classified_id>/przedluz', methods=['POST'], endpoint='classifieds_extend')
|
||||
@login_required
|
||||
@member_required
|
||||
def extend(classified_id):
|
||||
"""Przedłuż ogłoszenie o 30 dni"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
classified = db.query(Classified).filter(
|
||||
Classified.id == classified_id,
|
||||
Classified.author_id == current_user.id
|
||||
).first()
|
||||
|
||||
if not classified:
|
||||
return jsonify({'success': False, 'error': 'Ogłoszenie nie istnieje lub brak uprawnień'}), 404
|
||||
|
||||
# Przedłuż od teraz lub od daty wygaśnięcia (jeśli jeszcze aktywne)
|
||||
base_date = datetime.now()
|
||||
if classified.expires_at and classified.expires_at > base_date:
|
||||
base_date = classified.expires_at
|
||||
classified.expires_at = base_date + timedelta(days=30)
|
||||
classified.is_active = True
|
||||
classified.updated_at = datetime.now()
|
||||
db.commit()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': 'Ogłoszenie przedłużone o 30 dni',
|
||||
'new_expires': classified.expires_at.strftime('%d.%m.%Y')
|
||||
})
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@bp.route('/<int:classified_id>/zakoncz', methods=['POST'], endpoint='classifieds_close')
|
||||
@login_required
|
||||
@member_required
|
||||
|
||||
@ -218,9 +218,11 @@ def index():
|
||||
latest_classifieds = []
|
||||
try:
|
||||
from database import Classified
|
||||
from datetime import datetime as dt
|
||||
latest_classifieds = db.query(Classified).filter(
|
||||
Classified.is_active == True,
|
||||
Classified.is_test == False
|
||||
Classified.is_test == False,
|
||||
(Classified.expires_at == None) | (Classified.expires_at > dt.now())
|
||||
).order_by(Classified.created_at.desc()).limit(2).all()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@ -293,6 +293,11 @@
|
||||
</div>
|
||||
<div class="classified-title">
|
||||
<a href="{{ url_for('classifieds.classifieds_view', classified_id=classified.id) }}">{{ classified.title }}</a>
|
||||
{% if classified.is_expired %}
|
||||
<span style="display:inline-block; background:#fef2f2; color:#dc2626; font-size:11px; font-weight:600; padding:2px 8px; border-radius:4px; vertical-align:middle; margin-left:6px;">Wygasło</span>
|
||||
{% elif not classified.is_active %}
|
||||
<span style="display:inline-block; background:#f3f4f6; color:#6b7280; font-size:11px; font-weight:600; padding:2px 8px; border-radius:4px; vertical-align:middle; margin-left:6px;">Zamknięte</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="classified-description">
|
||||
{{ classified.description[:200] }}{% if classified.description|length > 200 %}...{% endif %}
|
||||
|
||||
@ -684,8 +684,14 @@
|
||||
<span class="inactive-badge">Nieaktywne</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if classified.is_expired %}
|
||||
<span style="display:inline-block; background:#fef2f2; color:#dc2626; font-size:var(--font-size-sm); font-weight:600; padding:4px 12px; border-radius:var(--radius); border:1px solid #fecaca;">Wygasło</span>
|
||||
{% endif %}
|
||||
{% if classified.author_id == current_user.id %}
|
||||
<a href="{{ url_for('classifieds.classifieds_edit', classified_id=classified.id) }}" class="btn btn-primary btn-sm">Edytuj</a>
|
||||
{% if classified.is_expired or not classified.is_active %}
|
||||
<button class="btn btn-primary btn-sm" onclick="extendClassified()" style="background:#10b981;border-color:#10b981;">Przedłuż o 30 dni</button>
|
||||
{% endif %}
|
||||
<button class="btn btn-secondary btn-sm close-btn" onclick="closeClassified()">Zamknij ogloszenie</button>
|
||||
{% endif %}
|
||||
{% if current_user.is_authenticated and current_user.can_access_admin_panel() %}
|
||||
@ -1231,6 +1237,24 @@ async function toggleQuestionVisibility(questionId) {
|
||||
}
|
||||
}
|
||||
|
||||
async function extendClassified() {
|
||||
try {
|
||||
const resp = await fetch('{{ url_for("classifieds.classifieds_extend", classified_id=classified.id) }}', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken }
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (data.success) {
|
||||
showToast('Ogłoszenie przedłużone do ' + data.new_expires, 'success');
|
||||
setTimeout(() => location.reload(), 1000);
|
||||
} else {
|
||||
showToast(data.error || 'Błąd', 'error');
|
||||
}
|
||||
} catch (e) {
|
||||
showToast('Błąd połączenia', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function openLightbox(src) {
|
||||
document.getElementById('lightboxImage').src = src;
|
||||
document.getElementById('lightbox').classList.add('active');
|
||||
|
||||
Loading…
Reference in New Issue
Block a user