feat: Add toggle button to hide/show test items on B2B board
- Add is_test field to Classified model - Add test-item styling (opacity + gray border + badge) - Add yellow toggle button with localStorage persistence - Add script to mark existing classifieds as test
This commit is contained in:
parent
e7d32b6b06
commit
ffc6d8219f
@ -1139,6 +1139,7 @@ class Classified(Base):
|
|||||||
# Status
|
# Status
|
||||||
is_active = Column(Boolean, default=True)
|
is_active = Column(Boolean, default=True)
|
||||||
is_ai_generated = Column(Boolean, default=False)
|
is_ai_generated = Column(Boolean, default=False)
|
||||||
|
is_test = Column(Boolean, default=False) # Oznaczenie dla testowych ogłoszeń
|
||||||
expires_at = Column(DateTime) # Auto-wygaśnięcie po 30 dniach
|
expires_at = Column(DateTime) # Auto-wygaśnięcie po 30 dniach
|
||||||
views_count = Column(Integer, default=0)
|
views_count = Column(Integer, default=0)
|
||||||
|
|
||||||
|
|||||||
51
scripts/mark_test_classifieds.py
Normal file
51
scripts/mark_test_classifieds.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Oznacz wszystkie obecne ogłoszenia B2B jako testowe."""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
# Load .env first
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
|
from sqlalchemy import text
|
||||||
|
from database import SessionLocal, Classified
|
||||||
|
|
||||||
|
def main():
|
||||||
|
db = SessionLocal()
|
||||||
|
|
||||||
|
# First, ensure the is_test column exists
|
||||||
|
try:
|
||||||
|
db.execute(text("ALTER TABLE classifieds ADD COLUMN IF NOT EXISTS is_test BOOLEAN DEFAULT FALSE"))
|
||||||
|
db.commit()
|
||||||
|
print("Kolumna is_test dodana (lub już istnieje)")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Uwaga przy dodawaniu kolumny: {e}")
|
||||||
|
db.rollback()
|
||||||
|
|
||||||
|
# Get all classifieds
|
||||||
|
classifieds = db.query(Classified).all()
|
||||||
|
|
||||||
|
if not classifieds:
|
||||||
|
print("Brak ogłoszeń w bazie danych.")
|
||||||
|
db.close()
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"Znaleziono {len(classifieds)} ogłoszeń do oznaczenia jako testowe...")
|
||||||
|
print()
|
||||||
|
|
||||||
|
for c in classifieds:
|
||||||
|
c.is_test = True
|
||||||
|
print(f" ID {c.id}: {c.title[:50]}... ({c.listing_type})")
|
||||||
|
|
||||||
|
db.commit()
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
print()
|
||||||
|
print(f"Oznaczono {len(classifieds)} ogłoszeń jako testowe")
|
||||||
|
print("Gotowe!")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@ -140,6 +140,39 @@
|
|||||||
color: #3730a3;
|
color: #3730a3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Test item styling */
|
||||||
|
.classified-card.test-item {
|
||||||
|
opacity: 0.6;
|
||||||
|
border-right: 4px solid #9ca3af;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test-badge {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
font-weight: 500;
|
||||||
|
background: #f3f4f6;
|
||||||
|
color: #6b7280;
|
||||||
|
margin-left: var(--spacing-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-test-btn {
|
||||||
|
background: #fef3c7;
|
||||||
|
border-color: #fcd34d;
|
||||||
|
color: #92400e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-test-btn:hover {
|
||||||
|
background: #fde68a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-test-btn.active {
|
||||||
|
background: var(--primary);
|
||||||
|
border-color: var(--primary);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
.classified-title {
|
.classified-title {
|
||||||
font-size: var(--font-size-lg);
|
font-size: var(--font-size-lg);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@ -240,15 +273,23 @@
|
|||||||
<a href="{{ url_for('classifieds_index', type=listing_type, category=cat_value) }}" class="filter-btn {% if category_filter == cat_value %}active{% endif %}">{{ cat_label }}</a>
|
<a href="{{ url_for('classifieds_index', type=listing_type, category=cat_value) }}" class="filter-btn {% if category_filter == cat_value %}active{% endif %}">{{ cat_label }}</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="filter-group" style="margin-left: auto;">
|
||||||
|
<button type="button" id="toggleTestBtn" class="filter-btn toggle-test-btn" onclick="toggleTestItems()">
|
||||||
|
<span class="hide-label">🙈 Ukryj testowe</span>
|
||||||
|
<span class="show-label" style="display:none;">👁 Pokaż testowe</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="classifieds-grid">
|
<div class="classifieds-grid">
|
||||||
{% if classifieds %}
|
{% if classifieds %}
|
||||||
{% for classified in classifieds %}
|
{% for classified in classifieds %}
|
||||||
<div class="classified-card {{ classified.listing_type }}">
|
<div class="classified-card {{ classified.listing_type }} {% if classified.is_test %}test-item{% endif %}">
|
||||||
<div class="classified-header">
|
<div class="classified-header">
|
||||||
<span class="classified-type {{ classified.listing_type }}">{{ 'Szukam' if classified.listing_type == 'szukam' else 'Oferuje' }}</span>
|
<span class="classified-type {{ classified.listing_type }}">{{ 'Szukam' if classified.listing_type == 'szukam' else 'Oferuje' }}</span>
|
||||||
<span class="classified-category category-{{ classified.category }}">{{ classified.category|replace('uslugi', 'Usługi')|replace('produkty', 'Produkty')|replace('wspolpraca', 'Współpraca')|replace('praca', 'Praca')|replace('inne', 'Inne')|replace('nieruchomosci', 'Nieruchomości') }}</span>
|
<span class="classified-category category-{{ classified.category }}">{{ classified.category|replace('uslugi', 'Usługi')|replace('produkty', 'Produkty')|replace('wspolpraca', 'Współpraca')|replace('praca', 'Praca')|replace('inne', 'Inne')|replace('nieruchomosci', 'Nieruchomości') }}</span>
|
||||||
|
{% if classified.is_test %}<span class="test-badge">Testowe</span>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="classified-title">
|
<div class="classified-title">
|
||||||
<a href="{{ url_for('classifieds_view', classified_id=classified.id) }}">{{ classified.title }}</a>
|
<a href="{{ url_for('classifieds_view', classified_id=classified.id) }}">{{ classified.title }}</a>
|
||||||
@ -287,3 +328,37 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_js %}
|
||||||
|
function setTestItemsVisibility(hidden) {
|
||||||
|
const btn = document.getElementById('toggleTestBtn');
|
||||||
|
const hideLabel = btn.querySelector('.hide-label');
|
||||||
|
const showLabel = btn.querySelector('.show-label');
|
||||||
|
const testItems = document.querySelectorAll('.test-item');
|
||||||
|
|
||||||
|
if (hidden) {
|
||||||
|
testItems.forEach(t => t.style.display = 'none');
|
||||||
|
hideLabel.style.display = 'none';
|
||||||
|
showLabel.style.display = '';
|
||||||
|
btn.classList.add('active');
|
||||||
|
} else {
|
||||||
|
testItems.forEach(t => t.style.display = '');
|
||||||
|
hideLabel.style.display = '';
|
||||||
|
showLabel.style.display = 'none';
|
||||||
|
btn.classList.remove('active');
|
||||||
|
}
|
||||||
|
localStorage.setItem('hideTestClassifieds', hidden ? 'true' : 'false');
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleTestItems() {
|
||||||
|
const isCurrentlyHidden = localStorage.getItem('hideTestClassifieds') === 'true';
|
||||||
|
setTestItemsVisibility(!isCurrentlyHidden);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply saved preference on page load
|
||||||
|
(function() {
|
||||||
|
if (localStorage.getItem('hideTestClassifieds') === 'true') {
|
||||||
|
setTestItemsVisibility(true);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
{% endblock %}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user