feat: Add section visibility toggle for company profiles
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

Allow company owners, managers, and admins to hide specific profile
sections from visitors. Hidden sections remain visible to authorized
users with a "Ukryta" badge. Includes migration, API endpoint,
edit UI tab, and conditional rendering for all 15 profile sections.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-02-17 09:59:32 +01:00
parent 74a13507d2
commit 2c0b068428
5 changed files with 321 additions and 31 deletions

View File

@ -5,7 +5,7 @@ Company Edit Routes
Routes for editing company profiles by authorized users.
"""
from flask import render_template, request, redirect, url_for, flash
from flask import render_template, request, redirect, url_for, flash, jsonify
from flask_login import login_required, current_user
from blueprints.public import bp
from sqlalchemy import or_
@ -20,6 +20,11 @@ EMPLOYEE_COUNT_WHITELIST = ['1-5', '6-10', '11-25', '26-50', '51-100', '101-250'
VALID_SOCIAL_PLATFORMS = ['facebook', 'linkedin', 'instagram', 'youtube', 'twitter', 'tiktok']
VALID_WEBSITE_TYPES = ['website', 'store', 'booking', 'blog', 'portfolio', 'other']
EDITABLE_SOURCES = [None, 'manual_edit', 'manual']
VALID_SECTION_KEYS = [
'about', 'services', 'board', 'owner_info', 'registry', 'legal',
'contact', 'social_media', 'recommendations', 'website', 'seo_audit',
'gbp_audit', 'social_audit', 'it_audit', 'news',
]
@bp.route('/firma/edytuj')
@ -67,6 +72,24 @@ def company_edit(company_id=None):
editable_contacts = [c for c in contacts if c.source in EDITABLE_SOURCES]
section_definitions = [
('about', 'O firmie', 'Opis firmy, historia, wartości'),
('services', 'Usługi i kompetencje', 'Oferowane usługi, technologie, obszar działania'),
('board', 'Zarząd i Wspólnicy', 'Osoby w zarządzie, wspólnicy, prokurenci'),
('owner_info', 'Właściciel (JDG)', 'Dane właściciela jednoosobowej działalności'),
('registry', 'Dane z rejestrów urzędowych', 'Dane z CEIDG/KRS, PKD, adresy rejestrowe'),
('legal', 'Informacje prawne i biznesowe', 'NIP, REGON, KRS, forma prawna'),
('contact', 'Dane kontaktowe', 'Telefony, e-mail, adres, dodatkowe kontakty'),
('social_media', 'Social Media', 'Profile w mediach społecznościowych'),
('recommendations', 'Rekomendacje', 'Rekomendacje od innych członków Izby'),
('website', 'Strona WWW', 'Link do strony i podgląd'),
('seo_audit', 'Analiza SEO', 'Wyniki audytu SEO strony internetowej'),
('gbp_audit', 'Audyt Google Business Profile', 'Wyniki audytu wizytówki Google'),
('social_audit', 'Audyt Social Media', 'Wyniki audytu mediów społecznościowych'),
('it_audit', 'Audyt IT', 'Wyniki audytu infrastruktury IT'),
('news', 'Aktualności i wydarzenia', 'Najnowsze wydarzenia firmy'),
]
return render_template(
'company_edit.html',
company=company,
@ -76,6 +99,7 @@ def company_edit(company_id=None):
social_media=social_media,
company_websites=company_websites,
permissions=permissions,
section_definitions=section_definitions,
)
finally:
db.close()
@ -309,3 +333,38 @@ def _save_websites(db, company):
company.website = ensure_url(sanitize_input(website_urls[0], max_length=500))
else:
company.website = None
@bp.route('/firma/edytuj/<int:company_id>/visibility', methods=['POST'])
@login_required
def company_edit_visibility(company_id):
"""Save section visibility preferences via AJAX."""
if not current_user.can_manage_company(company_id):
return jsonify({'error': 'Brak uprawnień'}), 403
db = SessionLocal()
try:
company = db.query(Company).get(company_id)
if not company:
return jsonify({'error': 'Nie znaleziono firmy'}), 404
data = request.get_json()
if not data or 'hidden_sections' not in data:
return jsonify({'error': 'Brak danych'}), 400
hidden = data['hidden_sections']
if not isinstance(hidden, list):
return jsonify({'error': 'Nieprawidłowy format'}), 400
# Validate keys
validated = [k for k in hidden if k in VALID_SECTION_KEYS]
company.hidden_sections = validated
db.commit()
return jsonify({'success': True, 'hidden_sections': validated})
except Exception as e:
db.rollback()
logger.error(f"Error saving visibility for company_id={company_id}: {e}")
return jsonify({'error': 'Wystąpił błąd'}), 500
finally:
db.close()

View File

@ -855,6 +855,13 @@ class Company(Base):
cascade='all, delete-orphan',
order_by='CompanyWebsite.is_primary.desc()')
# Profile section visibility
hidden_sections = Column(PG_JSONB, nullable=False, default=list, server_default='[]')
def is_section_hidden(self, section_key):
"""Check if a profile section is hidden from visitors."""
return section_key in (self.hidden_sections or [])
class Service(Base):
"""Services offered by companies"""

View File

@ -0,0 +1,7 @@
-- Migration 069: Add hidden_sections column to companies table
-- Allows company owners/managers to hide specific profile sections from visitors
ALTER TABLE companies ADD COLUMN IF NOT EXISTS hidden_sections JSONB NOT NULL DEFAULT '[]'::jsonb;
-- Grant permissions
GRANT ALL ON TABLE companies TO nordabiz_app;

View File

@ -91,6 +91,23 @@
margin-bottom: var(--spacing-xl);
}
.company-section.section-hidden {
opacity: 0.6;
border: 2px dashed var(--warning-color, #f59e0b);
}
.hidden-section-badge {
background: var(--warning-color, #f59e0b);
color: white;
padding: 4px 12px;
border-radius: var(--radius, 0.5rem);
font-size: 0.85rem;
margin-bottom: var(--spacing-sm, 8px);
display: inline-flex;
align-items: center;
gap: 6px;
}
.hidden-section-badge i { font-size: 0.9rem; }
.section-title {
font-size: var(--font-size-2xl);
color: var(--text-primary);
@ -898,8 +915,12 @@
<!-- O firmie - Single Description (prioritized sources) -->
{% set about_description = company.description_full or (ai_insights.business_summary if ai_insights else none) or (website_analysis.content_summary if website_analysis else none) %}
{% if about_description %}
<div class="company-section">
{% set _about_hidden = company.is_section_hidden('about') %}
{% if about_description and (not _about_hidden or can_edit_profile or is_admin) %}
<div class="company-section {% if _about_hidden %}section-hidden{% endif %}">
{% if _about_hidden %}
<div class="hidden-section-badge"><i class="fas fa-eye-slash"></i> Ukryta — niewidoczna dla odwiedzających</div>
{% endif %}
<h2 class="section-title">O firmie</h2>
<div style="background: var(--background); border-radius: var(--radius-lg); padding: var(--spacing-lg); border: 1px solid var(--border);">
@ -949,8 +970,12 @@
{% set www_services = website_analysis.services_extracted if website_analysis and website_analysis.services_extracted else [] %}
{% set www_keywords = website_analysis.main_keywords if website_analysis and website_analysis.main_keywords else [] %}
{% if www_services or www_keywords or company.services or company.competencies or company.services_offered or company.technologies_used %}
<div class="company-section">
{% set _services_hidden = company.is_section_hidden('services') %}
{% if (www_services or www_keywords or company.services or company.competencies or company.services_offered or company.technologies_used) and (not _services_hidden or can_edit_profile or is_admin) %}
<div class="company-section {% if _services_hidden %}section-hidden{% endif %}">
{% if _services_hidden %}
<div class="hidden-section-badge"><i class="fas fa-eye-slash"></i> Ukryta — niewidoczna dla odwiedzających</div>
{% endif %}
<h2 class="section-title">Usługi i kompetencje</h2>
{# Services description (text field from company edit) #}
@ -1079,8 +1104,12 @@
{% endif %}
<!-- Zarząd i Wspólnicy Section (ukryte dla firm z danymi KRS API - dane są w sekcji "Dane z rejestrów urzędowych") -->
{% if people and not (company.krs and company.data_source == 'KRS API') %}
<div class="company-section">
{% set _board_hidden = company.is_section_hidden('board') %}
{% if people and not (company.krs and company.data_source == 'KRS API') and (not _board_hidden or can_edit_profile or is_admin) %}
<div class="company-section {% if _board_hidden %}section-hidden{% endif %}">
{% if _board_hidden %}
<div class="hidden-section-badge"><i class="fas fa-eye-slash"></i> Ukryta — niewidoczna dla odwiedzających</div>
{% endif %}
<h2 class="section-title">Zarząd i Wspólnicy</h2>
{% set role_colors = {'zarzad': '#e74c3c', 'wspolnik': '#2ecc71', 'prokurent': '#f39c12', 'wlasciciel_jdg': '#9b59b6'} %}
@ -1152,8 +1181,12 @@
{% endif %}
<!-- Właściciel JDG Section (for sole proprietorships without KRS) -->
{% if company.owner_first_name and not company.krs and not people %}
<div class="company-section">
{% set _owner_hidden = company.is_section_hidden('owner_info') %}
{% if company.owner_first_name and not company.krs and not people and (not _owner_hidden or can_edit_profile or is_admin) %}
<div class="company-section {% if _owner_hidden %}section-hidden{% endif %}">
{% if _owner_hidden %}
<div class="hidden-section-badge"><i class="fas fa-eye-slash"></i> Ukryta — niewidoczna dla odwiedzających</div>
{% endif %}
<h2 class="section-title">Właściciel</h2>
<div style="display: flex; align-items: center; gap: var(--spacing-lg); padding: var(--spacing-lg); background: var(--background); border-radius: var(--radius-lg); border-left: 4px solid #9b59b6;">
@ -1181,8 +1214,12 @@
<!-- ============================================================ -->
<!-- DANE Z REJESTRÓW URZĘDOWYCH (CEIDG / KRS) -->
<!-- ============================================================ -->
{% if company.ceidg_id or (company.krs and company.data_source == 'KRS API') %}
<div class="company-section">
{% set _registry_hidden = company.is_section_hidden('registry') %}
{% if (company.ceidg_id or (company.krs and company.data_source == 'KRS API')) and (not _registry_hidden or can_edit_profile or is_admin) %}
<div class="company-section {% if _registry_hidden %}section-hidden{% endif %}">
{% if _registry_hidden %}
<div class="hidden-section-badge"><i class="fas fa-eye-slash"></i> Ukryta — niewidoczna dla odwiedzających</div>
{% endif %}
<h2 class="section-title">
Dane z rejestrów urzędowych
{% if company.ceidg_id %}
@ -1691,8 +1728,12 @@
<!-- ============================================================ -->
<!-- Legacy: Legal & Business Info - Card Based (ukryte dla firm z danymi z rejestrów - CEIDG lub KRS API) -->
{% if not (company.krs and company.data_source == 'KRS API') and not company.ceidg_id %}
<div class="company-section">
{% set _legal_hidden = company.is_section_hidden('legal') %}
{% if not (company.krs and company.data_source == 'KRS API') and not company.ceidg_id and (not _legal_hidden or can_edit_profile or is_admin) %}
<div class="company-section {% if _legal_hidden %}section-hidden{% endif %}">
{% if _legal_hidden %}
<div class="hidden-section-badge"><i class="fas fa-eye-slash"></i> Ukryta — niewidoczna dla odwiedzających</div>
{% endif %}
<h2 class="section-title">Informacje prawne i biznesowe</h2>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--spacing-lg);">
@ -1995,8 +2036,12 @@
{# Koniec warunku: not (company.krs and company.data_source == 'KRS API') and not company.ceidg_id #}
<!-- Contact Information - Card Based (ukryte dla firm z danymi z rejestrów - CEIDG lub KRS API) -->
{% if not (company.krs and company.data_source == 'KRS API') and not company.ceidg_id %}
<div class="company-section">
{% set _contact_hidden = company.is_section_hidden('contact') %}
{% if not (company.krs and company.data_source == 'KRS API') and not company.ceidg_id and (not _contact_hidden or can_edit_profile or is_admin) %}
<div class="company-section {% if _contact_hidden %}section-hidden{% endif %}">
{% if _contact_hidden %}
<div class="hidden-section-badge"><i class="fas fa-eye-slash"></i> Ukryta — niewidoczna dla odwiedzających</div>
{% endif %}
<h2 class="section-title">Dane kontaktowe</h2>
{% set source_names = {'website': 'Strona WWW', 'krs': 'Rejestr KRS', 'google_business': 'Google Business', 'facebook': 'Facebook', 'manual': 'Wprowadzono ręcznie', 'brave_search': 'Wyszukiwarka', 'ceidg': 'CEIDG', 'norda_biznes': 'Norda Biznes', 'website_scrape': 'Strona WWW (auto)'} %}
@ -2232,7 +2277,12 @@
{# Koniec warunku: not (company.krs and company.data_source == 'KRS API') and not company.ceidg_id dla sekcji "Dane kontaktowe" #}
<!-- Social Media Section - Always show all platforms -->
<div class="company-section">
{% set _social_hidden = company.is_section_hidden('social_media') %}
{% if not _social_hidden or can_edit_profile or is_admin %}
<div class="company-section {% if _social_hidden %}section-hidden{% endif %}">
{% if _social_hidden %}
<div class="hidden-section-badge"><i class="fas fa-eye-slash"></i> Ukryta — niewidoczna dla odwiedzających</div>
{% endif %}
<h2 class="section-title">Social Media</h2>
{% set platforms = ['facebook', 'instagram', 'youtube', 'linkedin', 'tiktok', 'twitter'] %}
@ -2357,6 +2407,7 @@
{% endfor %}
</div>
</div>
{% endif %}{# social_media visibility #}
{# ======================================================================
REKOMENDACJE - TYMCZASOWO UKRYTE
@ -2364,9 +2415,13 @@
Data ukrycia: 2026-02-01
Aby włączyć: zmień "False" na "True" w linii poniżej
====================================================================== #}
{% if False %} {# REKOMENDACJE DISABLED - zmień na True aby włączyć #}
{% set _recommendations_hidden = company.is_section_hidden('recommendations') %}
{% if False and (not _recommendations_hidden or can_edit_profile or is_admin) %} {# REKOMENDACJE DISABLED - zmień False na True aby włączyć #}
<!-- Recommendations Section -->
<div class="company-section">
<div class="company-section {% if _recommendations_hidden %}section-hidden{% endif %}">
{% if _recommendations_hidden %}
<div class="hidden-section-badge"><i class="fas fa-eye-slash"></i> Ukryta — niewidoczna dla odwiedzających</div>
{% endif %}
<h2 class="section-title">⭐ REKOMENDACJE {% if recommendations %}({{ recommendations|length }} rekomendacji){% endif %}</h2>
{% if current_user.is_authenticated %}
@ -2695,8 +2750,13 @@
<!-- Unified Website Section -->
{% set primary_website = company.websites|selectattr('is_primary')|first if company.websites else none %}
{% set primary_url = primary_website.url if primary_website else company.website %}
{% set _website_hidden = company.is_section_hidden('website') %}
{% if not _website_hidden or can_edit_profile or is_admin %}
{% if primary_url and (website_analysis or company.websites) %}
<div class="company-section">
<div class="company-section {% if _website_hidden %}section-hidden{% endif %}">
{% if _website_hidden %}
<div class="hidden-section-badge"><i class="fas fa-eye-slash"></i> Ukryta — niewidoczna dla odwiedzających</div>
{% endif %}
<h2 class="section-title">Strona WWW</h2>
<!-- Website banners with per-site audit cards -->
@ -2796,7 +2856,10 @@
</div>
{% elif company.website %}
<!-- Website URL only (no analysis yet) -->
<div class="company-section">
<div class="company-section {% if _website_hidden %}section-hidden{% endif %}">
{% if _website_hidden %}
<div class="hidden-section-badge"><i class="fas fa-eye-slash"></i> Ukryta — niewidoczna dla odwiedzających</div>
{% endif %}
<h2 class="section-title">Strona WWW</h2>
<div style="padding: var(--spacing-lg); background: linear-gradient(135deg, #3b82f6, #1d4ed8); border-radius: var(--radius-lg); display: flex; align-items: center; gap: var(--spacing-md);">
<div style="width: 56px; height: 56px; border-radius: 12px; background: rgba(255,255,255,0.2); display: flex; align-items: center; justify-content: center;">
@ -2823,7 +2886,10 @@
</div>
{% else %}
<!-- No Website Section -->
<div class="company-section">
<div class="company-section {% if _website_hidden %}section-hidden{% endif %}">
{% if _website_hidden %}
<div class="hidden-section-badge"><i class="fas fa-eye-slash"></i> Ukryta — niewidoczna dla odwiedzających</div>
{% endif %}
<h2 class="section-title">Strona WWW</h2>
<div style="display: flex; flex-direction: column; align-items: center; justify-content: center; padding: var(--spacing-xl); text-align: center;">
<div style="width: 80px; height: 80px; border-radius: 50%; background: linear-gradient(135deg, #f3f4f6, #e5e7eb); display: flex; align-items: center; justify-content: center; margin-bottom: var(--spacing-lg);">
@ -2853,10 +2919,15 @@
</div>
</div>
{% endif %}
{% endif %}{# website visibility #}
<!-- SEO Metrics Section - Only show if SEO audit was performed -->
{% if website_analysis and website_analysis.seo_audited_at and is_audit_owner %}
<div class="company-section" id="seo-metrics">
{% set _seo_hidden = company.is_section_hidden('seo_audit') %}
{% if website_analysis and website_analysis.seo_audited_at and is_audit_owner and (not _seo_hidden or can_edit_profile or is_admin) %}
<div class="company-section {% if _seo_hidden %}section-hidden{% endif %}" id="seo-metrics">
{% if _seo_hidden %}
<div class="hidden-section-badge"><i class="fas fa-eye-slash"></i> Ukryta — niewidoczna dla odwiedzających</div>
{% endif %}
<h2 class="section-title">
Analiza SEO
<span style="font-size: var(--font-size-sm); font-weight: normal; color: var(--text-secondary); margin-left: var(--spacing-sm);">
@ -3186,8 +3257,12 @@
{% endif %}
<!-- GBP Audit Section - Only show if GBP audit was performed -->
{% if gbp_audit and gbp_audit.completeness_score is not none and is_audit_owner %}
<div class="company-section" id="gbp-audit">
{% set _gbp_hidden = company.is_section_hidden('gbp_audit') %}
{% if gbp_audit and gbp_audit.completeness_score is not none and is_audit_owner and (not _gbp_hidden or can_edit_profile or is_admin) %}
<div class="company-section {% if _gbp_hidden %}section-hidden{% endif %}" id="gbp-audit">
{% if _gbp_hidden %}
<div class="hidden-section-badge"><i class="fas fa-eye-slash"></i> Ukryta — niewidoczna dla odwiedzających</div>
{% endif %}
<h2 class="section-title">
Audyt Google Business Profile
<span style="font-size: var(--font-size-sm); font-weight: normal; color: var(--text-secondary); margin-left: var(--spacing-sm);">
@ -3399,8 +3474,12 @@
{% endif %}
<!-- Social Media Audit Section -->
{% if social_media and social_media|length > 0 and is_audit_owner %}
<div class="company-section" id="social-media-audit">
{% set _social_audit_hidden = company.is_section_hidden('social_audit') %}
{% if social_media and social_media|length > 0 and is_audit_owner and (not _social_audit_hidden or can_edit_profile or is_admin) %}
<div class="company-section {% if _social_audit_hidden %}section-hidden{% endif %}" id="social-media-audit">
{% if _social_audit_hidden %}
<div class="hidden-section-badge"><i class="fas fa-eye-slash"></i> Ukryta — niewidoczna dla odwiedzających</div>
{% endif %}
<h2 class="section-title">
Audyt Social Media
</h2>
@ -3470,8 +3549,12 @@
{% endif %}
<!-- IT Audit Section - Only show if IT audit was performed -->
{% if it_audit and it_audit.overall_score is not none and is_audit_owner %}
<div class="company-section" id="it-audit">
{% set _it_hidden = company.is_section_hidden('it_audit') %}
{% if it_audit and it_audit.overall_score is not none and is_audit_owner and (not _it_hidden or can_edit_profile or is_admin) %}
<div class="company-section {% if _it_hidden %}section-hidden{% endif %}" id="it-audit">
{% if _it_hidden %}
<div class="hidden-section-badge"><i class="fas fa-eye-slash"></i> Ukryta — niewidoczna dla odwiedzających</div>
{% endif %}
<h2 class="section-title">
Audyt IT
<span style="font-size: var(--font-size-sm); font-weight: normal; color: var(--text-secondary); margin-left: var(--spacing-sm);">
@ -3643,8 +3726,12 @@
{% endif %}
{# Company Events - UKRYTE (2026-01-11) - do przywrócenia w przyszłości
{% if events %}
<div class="company-section">
{% set _news_hidden = company.is_section_hidden('news') %}
{% if events and (not _news_hidden or can_edit_profile or is_admin) %}
<div class="company-section {% if _news_hidden %}section-hidden{% endif %}">
{% if _news_hidden %}
<div class="hidden-section-badge"><i class="fas fa-eye-slash"></i> Ukryta — niewidoczna dla odwiedzających</div>
{% endif %}
<h2 class="section-title">
Aktualności i wydarzenia
<a href="{{ url_for('events', company=company.id) }}" style="float: right; font-size: var(--font-size-sm); color: var(--primary); text-decoration: none; font-weight: normal;">

View File

@ -281,6 +281,57 @@
/* Website type select */
.website-type-select { flex: 0 0 140px; font-weight: 500; }
/* Visibility tab */
.visibility-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--spacing-md);
border-bottom: 1px solid var(--border, #e0e4eb);
}
.visibility-row:last-child { border-bottom: none; }
.visibility-info { flex: 1; min-width: 0; }
.visibility-label {
display: block;
font-weight: 500;
color: var(--text-primary, #303030);
font-size: var(--font-size-base);
}
.visibility-desc {
display: block;
font-size: var(--font-size-sm);
color: var(--text-secondary, #464646);
margin-top: 2px;
}
.visibility-toggle {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 14px;
border: 1px solid var(--border, #e0e4eb);
border-radius: var(--radius);
background: #ecfdf5;
color: #059669;
font-size: var(--font-size-sm);
font-family: var(--font-family);
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
white-space: nowrap;
flex-shrink: 0;
}
.visibility-toggle svg { width: 18px; height: 18px; }
.visibility-toggle:hover { opacity: 0.8; }
.visibility-toggle.hidden-state {
background: #fef3c7;
color: #92400e;
border-color: #fde68a;
}
.visibility-toggle.saving {
opacity: 0.5;
pointer-events: none;
}
/* Website primary radio */
.website-primary-label {
display: flex;
@ -362,6 +413,10 @@
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/></svg>
<span class="tab-label">Social Media</span>
</button>
<button type="button" class="ce-tab" data-tab="visibility">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
<span class="tab-label">Widoczność</span>
</button>
</div>
<form method="POST" action="{{ url_for('public.company_edit_save', company_id=company.id) }}" id="companyEditForm">
@ -648,6 +703,38 @@
</fieldset>
</div>
<!-- TAB 5: Widoczność -->
<div class="ce-tab-content" id="tab-visibility">
<div class="ce-info-box">
<strong>Zarządzaj widocznością sekcji profilu</strong>
Ukryte sekcje nie będą widoczne dla odwiedzających Twój profil.
Ty i kadra zarządzająca nadal widzicie ukryte sekcje z oznaczeniem.
</div>
<div id="visibilitySections" style="margin-top: var(--spacing-lg);">
{% for key, label, description in section_definitions %}
<div class="visibility-row" data-section="{{ key }}">
<div class="visibility-info">
<span class="visibility-label">{{ label }}</span>
<span class="visibility-desc">{{ description }}</span>
</div>
<button type="button" class="visibility-toggle {% if company.is_section_hidden(key) %}hidden-state{% endif %}"
onclick="toggleSection('{{ key }}', this)" title="Kliknij aby zmienić widoczność">
{% if company.is_section_hidden(key) %}
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/><line x1="1" y1="1" x2="23" y2="23"/></svg>
<span>Ukryta</span>
{% else %}
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
<span>Widoczna</span>
{% endif %}
</button>
</div>
{% endfor %}
</div>
<div id="visibilityStatus" style="margin-top: var(--spacing-md); display: none;" class="ce-info-box"></div>
</div>
<!-- Form actions -->
<div class="ce-actions">
<button type="submit" class="btn btn-primary">
@ -813,4 +900,47 @@ function toggleWebsiteBtn() {
toggleWebsiteBtn();
}
})();
// Section visibility toggle (AJAX)
var hiddenSections = {{ company.hidden_sections | tojson }};
function toggleSection(key, btn) {
btn.classList.add('saving');
var idx = hiddenSections.indexOf(key);
if (idx >= 0) {
hiddenSections.splice(idx, 1);
} else {
hiddenSections.push(key);
}
fetch('{{ url_for("public.company_edit_visibility", company_id=company.id) }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ csrf_token() }}'
},
body: JSON.stringify({ hidden_sections: hiddenSections })
})
.then(function(r) { return r.json(); })
.then(function(data) {
btn.classList.remove('saving');
if (data.success) {
hiddenSections = data.hidden_sections;
var isHidden = hiddenSections.indexOf(key) >= 0;
btn.className = 'visibility-toggle' + (isHidden ? ' hidden-state' : '');
btn.innerHTML = isHidden
? '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/><line x1="1" y1="1" x2="23" y2="23"/></svg><span>Ukryta</span>'
: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg><span>Widoczna</span>';
} else {
// Revert on error
if (idx >= 0) hiddenSections.push(key);
else hiddenSections.splice(hiddenSections.indexOf(key), 1);
}
})
.catch(function() {
btn.classList.remove('saving');
if (idx >= 0) hiddenSections.push(key);
else hiddenSections.splice(hiddenSections.indexOf(key), 1);
});
}
{% endblock %}