- Email button as full-width card showing address + "Wyślij e-mail"
- Private message button with subtitle "na portalu Norda Biznes"
- Phone shown as plain text above action buttons
- Contact preferences moved to bottom as subtle footer
- Both actions clearly distinguishable with different styling
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Email/phone are already clickable links (mailto:/tel:), no need for
separate button. Renamed "Napisz" to "Wiadomość prywatna" with chat
icon to clearly distinguish from email contact.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Set is_norda_member=True when admin assigns active company to user
- Clear is_norda_member=False when last active company is removed
- Covers admin edit route and admin API add/remove company routes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create UserNotification when sending private message (bell icon)
- Add "Wiadomości" link in main nav between Social and Aktualności
- Unread badge syncs across nav, user menu, and bell via polling
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- "Powrót do firmy" link when composing from company profile
- "Wyślij e-mail" button opens default mail client (mailto:)
- "E-mail" button on company contact cards for direct mailto
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shows portal users linked to a company with their contact details,
role badges, Norda membership status, and direct messaging link.
Respects individual privacy settings (show_phone, show_email).
Addresses forum feedback from Jakub Bornowski (topic #18).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a "Send reset" action button in the Problems tab and user profile page,
allowing admins to send password reset emails directly from User Insights
dashboard. Each reset requires manual confirmation via dialog.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Users who had auth problems (failed logins, password resets, security
alerts) but have since logged in successfully are now shown in a
collapsed "Rozwiązane problemy" section. Active problems remain
prominently displayed at the top.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shows whether password resets and welcome emails led to successful logins:
- Summary cards: success rate, resolved/pending/failed counts, avg time to login
- Detailed table: each action with user, type, date sent, and outcome
- Resolved = user logged in after email, Pending = <48h, Failed = no login
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- _tab_problems: 750 queries → ~10 batch queries with GROUP BY
- _tab_engagement: 2550 queries → ~12 batch queries, sparkline in 1 query
- user_insights_profile: 60+ queries → batch trend (2 queries), bot filtering on all metrics
- Stat cards exclude UNAFFILIATED, dormant excludes never-logged-in users
- Engagement status: never-logged=dormant, login<=7d+score>=10=active, 8-30d=at_risk
- Badge CSS: support both at-risk and at_risk class names
- Problems table: added Alerts and Locked columns
- Security alerts stat card in Problems tab
- Back link preserves tab/period context
- Trend chart Y-axis dynamic instead of hardcoded max:30
- Timeline truncation info when >= 150 events
- Migration 080: composite indexes on audit_logs and email_logs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add is_bot column to user_sessions with backfill from user_agent patterns
- Update analytics_daily trigger to skip bot sessions
- Recalculate 90 days of analytics_daily without bot contamination
- Replace cumulative failed_login_attempts with time-based audit_logs queries
- Switch engagement score from linear (capped at 100) to log2 scale
- Expand section_map from 9 to 17 categories (~95% traffic coverage)
- Exclude robots.txt, sitemap.xml etc from page view tracking
- Add bot filter to all overview, pages, paths, and engagement queries
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Proactive alerts in Problems tab: never_logged_in, locked, reset_no_effect, repeat_resets
- 5th stat card showing never-logged-in users count
- Full problem chronology in user profile: audit_logs, emails, sessions, security alerts
- Resolution status card: resolved/pending/blocked/unresolved with time-to-resolution
- Timeline enhanced with detail field, CSS severity classes, and new icon types
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
email_logs.user_id is never populated for password_reset emails.
Match by recipient_email instead. Also fix failed_logins stat card
to use users.failed_login_attempts sum instead of security_alerts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Migration 064 fixes 12 records in company_websites table missing https://
- Added ensure_url filter to w.url in contact bar template as safety net
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Updated Facebook link from /nordabiznes to /profile.php?id=100057396041901
across all 4 locations (email templates, JSON-LD schema)
- Added Facebook link to site footer (Contact section)
- Added "Follow us on Facebook" to landing page CTA
- Redesigned upcoming events: side-by-side layout instead of stacked
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previous logo-circle.png was 404, favicon-192 was wrong icon.
Generated logo-email.png from favicon.svg compass via sharp.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shared _email_v3_wrap() helper: branded header with logo, full footer
with address/links. Updated: password reset, welcome, forum reply,
role notification. Action buttons grid layout in /admin/users.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Email: dark header with compass, company card, green checkmarks, Polish
date format, full footer with address, phone and tech support contact.
Actions: 4-column grid layout instead of vertical stack.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds envelope icon in AKCJE column that sends an email to the user
with their current company role and permissions summary.
Uses approved v3 email template with Norda Business branding.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Every email sent via send_email() now includes a BCC to the portal
administrator (MAIL_BCC env var, defaults to maciej.pienczyn@inpi.pl).
Recipients who are already in TO are automatically excluded from BCC.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix address from Hallera 18 to 12 Marca 238/5 in all JSON-LD, contact
section, and Google Maps embed
- Update geo coordinates for new address
- Broaden company descriptions: not just Wejherowo but also powiat
wejherowski, neighboring counties, and wojewodztwo pomorskie
- Update all meta descriptions, OG tags, hero text, and tile headers
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add Polish city name declensions to local keyword matcher
- Add openingHours string format alongside openingHoursSpecification
- Add Wejherowo to page title for city_in_title signal
- Add service+city keyword phrases in visible text (serwis, transport,
szkolenia, sklep, remonty, instalacje + Wejherowo/Rumia/Reda/Gdynia)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add email, image, priceRange, openingHoursSpecification to LocalBusiness JSON-LD
- Add Google Maps embed with address section on landing page
- Add local keywords (Wejherowo, Kaszuby) in visible text
- Add frame-src CSP directive for Google Maps iframe
- Responsive layout for map section on mobile
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
parsedate_to_datetime returns offset-aware datetime from Last-Modified
header, but datetime.now() is naive. Strip tzinfo before subtraction.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add missing SEO elements to improve audit score from 89 to 95+:
- Canonical URL and dynamic meta description blocks in base.html
- Open Graph tags (og:title, og:description, og:image, og:url, og:locale)
- JSON-LD structured data (Organization + WebSite schemas)
- robots.txt route with proper Disallow rules
- sitemap.xml route with homepage and release-notes
- LocalBusiness JSON-LD schema on landing page for Local SEO
- Last-Modified header for freshness signals
- Preload critical image for LCP optimization
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. seo_analyzer.py: Consider aria-label, title, img AND svg as valid
link text (SVG icon links were falsely counted as "without text")
2. routes_portal_seo.py: Calculate overall_seo score using
SEOAuditor._calculate_overall_score() before saving to DB
(was always None because stream route bypasses audit_company())
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. Extract HSTS, CSP, X-Frame-Options, X-Content-Type-Options from
HTTP response headers during portal SEO audit (were always None
because SEOAuditor doesn't check security headers natively)
2. Add aria-label to all social media and website icon links on
landing page tiles (300 of 317 links had no text content,
only SVG icons)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use auto table layout with minimal font (10px) and padding (3px 4px)
so browser calculates natural column widths. Remove colgroup.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reduce font size to 11px, padding to 4px 5px, use table-layout fixed
with colgroup for column width control. Score badges at 10px.
All ~35 columns should now fit on a full-width monitor.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use container-full class to expand this page to full monitor width
so the history table with ~35 columns is visible without scrolling.
Only affects this specific page, not the rest of the portal.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Show all collected SEO data in history table organized by color-coded groups:
PageSpeed scores, Core Web Vitals, On-Page SEO checks, Security headers,
Content metrics, and composite scores. Dashboard includes score cards with
deltas, check grid for 17 boolean checks, content metrics grid, and
horizontally scrollable history table with ~35 columns.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
OnPageSEOResult uses nested objects (meta_tags.title, images.total_images,
structured_data.has_structured_data). TechnicalSEOResult uses robots_txt.exists,
sitemap.exists, canonical.has_canonical. Fixed all field access paths.
Extracted DB save logic to _save_audit_to_db() for clarity.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Audit now runs step-by-step with real-time progress via Server-Sent Events.
Each of 9 steps (fetch, on-page, technical, PageSpeed, local SEO, citations,
freshness, save) shows status with spinner, checkmark, or error icon.
Removed old POST form in favor of SSE-based streaming approach.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SEOAuditor result dict contains datetime objects that can't be serialized
to JSONB. Added _make_json_safe() to recursively convert them.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Model had columns (overall_score, on_page_score, etc.) that didn't exist
in the migration. Updated model and templates to match the actual table.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The audit_owner_required decorator was never defined in utils/decorators.py,
causing ImportError that prevented the entire admin blueprint from loading.
Uses the same is_audit_owner() pattern as routes_audits.py and routes_social.py.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add /admin/portal-seo to run SEO audits on nordabiznes.pl
using the same SEOAuditor used for company websites.
Tracks results over time for before/after comparison.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>