feat: collapsible sections, reorder layout (charts > app posts > FB posts)
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
Sections now have collapsible headers with localStorage persistence. Layout order: FB page stats > Analityka (charts) > Top 5 > App posts > FB posts. Each section can be independently expanded/collapsed by clicking the header. Collapse state persists across page loads via localStorage. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c42643b07c
commit
5c93bfa635
@ -92,6 +92,25 @@
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.collapsible-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
padding: var(--spacing-sm) var(--spacing-md);
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
margin-bottom: var(--spacing-sm);
|
||||
}
|
||||
.collapsible-header:hover { background: var(--background); }
|
||||
.collapsible-header h3 { margin: 0; font-size: var(--font-size-md); }
|
||||
.collapsible-header .collapse-icon { transition: transform 0.2s; font-size: 1.2em; color: var(--text-secondary); }
|
||||
.collapsible-header.collapsed .collapse-icon { transform: rotate(-90deg); }
|
||||
.collapsible-body { transition: max-height 0.3s ease; overflow: hidden; }
|
||||
.collapsible-body.collapsed { display: none; }
|
||||
|
||||
.posts-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
@ -443,22 +462,19 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<!-- Ostatnie posty z Facebook -->
|
||||
<!-- === SEKCJA 1: Analityka Facebook (wykresy) === -->
|
||||
{% for company_id_key, fb in fb_stats.items() %}
|
||||
<div class="fb-posts-section" id="fbPostsSection-{{ company_id_key }}">
|
||||
<div class="fb-posts-header">
|
||||
<h3>Posty na Facebook
|
||||
{% if cached_fb_posts.get(company_id_key) %}
|
||||
<span style="font-size: var(--font-size-xs); color: var(--text-secondary); font-weight: 400;">
|
||||
({{ cached_fb_posts[company_id_key].total_count }} postów, cache z {{ cached_fb_posts[company_id_key].cached_at.strftime('%d.%m %H:%M') }})
|
||||
</span>
|
||||
{% endif %}
|
||||
</h3>
|
||||
<div style="display: flex; gap: var(--spacing-xs); align-items: center; flex-wrap: wrap;">
|
||||
<button class="btn btn-secondary btn-small" onclick="loadFbPosts({{ company_id_key }}, this)">Najnowsze 10</button>
|
||||
<button class="btn btn-primary btn-small" onclick="refreshAllFbPosts({{ company_id_key }}, this)">Odśwież wszystkie</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapsible-header" onclick="toggleSection('fb-charts-{{ company_id_key }}')">
|
||||
<h3>Analityka Facebook
|
||||
{% if cached_fb_posts.get(company_id_key) %}
|
||||
<span style="font-size: var(--font-size-xs); color: var(--text-secondary); font-weight: 400;">
|
||||
({{ cached_fb_posts[company_id_key].total_count }} postów)
|
||||
</span>
|
||||
{% endif %}
|
||||
</h3>
|
||||
<span class="collapse-icon">▼</span>
|
||||
</div>
|
||||
<div id="fb-charts-{{ company_id_key }}-body" class="collapsible-body">
|
||||
<div id="fbChartsSection-{{ company_id_key }}" style="display:none;">
|
||||
<div id="fbChartsInfo-{{ company_id_key }}" style="text-align:center;margin-bottom:var(--spacing-sm);padding:var(--spacing-xs) var(--spacing-sm);color:var(--text-secondary);font-size:var(--font-size-sm);background:var(--surface);border-radius:var(--radius);border:1px solid var(--border);"></div>
|
||||
<div class="fb-charts-grid">
|
||||
@ -468,14 +484,28 @@
|
||||
<div class="fb-chart-card"><h4>Typy postów</h4><div style="height:200px;"><canvas id="postTypesChart-{{ company_id_key }}"></canvas></div></div>
|
||||
<div class="fb-chart-card"><h4>Najlepszy dzień tygodnia</h4><canvas id="bestDayChart-{{ company_id_key }}"></canvas></div>
|
||||
<div class="fb-chart-card"><h4>Najlepsza godzina</h4><canvas id="bestHourChart-{{ company_id_key }}"></canvas></div>
|
||||
<div class="fb-chart-card" style="grid-column: 1 / -1;"><h4>Top 5 postów</h4><div style="height:200px;"><canvas id="topPostsChart-{{ company_id_key }}"></canvas></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="fbPostsContainer-{{ company_id_key }}"></div>
|
||||
</div>
|
||||
|
||||
<div class="collapsible-header" onclick="toggleSection('fb-top5-{{ company_id_key }}')">
|
||||
<h3>Top 5 postów</h3>
|
||||
<span class="collapse-icon">▼</span>
|
||||
</div>
|
||||
<div id="fb-top5-{{ company_id_key }}-body" class="collapsible-body">
|
||||
<div id="fbTop5Section-{{ company_id_key }}" style="display:none;">
|
||||
<div class="fb-chart-card" style="margin-bottom:var(--spacing-md);"><div style="height:200px;"><canvas id="topPostsChart-{{ company_id_key }}"></canvas></div></div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<!-- === SEKCJA 2: Posty tworzone w aplikacji === -->
|
||||
<div class="collapsible-header" onclick="toggleSection('app-posts')">
|
||||
<h3>Posty tworzone w aplikacji</h3>
|
||||
<span class="collapse-icon">▼</span>
|
||||
</div>
|
||||
<div id="app-posts-body" class="collapsible-body">
|
||||
<!-- Filtry -->
|
||||
<div class="filters-row">
|
||||
<div class="filter-group">
|
||||
@ -598,6 +628,33 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div><!-- /app-posts-body -->
|
||||
|
||||
<!-- === SEKCJA 3: Posty z Facebooka (zwijalne) === -->
|
||||
{% if fb_stats %}
|
||||
{% for company_id_key, fb in fb_stats.items() %}
|
||||
<div class="fb-posts-section" id="fbPostsSection-{{ company_id_key }}">
|
||||
<div class="collapsible-header" onclick="toggleSection('fb-posts-{{ company_id_key }}')">
|
||||
<h3>Posty z Facebooka
|
||||
{% if cached_fb_posts.get(company_id_key) %}
|
||||
<span style="font-size: var(--font-size-xs); color: var(--text-secondary); font-weight: 400;">
|
||||
({{ cached_fb_posts[company_id_key].total_count }} postów, cache z {{ cached_fb_posts[company_id_key].cached_at.strftime('%d.%m %H:%M') }})
|
||||
</span>
|
||||
{% endif %}
|
||||
</h3>
|
||||
<div style="display:flex;gap:var(--spacing-xs);align-items:center;" onclick="event.stopPropagation();">
|
||||
<button class="btn btn-secondary btn-small" onclick="loadFbPosts({{ company_id_key }}, this)">Najnowsze 10</button>
|
||||
<button class="btn btn-primary btn-small" onclick="refreshAllFbPosts({{ company_id_key }}, this)">Odśwież wszystkie</button>
|
||||
</div>
|
||||
<span class="collapse-icon">▼</span>
|
||||
</div>
|
||||
<div id="fb-posts-{{ company_id_key }}-body" class="collapsible-body">
|
||||
<div id="fbPostsContainer-{{ company_id_key }}"></div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Confirm Modal -->
|
||||
@ -728,6 +785,33 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Collapsible sections with localStorage persistence
|
||||
function toggleSection(sectionId) {
|
||||
var body = document.getElementById(sectionId + '-body');
|
||||
var header = body ? body.previousElementSibling : null;
|
||||
if (!body) return;
|
||||
var collapsed = !body.classList.contains('collapsed');
|
||||
body.classList.toggle('collapsed');
|
||||
if (header && header.classList.contains('collapsible-header')) {
|
||||
header.classList.toggle('collapsed', collapsed);
|
||||
}
|
||||
try { localStorage.setItem('sp_' + sectionId, collapsed ? '0' : '1'); } catch(e) {}
|
||||
}
|
||||
|
||||
// Restore collapsed state from localStorage
|
||||
document.querySelectorAll('.collapsible-header').forEach(function(h) {
|
||||
var body = h.nextElementSibling;
|
||||
if (!body || !body.id) return;
|
||||
var key = 'sp_' + body.id.replace('-body', '');
|
||||
try {
|
||||
var saved = localStorage.getItem(key);
|
||||
if (saved === '0') {
|
||||
body.classList.add('collapsed');
|
||||
h.classList.add('collapsed');
|
||||
}
|
||||
} catch(e) {}
|
||||
});
|
||||
|
||||
function formatFbDate(isoStr) {
|
||||
if (!isoStr) return '';
|
||||
var d = new Date(isoStr);
|
||||
@ -1349,8 +1433,10 @@
|
||||
});
|
||||
window._fbCharts[companyId].push(chart7);
|
||||
|
||||
// Show charts section
|
||||
// Show charts and top5 sections
|
||||
document.getElementById('fbChartsSection-' + companyId).style.display = 'block';
|
||||
var top5El = document.getElementById('fbTop5Section-' + companyId);
|
||||
if (top5El) top5El.style.display = 'block';
|
||||
}
|
||||
|
||||
function syncFacebookData(companyId, btn) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user