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
Instead of skipping OAuth-connected Facebook profiles, the enrichment scan now calls sync_facebook_to_social_media() to fetch fresh data via Graph API. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
482 lines
23 KiB
HTML
482 lines
23 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}Raport audytu Social Media - Norda Biznes Partner{% endblock %}
|
||
|
||
{% block extra_css %}
|
||
<style>
|
||
.review-page { max-width: 1100px; margin: 0 auto; }
|
||
|
||
.review-header {
|
||
display: flex; justify-content: space-between; align-items: flex-start;
|
||
margin-bottom: var(--spacing-xl); flex-wrap: wrap; gap: var(--spacing-md);
|
||
}
|
||
|
||
.review-header h1 { font-size: var(--font-size-2xl); margin: 0; }
|
||
|
||
.review-actions { display: flex; gap: var(--spacing-sm); align-items: center; }
|
||
|
||
/* Summary cards */
|
||
.review-summary {
|
||
display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||
gap: var(--spacing-md); margin-bottom: var(--spacing-xl);
|
||
}
|
||
|
||
.review-stat {
|
||
background: var(--surface); padding: var(--spacing-md);
|
||
border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); text-align: center;
|
||
}
|
||
|
||
.review-stat-value { font-size: var(--font-size-2xl); font-weight: 700; display: block; }
|
||
.review-stat-label { font-size: var(--font-size-sm); color: var(--text-secondary); }
|
||
|
||
.review-stat-value.green { color: #22c55e; }
|
||
.review-stat-value.yellow { color: #f59e0b; }
|
||
.review-stat-value.red { color: #ef4444; }
|
||
.review-stat-value.blue { color: #3b82f6; }
|
||
.review-stat-value.gray { color: #9ca3af; }
|
||
|
||
/* Company section */
|
||
.company-review {
|
||
background: var(--surface); border-radius: var(--radius-lg);
|
||
box-shadow: var(--shadow-sm); margin-bottom: var(--spacing-md); overflow: hidden;
|
||
}
|
||
|
||
.company-review-header {
|
||
display: flex; align-items: center; gap: var(--spacing-md);
|
||
padding: var(--spacing-md) var(--spacing-lg);
|
||
background: var(--background); cursor: pointer;
|
||
border-bottom: 1px solid var(--border-color, #e5e7eb);
|
||
}
|
||
|
||
.company-review-header:hover { background: #f3f4f6; }
|
||
|
||
.company-review-header h3 { margin: 0; font-size: var(--font-size-base); flex: 1; }
|
||
|
||
.company-review-header .badge {
|
||
padding: 2px 8px; border-radius: 10px; font-size: 11px; font-weight: 600;
|
||
}
|
||
|
||
.badge.changes { background: #dbeafe; color: #1d4ed8; }
|
||
.badge.error { background: #fee2e2; color: #991b1b; }
|
||
.badge.skipped { background: #f3f4f6; color: #6b7280; }
|
||
|
||
.company-review-body { padding: var(--spacing-md) var(--spacing-lg); }
|
||
|
||
/* Platform diff */
|
||
.platform-diff {
|
||
margin-bottom: var(--spacing-md); padding: var(--spacing-md);
|
||
background: var(--background); border-radius: var(--radius);
|
||
border-left: 3px solid #3b82f6;
|
||
}
|
||
|
||
.platform-diff.error { border-left-color: #ef4444; }
|
||
.platform-diff.skipped { border-left-color: #9ca3af; }
|
||
.platform-diff.no-changes { border-left-color: #22c55e; }
|
||
|
||
.platform-diff-header {
|
||
display: flex; align-items: center; gap: var(--spacing-sm);
|
||
margin-bottom: var(--spacing-sm); font-weight: 600;
|
||
}
|
||
|
||
/* Changes table */
|
||
.changes-table { width: 100%; font-size: var(--font-size-sm); border-collapse: collapse; }
|
||
.changes-table th {
|
||
text-align: left; padding: 4px 8px; font-weight: 500;
|
||
color: var(--text-secondary); font-size: var(--font-size-xs);
|
||
border-bottom: 1px solid var(--border-color, #e5e7eb);
|
||
}
|
||
.changes-table td { padding: 6px 8px; border-bottom: 1px solid #f3f4f6; }
|
||
.changes-table .old-val { color: #991b1b; text-decoration: line-through; }
|
||
.changes-table .new-val { color: #15803d; font-weight: 500; }
|
||
.changes-table .arrow { color: var(--text-secondary); padding: 0 4px; }
|
||
|
||
/* Status message */
|
||
.approval-banner {
|
||
padding: var(--spacing-md) var(--spacing-lg);
|
||
border-radius: var(--radius-lg); margin-bottom: var(--spacing-xl);
|
||
display: flex; align-items: center; gap: var(--spacing-md);
|
||
}
|
||
|
||
.approval-banner.pending {
|
||
background: #fef3c7; border: 1px solid #f59e0b; color: #92400e;
|
||
}
|
||
|
||
.approval-banner.approved {
|
||
background: #dcfce7; border: 1px solid #22c55e; color: #15803d;
|
||
}
|
||
|
||
.approval-banner.empty {
|
||
background: #f3f4f6; border: 1px solid #d1d5db; color: #6b7280;
|
||
}
|
||
|
||
.toggle-arrow { transition: transform 0.2s; display: inline-block; }
|
||
.toggle-arrow.open { transform: rotate(90deg); }
|
||
|
||
.hidden { display: none !important; }
|
||
|
||
/* No changes section */
|
||
.no-changes-section {
|
||
margin-top: var(--spacing-xl); padding: var(--spacing-md);
|
||
background: var(--background); border-radius: var(--radius-lg);
|
||
}
|
||
|
||
.no-changes-list {
|
||
display: flex; flex-wrap: wrap; gap: var(--spacing-xs);
|
||
font-size: var(--font-size-xs); color: var(--text-secondary);
|
||
}
|
||
|
||
.no-changes-list span {
|
||
background: var(--surface); padding: 2px 8px;
|
||
border-radius: var(--radius); border: 1px solid var(--border-color, #e5e7eb);
|
||
}
|
||
</style>
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="review-page">
|
||
<a href="{{ url_for('admin.admin_social_audit') }}" class="back-link" style="display: inline-flex; align-items: center; gap: 4px; color: var(--text-secondary); text-decoration: none; font-size: var(--font-size-sm); margin-bottom: var(--spacing-md);">
|
||
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/></svg>
|
||
Powrót do dashboardu
|
||
</a>
|
||
|
||
<div class="review-header">
|
||
<div>
|
||
<h1>Raport audytu Social Media</h1>
|
||
{% if summary.last_run %}
|
||
<p style="color: var(--text-secondary); font-size: var(--font-size-sm); margin: 4px 0 0;">
|
||
Skanowanie: {{ summary.last_run }}
|
||
</p>
|
||
{% endif %}
|
||
</div>
|
||
|
||
{% if not approved and summary.profiles_with_changes > 0 %}
|
||
<div class="review-actions">
|
||
<button class="btn btn-danger btn-sm" onclick="discardChanges()" id="discardBtn">
|
||
<svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/></svg>
|
||
Odrzuć wszystko
|
||
</button>
|
||
<button class="btn btn-primary" onclick="approveChanges()" id="approveBtn">
|
||
<svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>
|
||
Zatwierdź {{ summary.profiles_with_changes }} zmian
|
||
</button>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<!-- Summary -->
|
||
<div class="review-summary">
|
||
<div class="review-stat">
|
||
<span class="review-stat-value">{{ summary.total_companies }}</span>
|
||
<span class="review-stat-label">Firm przeskanowanych</span>
|
||
</div>
|
||
<div class="review-stat">
|
||
<span class="review-stat-value">{{ summary.total_profiles_scanned }}</span>
|
||
<span class="review-stat-label">Profili sprawdzonych</span>
|
||
</div>
|
||
<div class="review-stat">
|
||
<span class="review-stat-value blue">{{ summary.profiles_with_changes }}</span>
|
||
<span class="review-stat-label">Z nowymi danymi</span>
|
||
</div>
|
||
<div class="review-stat">
|
||
<span class="review-stat-value blue">{{ summary.profiles_skipped }}</span>
|
||
<span class="review-stat-label">Sync API (OAuth)</span>
|
||
</div>
|
||
<div class="review-stat">
|
||
<span class="review-stat-value gray">{{ summary.profiles_no_data }}</span>
|
||
<span class="review-stat-label">Bez zmian</span>
|
||
</div>
|
||
<div class="review-stat">
|
||
<span class="review-stat-value red">{{ summary.profiles_errors }}</span>
|
||
<span class="review-stat-label">Błędów</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Approval banner -->
|
||
{% if approved %}
|
||
<div class="approval-banner approved">
|
||
<svg width="24" height="24" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||
<div>
|
||
<strong>Zmiany zostały zatwierdzone i zapisane do bazy danych.</strong>
|
||
<p style="margin: 4px 0 0; font-size: var(--font-size-sm);">Dane zostały zaktualizowane. Możesz wrócić do dashboardu lub uruchomić nowy audyt.</p>
|
||
</div>
|
||
</div>
|
||
{% elif summary.profiles_with_changes > 0 %}
|
||
<div class="approval-banner pending">
|
||
<svg width="24" height="24" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z"/></svg>
|
||
<div>
|
||
<strong>{{ summary.profiles_with_changes }} profili oczekuje na zatwierdzenie.</strong>
|
||
<p style="margin: 4px 0 0; font-size: var(--font-size-sm);">
|
||
Przejrzyj zmiany poniżej. Dane NIE zostały jeszcze zapisane do bazy.
|
||
Kliknij <strong>Zatwierdź</strong> aby zapisać lub <strong>Odrzuć</strong> aby anulować.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
{% else %}
|
||
<div class="approval-banner empty">
|
||
<svg width="24" height="24" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||
<div>
|
||
<strong>Audyt nie znalazł nowych danych do zaktualizowania.</strong>
|
||
<p style="margin: 4px 0 0; font-size: var(--font-size-sm);">Scraper nie pobrał nowych danych. Platformy jak Facebook, Instagram i LinkedIn blokują dostęp publiczny — podłącz OAuth API, aby pobierać dane z tych serwisów.</p>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- Changes per company -->
|
||
{% for result in results %}
|
||
<div class="company-review">
|
||
<div class="company-review-header" onclick="toggleCompany(this)">
|
||
<span class="toggle-arrow">▶</span>
|
||
<h3>{{ result.company_name }}</h3>
|
||
{% set change_count = result.profiles|selectattr('status', 'eq', 'changes')|list|length %}
|
||
{% set error_count = result.profiles|selectattr('status', 'eq', 'error')|list|length %}
|
||
{% set skip_count = result.profiles|selectattr('status', 'eq', 'skipped')|list|length %}
|
||
{% if change_count > 0 %}
|
||
<span class="badge changes">{{ change_count }} zmian</span>
|
||
{% endif %}
|
||
{% if error_count > 0 %}
|
||
<span class="badge error">{{ error_count }} błędów</span>
|
||
{% endif %}
|
||
{% if skip_count > 0 %}
|
||
<span class="badge skipped">{{ skip_count }} pom.</span>
|
||
{% endif %}
|
||
</div>
|
||
<div class="company-review-body hidden">
|
||
{% for p in result.profiles %}
|
||
<div class="platform-diff {{ p.status if p.status in ('error', 'skipped') else ('no-changes' if p.status in ('no_changes', 'synced_api') else '') }}">
|
||
<div class="platform-diff-header">
|
||
<span style="text-transform: capitalize;">{{ p.platform }}</span>
|
||
{% if p.status == 'changes' %}
|
||
<span style="color: #3b82f6; font-size: var(--font-size-xs); font-weight: 400;">{{ p.changes|length }} pól do aktualizacji</span>
|
||
{% elif p.status == 'skipped' %}
|
||
<span style="color: #9ca3af; font-size: var(--font-size-xs); font-weight: 400;">{{ p.reason }}</span>
|
||
{% elif p.status == 'error' %}
|
||
<span style="color: #ef4444; font-size: var(--font-size-xs); font-weight: 400;">{{ p.reason }}</span>
|
||
{% elif p.status == 'no_data' %}
|
||
<span style="color: #9ca3af; font-size: var(--font-size-xs); font-weight: 400;">{{ p.get('reason', 'Brak nowych danych') }}</span>
|
||
{% elif p.status == 'synced_api' %}
|
||
<span style="color: #3b82f6; font-size: var(--font-size-xs); font-weight: 400;">{{ p.reason }}</span>
|
||
{% elif p.status == 'no_changes' %}
|
||
<span style="color: #22c55e; font-size: var(--font-size-xs); font-weight: 400;">Dane aktualne</span>
|
||
{% endif %}
|
||
{% if p.url %}
|
||
<a href="{{ p.url }}" target="_blank" rel="noopener" style="font-size: 11px; color: var(--text-secondary); margin-left: auto;">{{ p.url|truncate(50) }}</a>
|
||
{% endif %}
|
||
</div>
|
||
|
||
{% if p.status == 'changes' and p.changes %}
|
||
<table class="changes-table">
|
||
<thead>
|
||
<tr>
|
||
<th style="width: 30%;">Pole</th>
|
||
<th style="width: 30%;">Obecna wartość</th>
|
||
<th style="width: 5%;"></th>
|
||
<th style="width: 35%;">Nowa wartość</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for ch in p.changes %}
|
||
<tr>
|
||
<td>{{ ch.label }}</td>
|
||
<td class="old-val">{{ ch.old }}</td>
|
||
<td class="arrow">→</td>
|
||
<td class="new-val">{{ ch.new }}</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
{% endif %}
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
|
||
<!-- Companies without changes (collapsed) -->
|
||
{% set no_change_companies = [] %}
|
||
{% for r in all_results %}
|
||
{% if not r.has_changes and not r.profiles|selectattr('status', 'eq', 'error')|list %}
|
||
{% if no_change_companies.append(r) %}{% endif %}
|
||
{% endif %}
|
||
{% endfor %}
|
||
|
||
{% if no_change_companies %}
|
||
<div class="no-changes-section">
|
||
<p style="font-size: var(--font-size-sm); font-weight: 500; margin: 0 0 var(--spacing-sm);">
|
||
{{ no_change_companies|length }} firm bez zmian:
|
||
</p>
|
||
<div class="no-changes-list">
|
||
{% for r in no_change_companies %}
|
||
<span>{{ r.company_name }}</span>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- Bottom actions (fixed for long pages) -->
|
||
{% if not approved and summary.profiles_with_changes > 0 %}
|
||
<div style="position: sticky; bottom: 0; background: var(--surface); padding: var(--spacing-md); border-top: 2px solid var(--border-color, #e5e7eb); margin-top: var(--spacing-xl); display: flex; justify-content: space-between; align-items: center; border-radius: var(--radius-lg) var(--radius-lg) 0 0; box-shadow: 0 -4px 12px rgba(0,0,0,0.1);">
|
||
<span style="font-size: var(--font-size-sm); color: var(--text-secondary);">
|
||
{{ summary.profiles_with_changes }} profili ({{ summary.companies_with_changes }} firm) oczekuje na zatwierdzenie
|
||
</span>
|
||
<div style="display: flex; gap: var(--spacing-sm);">
|
||
<button class="btn btn-danger btn-sm" onclick="discardChanges()">Odrzuć</button>
|
||
<button class="btn btn-primary" onclick="approveChanges()">Zatwierdź i zapisz</button>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
<!-- Confirm Modal -->
|
||
<div class="modal-overlay" id="confirmModal">
|
||
<div class="modal-box">
|
||
<div class="modal-icon" id="modalIcon">
|
||
<svg width="28" height="28" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z"/></svg>
|
||
</div>
|
||
<h3 class="modal-title" id="modalTitle"></h3>
|
||
<p class="modal-message" id="modalMessage"></p>
|
||
<p class="modal-detail" id="modalDetail"></p>
|
||
<div class="modal-actions" id="modalActions"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="toastContainer" style="position: fixed; top: 80px; right: 20px; z-index: 3000; display: flex; flex-direction: column; gap: 10px;"></div>
|
||
|
||
<style>
|
||
.modal-overlay { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 2000; justify-content: center; align-items: center; animation: modalFadeIn 0.2s ease; }
|
||
.modal-overlay.active { display: flex; }
|
||
.modal-box { background: var(--surface); border-radius: var(--radius-lg); padding: var(--spacing-xl); max-width: 440px; width: 90%; text-align: center; box-shadow: 0 20px 40px rgba(0,0,0,0.2); animation: modalSlideUp 0.3s ease; }
|
||
@keyframes modalFadeIn { from { opacity: 0; } to { opacity: 1; } }
|
||
@keyframes modalSlideUp { from { transform: translateY(20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
|
||
.modal-icon { width: 56px; height: 56px; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto var(--spacing-md); }
|
||
.modal-icon.warn { background: #fef3c7; color: #f59e0b; }
|
||
.modal-icon.danger { background: #fee2e2; color: #ef4444; }
|
||
.modal-icon.success { background: #dcfce7; color: #22c55e; }
|
||
.modal-title { font-size: var(--font-size-lg); font-weight: 600; color: var(--text-primary); margin-bottom: var(--spacing-sm); }
|
||
.modal-message { color: var(--text-secondary); margin-bottom: var(--spacing-xs); line-height: 1.5; }
|
||
.modal-detail { font-size: var(--font-size-sm); color: var(--text-secondary); margin-bottom: var(--spacing-lg); }
|
||
.modal-actions { display: flex; gap: var(--spacing-sm); justify-content: center; }
|
||
.toast { padding: 12px 20px; border-radius: var(--radius); background: var(--surface); border-left: 4px solid var(--primary); box-shadow: 0 4px 12px rgba(0,0,0,0.15); display: flex; align-items: center; gap: 10px; animation: toastIn 0.3s ease; max-width: 400px; }
|
||
.toast.success { border-left-color: #22c55e; }
|
||
.toast.error { border-left-color: #ef4444; }
|
||
@keyframes toastIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
|
||
@keyframes toastOut { from { opacity: 1; } to { opacity: 0; } }
|
||
</style>
|
||
{% endblock %}
|
||
|
||
{% block extra_js %}
|
||
function toggleCompany(header) {
|
||
var body = header.nextElementSibling;
|
||
var arrow = header.querySelector('.toggle-arrow');
|
||
body.classList.toggle('hidden');
|
||
arrow.classList.toggle('open');
|
||
}
|
||
|
||
// Toast
|
||
function showToast(message, type, duration) {
|
||
type = type || 'info'; duration = duration || 4000;
|
||
var icons = { success: '✓', error: '✕', warning: '⚠', info: 'ℹ' };
|
||
var container = document.getElementById('toastContainer');
|
||
var toast = document.createElement('div');
|
||
toast.className = 'toast ' + type;
|
||
toast.innerHTML = '<span style="font-size:1.2em">' + (icons[type]||'ℹ') + '</span><span>' + message + '</span>';
|
||
container.appendChild(toast);
|
||
setTimeout(function() { toast.style.animation = 'toastOut 0.3s ease forwards'; setTimeout(function() { toast.remove(); }, 300); }, duration);
|
||
}
|
||
|
||
// Modal
|
||
function showModal(opts) {
|
||
var icon = document.getElementById('modalIcon');
|
||
icon.className = 'modal-icon ' + (opts.iconType || 'warn');
|
||
icon.innerHTML = opts.iconSvg || '<svg width="28" height="28" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z"/></svg>';
|
||
document.getElementById('modalTitle').textContent = opts.title;
|
||
document.getElementById('modalMessage').innerHTML = opts.message;
|
||
document.getElementById('modalDetail').textContent = opts.detail || '';
|
||
var actions = document.getElementById('modalActions');
|
||
actions.innerHTML = '';
|
||
(opts.buttons || []).forEach(function(b) {
|
||
var btn = document.createElement('button');
|
||
btn.className = 'btn ' + (b.cls || 'btn-outline');
|
||
btn.textContent = b.label;
|
||
btn.onclick = function() { closeModal(); if (b.action) b.action(); };
|
||
actions.appendChild(btn);
|
||
});
|
||
document.getElementById('confirmModal').classList.add('active');
|
||
}
|
||
|
||
function closeModal() {
|
||
document.getElementById('confirmModal').classList.remove('active');
|
||
}
|
||
|
||
document.getElementById('confirmModal').addEventListener('click', function(e) {
|
||
if (e.target === this) closeModal();
|
||
});
|
||
|
||
function approveChanges() {
|
||
showModal({
|
||
title: 'Zatwierdź zmiany',
|
||
iconType: 'warn',
|
||
message: 'Czy na pewno chcesz zatwierdzić i zapisać zebrane dane do bazy?',
|
||
detail: 'Ta operacja zaktualizuje {{ summary.profiles_with_changes }} profili w {{ summary.companies_with_changes }} firmach.',
|
||
buttons: [
|
||
{ label: 'Anuluj', cls: 'btn-outline' },
|
||
{ label: 'Zatwierdź i zapisz', cls: 'btn-primary', action: doApprove }
|
||
]
|
||
});
|
||
}
|
||
|
||
function doApprove() {
|
||
var approveBtn = document.getElementById('approveBtn');
|
||
var discardBtn = document.getElementById('discardBtn');
|
||
if (approveBtn) { approveBtn.disabled = true; approveBtn.textContent = 'Zapisywanie...'; }
|
||
if (discardBtn) discardBtn.disabled = true;
|
||
|
||
fetch('{{ url_for("admin.admin_social_audit_enrichment_approve") }}', {
|
||
method: 'POST',
|
||
headers: {'X-CSRFToken': '{{ csrf_token() }}'},
|
||
})
|
||
.then(function(r) { return r.json(); })
|
||
.then(function(data) {
|
||
if (data.status === 'approved') {
|
||
showToast('Zaktualizowano ' + data.applied + ' profili' + (data.errors > 0 ? ' (' + data.errors + ' błędów)' : ''), 'success', 5000);
|
||
setTimeout(function() { location.reload(); }, 1500);
|
||
} else {
|
||
showToast('Błąd: ' + (data.error || 'Nieznany błąd'), 'error');
|
||
if (approveBtn) { approveBtn.disabled = false; approveBtn.textContent = 'Zatwierdź'; }
|
||
if (discardBtn) discardBtn.disabled = false;
|
||
}
|
||
})
|
||
.catch(function(e) {
|
||
showToast('Błąd połączenia: ' + e.message, 'error');
|
||
if (approveBtn) { approveBtn.disabled = false; approveBtn.textContent = 'Zatwierdź'; }
|
||
if (discardBtn) discardBtn.disabled = false;
|
||
});
|
||
}
|
||
|
||
function discardChanges() {
|
||
showModal({
|
||
title: 'Odrzuć zmiany',
|
||
iconType: 'danger',
|
||
iconSvg: '<svg width="28" height="28" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/></svg>',
|
||
message: 'Czy na pewno chcesz odrzucić wszystkie zebrane dane?',
|
||
detail: 'Baza danych nie zostanie zmieniona.',
|
||
buttons: [
|
||
{ label: 'Anuluj', cls: 'btn-outline' },
|
||
{ label: 'Odrzuć', cls: 'btn-danger', action: doDiscard }
|
||
]
|
||
});
|
||
}
|
||
|
||
function doDiscard() {
|
||
fetch('{{ url_for("admin.admin_social_audit_enrichment_discard") }}', {
|
||
method: 'POST',
|
||
headers: {'X-CSRFToken': '{{ csrf_token() }}'},
|
||
})
|
||
.then(function(r) { return r.json(); })
|
||
.then(function(data) {
|
||
if (data.status === 'discarded') {
|
||
showToast('Odrzucono ' + data.count + ' zmian. Baza bez zmian.', 'success');
|
||
setTimeout(function() { window.location.href = '{{ url_for("admin.admin_social_audit") }}'; }, 1500);
|
||
}
|
||
});
|
||
}
|
||
{% endblock %}
|