Social Media audit: progress bar improvements
- Add detailed logging to SocialMediaAuditor (website scan, Brave search, results) - Slow down progress bar animation (400ms instead of 200ms) for better readability - Bold "ZNALEZIONO" text for found platforms - Display Google rating and review count in progress - Increase wait time before modal close (4 seconds) - Add console.log for debugging audit response Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
8fed190303
commit
c319777d58
@ -922,6 +922,7 @@ class SocialMediaAuditor:
|
||||
Returns comprehensive audit result.
|
||||
"""
|
||||
logger.info(f"Auditing company: {company['name']} (ID: {company['id']})")
|
||||
logger.info(f"Company website: {company.get('website', 'NOT SET')}")
|
||||
|
||||
result = {
|
||||
'company_id': company['id'],
|
||||
@ -936,27 +937,43 @@ class SocialMediaAuditor:
|
||||
# 1. Website audit
|
||||
if company.get('website'):
|
||||
try:
|
||||
logger.info(f"Starting website audit for: {company['website']}")
|
||||
result['website'] = self.website_auditor.audit_website(company['website'])
|
||||
logger.info(f"Website audit completed. HTTP status: {result['website'].get('http_status')}")
|
||||
except Exception as e:
|
||||
logger.error(f"Website audit failed: {str(e)}")
|
||||
result['errors'].append(f'Website audit failed: {str(e)}')
|
||||
else:
|
||||
logger.warning(f"No website URL for company {company['name']}")
|
||||
result['website'] = {'errors': ['No website URL']}
|
||||
|
||||
# 2. Social media from website
|
||||
website_social = result['website'].get('social_media_links', {})
|
||||
if website_social:
|
||||
logger.info(f"Social media found on website: {list(website_social.keys())}")
|
||||
else:
|
||||
logger.info("No social media links found on website")
|
||||
|
||||
# 3. Search for additional social media via Brave
|
||||
city = company.get('address_city', 'Wejherowo')
|
||||
try:
|
||||
logger.info(f"Searching Brave for social media: {company['name']} in {city}")
|
||||
brave_social = self.brave_searcher.search_social_media(company['name'], city)
|
||||
if brave_social:
|
||||
logger.info(f"Brave search found: {list(brave_social.keys())}")
|
||||
else:
|
||||
logger.info("Brave search found no additional social media")
|
||||
# Merge, website takes precedence
|
||||
for platform, url in brave_social.items():
|
||||
if platform not in website_social:
|
||||
website_social[platform] = url
|
||||
logger.info(f"Added {platform} from Brave search: {url}")
|
||||
except Exception as e:
|
||||
logger.warning(f"Brave search failed: {str(e)}")
|
||||
result['errors'].append(f'Brave search failed: {str(e)}')
|
||||
|
||||
result['social_media'] = website_social
|
||||
logger.info(f"Total social media profiles found: {len(website_social)} - {list(website_social.keys())}")
|
||||
|
||||
# 4. Google reviews search - prefer Google Places API if available
|
||||
try:
|
||||
|
||||
@ -426,61 +426,129 @@
|
||||
|
||||
/* Loading Overlay */
|
||||
.loading-overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
display: flex;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
z-index: 9999;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9999;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.3s ease, visibility 0.3s ease;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.loading-overlay.active {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.loading-content {
|
||||
background: var(--surface);
|
||||
padding: var(--spacing-xl);
|
||||
border-radius: var(--radius-lg);
|
||||
text-align: center;
|
||||
max-width: 400px;
|
||||
box-shadow: var(--shadow-lg);
|
||||
max-width: 450px;
|
||||
width: 90%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border: 4px solid #e2e8f0;
|
||||
.loading-header {
|
||||
margin-bottom: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.loading-header h3 {
|
||||
font-size: var(--font-size-lg);
|
||||
color: var(--text-primary);
|
||||
margin-bottom: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.loading-header p {
|
||||
color: var(--text-secondary);
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
||||
.loading-steps {
|
||||
text-align: left;
|
||||
margin-bottom: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.loading-step {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-sm);
|
||||
padding: var(--spacing-sm) 0;
|
||||
border-bottom: 1px solid var(--border-light, #f1f5f9);
|
||||
}
|
||||
|
||||
.loading-step:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.step-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.step-icon.pending { color: var(--text-tertiary); }
|
||||
.step-icon.in_progress { color: #a855f7; }
|
||||
.step-icon.complete { color: var(--success); }
|
||||
.step-icon.error { color: var(--error); }
|
||||
.step-icon.missing { color: var(--text-tertiary); opacity: 0.6; }
|
||||
.step-icon.skipped { color: var(--text-tertiary); opacity: 0.5; }
|
||||
|
||||
.step-spinner {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border: 2px solid var(--border);
|
||||
border-top-color: #a855f7;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 0 auto var(--spacing-md);
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.loading-title {
|
||||
font-size: var(--font-size-lg);
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: var(--spacing-sm);
|
||||
}
|
||||
|
||||
.loading-description {
|
||||
color: var(--text-secondary);
|
||||
.step-text {
|
||||
flex: 1;
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
||||
.step-text.pending { color: var(--text-tertiary); }
|
||||
.step-text.in_progress { color: var(--text-primary); font-weight: 500; }
|
||||
.step-text.complete { color: var(--text-secondary); }
|
||||
.step-text.error { color: var(--error); }
|
||||
.step-text.missing { color: var(--text-tertiary); font-style: italic; }
|
||||
.step-text.skipped { color: var(--text-tertiary); opacity: 0.6; }
|
||||
|
||||
/* Source tag styling */
|
||||
.source-tag {
|
||||
display: inline-block;
|
||||
font-size: 9px;
|
||||
font-weight: 600;
|
||||
padding: 1px 5px;
|
||||
border-radius: 3px;
|
||||
margin-left: 4px;
|
||||
text-transform: uppercase;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.source-tag.website { background: #6366f1; color: white; }
|
||||
.source-tag.brave { background: #fb542b; color: white; }
|
||||
.source-tag.google { background: #4285f4; color: white; }
|
||||
.source-tag.facebook { background: #1877f2; color: white; }
|
||||
.source-tag.instagram { background: #e4405f; color: white; }
|
||||
.source-tag.linkedin { background: #0a66c2; color: white; }
|
||||
.source-tag.youtube { background: #ff0000; color: white; }
|
||||
.source-tag.twitter { background: #000000; color: white; }
|
||||
.source-tag.tiktok { background: #000000; color: white; }
|
||||
|
||||
/* Modal */
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
@ -812,9 +880,99 @@
|
||||
<!-- Loading Overlay -->
|
||||
<div class="loading-overlay" id="loadingOverlay">
|
||||
<div class="loading-content">
|
||||
<div class="loading-spinner"></div>
|
||||
<div class="loading-title">Trwa audyt Social Media...</div>
|
||||
<div class="loading-description">Weryfikujemy profile na platformach spolecznosciowych. Moze to potrwac kilka sekund.</div>
|
||||
<div class="loading-header">
|
||||
<h3>Audyt Social Media</h3>
|
||||
<p>Szukam profili w mediach spolecznosciowych...</p>
|
||||
</div>
|
||||
<div class="loading-steps" id="loadingSteps">
|
||||
<!-- Step 1: Scan Website -->
|
||||
<div class="loading-step" id="step-website">
|
||||
<div class="step-icon in_progress">
|
||||
<div class="step-spinner"></div>
|
||||
</div>
|
||||
<span class="step-text in_progress">Skanuje strone WWW <span class="source-tag website">WWW</span></span>
|
||||
</div>
|
||||
|
||||
<!-- Step 2: Facebook -->
|
||||
<div class="loading-step" id="step-facebook">
|
||||
<div class="step-icon pending">
|
||||
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<circle cx="12" cy="12" r="10" stroke-width="2"/>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="step-text pending">Szukam Facebook <span class="source-tag facebook">FB</span></span>
|
||||
</div>
|
||||
|
||||
<!-- Step 3: Instagram -->
|
||||
<div class="loading-step" id="step-instagram">
|
||||
<div class="step-icon pending">
|
||||
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<circle cx="12" cy="12" r="10" stroke-width="2"/>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="step-text pending">Szukam Instagram <span class="source-tag instagram">IG</span></span>
|
||||
</div>
|
||||
|
||||
<!-- Step 4: LinkedIn -->
|
||||
<div class="loading-step" id="step-linkedin">
|
||||
<div class="step-icon pending">
|
||||
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<circle cx="12" cy="12" r="10" stroke-width="2"/>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="step-text pending">Szukam LinkedIn <span class="source-tag linkedin">LI</span></span>
|
||||
</div>
|
||||
|
||||
<!-- Step 5: YouTube -->
|
||||
<div class="loading-step" id="step-youtube">
|
||||
<div class="step-icon pending">
|
||||
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<circle cx="12" cy="12" r="10" stroke-width="2"/>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="step-text pending">Szukam YouTube <span class="source-tag youtube">YT</span></span>
|
||||
</div>
|
||||
|
||||
<!-- Step 6: Twitter/X -->
|
||||
<div class="loading-step" id="step-twitter">
|
||||
<div class="step-icon pending">
|
||||
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<circle cx="12" cy="12" r="10" stroke-width="2"/>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="step-text pending">Szukam X (Twitter) <span class="source-tag twitter">X</span></span>
|
||||
</div>
|
||||
|
||||
<!-- Step 7: TikTok -->
|
||||
<div class="loading-step" id="step-tiktok">
|
||||
<div class="step-icon pending">
|
||||
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<circle cx="12" cy="12" r="10" stroke-width="2"/>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="step-text pending">Szukam TikTok <span class="source-tag tiktok">TK</span></span>
|
||||
</div>
|
||||
|
||||
<!-- Step 8: Google Business -->
|
||||
<div class="loading-step" id="step-google">
|
||||
<div class="step-icon pending">
|
||||
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<circle cx="12" cy="12" r="10" stroke-width="2"/>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="step-text pending">Pobieram dane Google <span class="source-tag google">Google</span></span>
|
||||
</div>
|
||||
|
||||
<!-- Step 9: Save -->
|
||||
<div class="loading-step" id="step-save">
|
||||
<div class="step-icon pending">
|
||||
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<circle cx="12" cy="12" r="10" stroke-width="2"/>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="step-text pending">Zapisuje wyniki</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -838,7 +996,70 @@
|
||||
const csrfToken = '{{ csrf_token() }}';
|
||||
const companySlug = '{{ company.slug }}';
|
||||
|
||||
// All step IDs in order
|
||||
const allSteps = [
|
||||
'step-website',
|
||||
'step-facebook', 'step-instagram', 'step-linkedin',
|
||||
'step-youtube', 'step-twitter', 'step-tiktok',
|
||||
'step-google', 'step-save'
|
||||
];
|
||||
|
||||
// Platform step mapping
|
||||
const platformSteps = {
|
||||
'facebook': 'step-facebook',
|
||||
'instagram': 'step-instagram',
|
||||
'linkedin': 'step-linkedin',
|
||||
'youtube': 'step-youtube',
|
||||
'twitter': 'step-twitter',
|
||||
'tiktok': 'step-tiktok'
|
||||
};
|
||||
|
||||
// SVG icons for different states
|
||||
const icons = {
|
||||
pending: '<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" stroke-width="2"/></svg>',
|
||||
in_progress: '<div class="step-spinner"></div>',
|
||||
complete: '<svg width="18" height="18" 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>',
|
||||
error: '<svg width="18" height="18" 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>',
|
||||
missing: '<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 12H4"/></svg>'
|
||||
};
|
||||
|
||||
function resetSteps() {
|
||||
allSteps.forEach((stepId, index) => {
|
||||
const stepEl = document.getElementById(stepId);
|
||||
if (stepEl) {
|
||||
const iconEl = stepEl.querySelector('.step-icon');
|
||||
const textEl = stepEl.querySelector('.step-text');
|
||||
|
||||
if (index === 0) {
|
||||
iconEl.className = 'step-icon in_progress';
|
||||
iconEl.innerHTML = icons.in_progress;
|
||||
textEl.className = 'step-text in_progress';
|
||||
} else {
|
||||
iconEl.className = 'step-icon pending';
|
||||
iconEl.innerHTML = icons.pending;
|
||||
textEl.className = 'step-text pending';
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateStep(stepId, status, message) {
|
||||
const stepEl = document.getElementById(stepId);
|
||||
if (!stepEl) return;
|
||||
|
||||
const iconEl = stepEl.querySelector('.step-icon');
|
||||
const textEl = stepEl.querySelector('.step-text');
|
||||
|
||||
iconEl.className = 'step-icon ' + status;
|
||||
iconEl.innerHTML = icons[status] || icons.pending;
|
||||
textEl.className = 'step-text ' + status;
|
||||
if (message) {
|
||||
textEl.innerHTML = message;
|
||||
}
|
||||
}
|
||||
|
||||
function showLoading() {
|
||||
resetSteps();
|
||||
document.getElementById('loadingOverlay').classList.add('active');
|
||||
}
|
||||
|
||||
@ -867,6 +1088,46 @@ function closeModal() {
|
||||
document.getElementById('modalOverlay').classList.remove('active');
|
||||
}
|
||||
|
||||
// Animate step-by-step progress for social media platforms
|
||||
async function animatePlatformSteps(foundPlatforms, googleData) {
|
||||
const delay = 400; // ms between steps - slower for readability
|
||||
|
||||
// Website scan complete
|
||||
updateStep('step-website', 'complete', 'Skanowanie strony WWW zakonczone <span class="source-tag website">WWW</span>');
|
||||
await new Promise(r => setTimeout(r, delay));
|
||||
|
||||
// Process each platform
|
||||
const platforms = ['facebook', 'instagram', 'linkedin', 'youtube', 'twitter', 'tiktok'];
|
||||
for (const platform of platforms) {
|
||||
const stepId = platformSteps[platform];
|
||||
const platformLabel = platform === 'twitter' ? 'X (Twitter)' : platform.charAt(0).toUpperCase() + platform.slice(1);
|
||||
const sourceTag = `<span class="source-tag ${platform}">${platform === 'twitter' ? 'X' : platform.substring(0,2).toUpperCase()}</span>`;
|
||||
|
||||
updateStep(stepId, 'in_progress', `Szukam ${platformLabel}... ${sourceTag}`);
|
||||
await new Promise(r => setTimeout(r, delay / 2));
|
||||
|
||||
if (foundPlatforms && foundPlatforms.includes(platform)) {
|
||||
updateStep(stepId, 'complete', `<strong>${platformLabel}</strong>: ZNALEZIONO ${sourceTag}`);
|
||||
} else {
|
||||
updateStep(stepId, 'missing', `${platformLabel}: brak profilu ${sourceTag}`);
|
||||
}
|
||||
await new Promise(r => setTimeout(r, delay / 2));
|
||||
}
|
||||
|
||||
// Google data
|
||||
updateStep('step-google', 'in_progress', 'Pobieram dane z Google... <span class="source-tag google">Google</span>');
|
||||
await new Promise(r => setTimeout(r, delay));
|
||||
|
||||
if (googleData && (googleData.google_rating || googleData.google_reviews_count)) {
|
||||
const rating = googleData.google_rating ? `${googleData.google_rating}/5` : '';
|
||||
const reviews = googleData.google_reviews_count ? `${googleData.google_reviews_count} opinii` : '';
|
||||
const details = [rating, reviews].filter(Boolean).join(', ');
|
||||
updateStep('step-google', 'complete', `Google: ${details} <span class="source-tag google">Google</span>`);
|
||||
} else {
|
||||
updateStep('step-google', 'missing', 'Google: brak profilu GBP <span class="source-tag google">Google</span>');
|
||||
}
|
||||
}
|
||||
|
||||
async function runAudit() {
|
||||
const btn = document.getElementById('runAuditBtn');
|
||||
if (btn) {
|
||||
@ -874,6 +1135,9 @@ async function runAudit() {
|
||||
}
|
||||
showLoading();
|
||||
|
||||
// Start animation
|
||||
await new Promise(r => setTimeout(r, 300));
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/social/audit', {
|
||||
method: 'POST',
|
||||
@ -885,13 +1149,30 @@ async function runAudit() {
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
console.log('Audit response:', data); // Debug
|
||||
|
||||
hideLoading();
|
||||
// Animate the steps with found platforms and Google data
|
||||
const foundPlatforms = data.platforms || [];
|
||||
const googleData = data.google_reviews || {};
|
||||
await animatePlatformSteps(foundPlatforms, googleData);
|
||||
|
||||
// Save step
|
||||
updateStep('step-save', 'in_progress', 'Zapisuje wyniki do bazy...');
|
||||
await new Promise(r => setTimeout(r, 400));
|
||||
|
||||
if (response.ok && data.success) {
|
||||
showModal('Audyt zakonczony', 'Audyt Social Media zostal zakonczony pomyslnie. Strona zostanie odswiezona.', true);
|
||||
setTimeout(() => location.reload(), 1500);
|
||||
const summary = `Zapisano: ${data.profiles_found || 0} profili, wynik: ${data.score || 0}/100`;
|
||||
updateStep('step-save', 'complete', summary);
|
||||
|
||||
// Wait longer for user to see complete results
|
||||
await new Promise(r => setTimeout(r, 4000));
|
||||
hideLoading();
|
||||
showModal('Audyt zakonczony', `Znaleziono ${data.profiles_found || 0} profili social media. Strona zostanie odswiezona.`, true);
|
||||
setTimeout(() => location.reload(), 2000);
|
||||
} else {
|
||||
updateStep('step-save', 'error', 'Blad zapisu: ' + (data.error || 'nieznany blad'));
|
||||
await new Promise(r => setTimeout(r, 4000));
|
||||
hideLoading();
|
||||
showModal('Blad', data.error || 'Wystapil nieznany blad podczas audytu.', false);
|
||||
if (btn) btn.disabled = false;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user