feat: add user profile preview to admin company detail page
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
Show social media cards, SEO PageSpeed scores, and GBP stats directly in admin view. Add "Profil publiczny" link to header. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7b985f231e
commit
fb007af4ba
@ -782,6 +782,9 @@ def admin_company_detail(company_id):
|
||||
completeness=completeness,
|
||||
users=users,
|
||||
hints=hints,
|
||||
social_accounts=social_accounts,
|
||||
seo_analysis=seo_analysis,
|
||||
gbp_audit=gbp_audit,
|
||||
)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
@ -532,12 +532,108 @@
|
||||
to { transform: translateX(100%); opacity: 0; }
|
||||
}
|
||||
|
||||
/* Preview section */
|
||||
.preview-section {
|
||||
border-top: 2px solid var(--border);
|
||||
padding-top: var(--spacing-lg);
|
||||
margin-top: var(--spacing-lg);
|
||||
}
|
||||
.preview-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
cursor: pointer;
|
||||
padding: var(--spacing-sm) 0;
|
||||
user-select: none;
|
||||
}
|
||||
.preview-header h2 { margin: 0; }
|
||||
.preview-toggle {
|
||||
transition: transform 0.2s;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
.preview-toggle.collapsed { transform: rotate(-90deg); }
|
||||
.preview-body { overflow: hidden; }
|
||||
.preview-body.collapsed { display: none; }
|
||||
.preview-block { margin-bottom: var(--spacing-xl); }
|
||||
.preview-block h3 {
|
||||
font-size: var(--font-size-base);
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: var(--spacing-md);
|
||||
font-weight: 600;
|
||||
}
|
||||
.preview-grid-3 {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
.preview-grid-4 {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: var(--spacing-md);
|
||||
}
|
||||
.preview-card {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--spacing-lg);
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
}
|
||||
.preview-card a {
|
||||
color: var(--text-secondary);
|
||||
font-size: var(--font-size-sm);
|
||||
text-decoration: none;
|
||||
}
|
||||
.preview-card a:hover { text-decoration: underline; }
|
||||
.preview-score-circle {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.preview-social-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.preview-label {
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
.preview-value {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.preview-badge {
|
||||
font-size: 11px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
.preview-empty {
|
||||
color: var(--text-secondary);
|
||||
font-style: italic;
|
||||
padding: var(--spacing-lg);
|
||||
text-align: center;
|
||||
border: 1px dashed var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.header-row { flex-direction: column; }
|
||||
.info-grid { grid-template-columns: 1fr; }
|
||||
.actions-grid { grid-template-columns: 1fr; }
|
||||
.checklist-grid { grid-template-columns: 1fr; }
|
||||
.header-actions { width: 100%; justify-content: flex-end; }
|
||||
.preview-grid-3 { grid-template-columns: 1fr; }
|
||||
.preview-grid-4 { grid-template-columns: repeat(2, 1fr); }
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
@ -557,8 +653,12 @@
|
||||
<span class="badge badge-{{ company.status or 'pending' }}">{{ company.status or 'pending' }}</span>
|
||||
</h1>
|
||||
<div class="header-actions">
|
||||
<a href="/company/{{ company.id }}" target="_blank" class="btn-link" style="color: #059669;">
|
||||
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg>
|
||||
Profil publiczny
|
||||
</a>
|
||||
<a href="{{ url_for('admin.company_settings', company_id=company.id) }}" class="btn-link">
|
||||
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.066 2.573c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.573 1.066c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.066-2.573c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/><circle cx="12" cy="12" r="3"/></svg>
|
||||
<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.066 2.573c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.573 1.066c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.066-2.573c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.11 2.37-2.37.996.608 2.296.07 2.572-1.065z"/><circle cx="12" cy="12" r="3"/></svg>
|
||||
Ustawienia
|
||||
</a>
|
||||
<a href="{{ url_for('admin.admin_companies') }}" class="btn-link">
|
||||
@ -824,7 +924,141 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 5. Completeness checklist -->
|
||||
<!-- 5. Preview: what the user sees -->
|
||||
<div class="section preview-section">
|
||||
<div class="preview-header" onclick="togglePreview()">
|
||||
<h2>Podgląd profilu użytkownika</h2>
|
||||
<svg class="preview-toggle" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><polyline points="6 9 12 15 18 9"/></svg>
|
||||
</div>
|
||||
<div class="preview-body" id="previewBody">
|
||||
|
||||
{# Social Media #}
|
||||
<div class="preview-block">
|
||||
<h3>Social Media</h3>
|
||||
{% if social_accounts and social_accounts|length > 0 %}
|
||||
<div class="preview-grid-3">
|
||||
{% for sm in social_accounts %}
|
||||
{% set colors = {'facebook': '#1877F2', 'instagram': '#E4405F', 'linkedin': '#0A66C2', 'youtube': '#FF0000', 'twitter': '#000000', 'tiktok': '#000000'} %}
|
||||
{% set color = colors.get(sm.platform, '#6b7280') %}
|
||||
<div class="preview-card" style="border-color: {{ color }}30;">
|
||||
<div class="preview-social-icon" style="background: {{ color }}15;">
|
||||
{% if sm.platform == 'facebook' %}
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="{{ color }}"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>
|
||||
{% elif sm.platform == 'instagram' %}
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="{{ color }}"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/></svg>
|
||||
{% elif sm.platform == 'linkedin' %}
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="{{ color }}"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>
|
||||
{% elif sm.platform == 'youtube' %}
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="{{ color }}"><path d="M23.498 6.186a3.016 3.016 0 00-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 00.502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 002.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 002.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/></svg>
|
||||
{% elif sm.platform == 'tiktok' %}
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="{{ color }}"><path d="M12.525.02c1.31-.02 2.61-.01 3.91-.02.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.15 1.07-.14 1.61.24 1.64 1.82 3.02 3.5 2.87 1.12-.01 2.19-.66 2.77-1.61.19-.33.4-.67.41-1.06.1-1.79.06-3.57.07-5.36.01-4.03-.01-8.05.02-12.07z"/></svg>
|
||||
{% else %}
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="{{ color }}"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div style="font-weight: 600; text-transform: capitalize;">{{ sm.platform }}</div>
|
||||
{% if sm.followers_count %}
|
||||
<div class="preview-label">{{ sm.followers_count }} obserwujących</div>
|
||||
{% elif sm.page_name %}
|
||||
<div class="preview-label">{{ sm.page_name|truncate(25) }}</div>
|
||||
{% endif %}
|
||||
{% if sm.check_status == 'needs_verification' %}
|
||||
<span class="preview-badge" style="background: #fef3c7; color: #92400e;">Do weryfikacji</span>
|
||||
{% elif sm.is_valid %}
|
||||
<span class="preview-badge" style="background: #dcfce7; color: #166534;">Zweryfikowany</span>
|
||||
{% endif %}
|
||||
{% if sm.url %}
|
||||
<a href="{{ sm.url }}" target="_blank">Zobacz profil</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="preview-empty">Brak profili social media. Uruchom audyt powyżej.</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{# SEO PageSpeed #}
|
||||
<div class="preview-block">
|
||||
<h3>SEO — PageSpeed</h3>
|
||||
{% if seo_analysis and seo_analysis.seo_audited_at %}
|
||||
<div class="preview-grid-4">
|
||||
{% set scores = [
|
||||
('SEO', seo_analysis.pagespeed_seo_score),
|
||||
('Wydajność', seo_analysis.pagespeed_performance_score),
|
||||
('Dostępność', seo_analysis.pagespeed_accessibility_score),
|
||||
('Best Practices', seo_analysis.pagespeed_best_practices_score)
|
||||
] %}
|
||||
{% for label, score in scores %}
|
||||
<div class="preview-card">
|
||||
{% if score is not none %}
|
||||
{% if score >= 90 %}
|
||||
{% set sc = '#166534' %}{% set bg = '#dcfce7' %}{% set bc = '#86efac' %}
|
||||
{% elif score >= 50 %}
|
||||
{% set sc = '#92400e' %}{% set bg = '#fef3c7' %}{% set bc = '#fcd34d' %}
|
||||
{% else %}
|
||||
{% set sc = '#991b1b' %}{% set bg = '#fee2e2' %}{% set bc = '#fca5a5' %}
|
||||
{% endif %}
|
||||
<div class="preview-score-circle" style="background: {{ bg }}; color: {{ sc }}; border: 2px solid {{ bc }};">
|
||||
{{ score }}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="preview-score-circle" style="background: #f3f4f6; color: #9ca3af; border: 2px solid #e5e7eb;">
|
||||
—
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="preview-label">{{ label }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="preview-empty">Brak audytu SEO. Uruchom audyt powyżej.</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{# GBP #}
|
||||
<div class="preview-block">
|
||||
<h3>Google Business Profile</h3>
|
||||
{% if gbp_audit and gbp_audit.completeness_score is not none %}
|
||||
<div class="preview-grid-4">
|
||||
{# Reviews #}
|
||||
<div class="preview-card">
|
||||
<div class="preview-value" style="color: #d97706;">{{ gbp_audit.review_count or 0 }}</div>
|
||||
<div class="preview-label">Opinii</div>
|
||||
</div>
|
||||
{# Rating #}
|
||||
<div class="preview-card">
|
||||
{% set rating = gbp_audit.average_rating or 0 %}
|
||||
{% if rating >= 4.5 %}{% set rc = '#166534' %}
|
||||
{% elif rating >= 3.5 %}{% set rc = '#d97706' %}
|
||||
{% else %}{% set rc = '#991b1b' %}{% endif %}
|
||||
<div class="preview-value" style="color: {{ rc }};">{{ '%.1f'|format(rating) }}</div>
|
||||
<div class="preview-label">Średnia ocen</div>
|
||||
</div>
|
||||
{# Photos #}
|
||||
<div class="preview-card">
|
||||
<div class="preview-value" style="color: #2563eb;">{{ gbp_audit.photo_count or 0 }}</div>
|
||||
<div class="preview-label">Zdjęć</div>
|
||||
</div>
|
||||
{# Completeness #}
|
||||
<div class="preview-card">
|
||||
{% set cs = gbp_audit.completeness_score %}
|
||||
{% if cs >= 90 %}{% set cc = '#166534' %}
|
||||
{% elif cs >= 50 %}{% set cc = '#d97706' %}
|
||||
{% else %}{% set cc = '#991b1b' %}{% endif %}
|
||||
<div class="preview-value" style="color: {{ cc }};">{{ cs }}%</div>
|
||||
<div class="preview-label">Kompletność</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="preview-empty">Brak audytu GBP. Uruchom audyt powyżej.</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 6. Completeness checklist -->
|
||||
<div class="section">
|
||||
<h2>Lista kompletności ({{ completeness.filled }}/{{ completeness.total }})</h2>
|
||||
<div class="checklist-grid">
|
||||
@ -868,6 +1102,14 @@
|
||||
{% block extra_js %}
|
||||
var csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
||||
|
||||
// Preview section toggle
|
||||
function togglePreview() {
|
||||
var body = document.getElementById('previewBody');
|
||||
var toggle = document.querySelector('.preview-toggle');
|
||||
body.classList.toggle('collapsed');
|
||||
toggle.classList.toggle('collapsed');
|
||||
}
|
||||
|
||||
function showToast(message, type) {
|
||||
var container = document.getElementById('toastContainer');
|
||||
var toast = document.createElement('div');
|
||||
|
||||
Loading…
Reference in New Issue
Block a user