- Person profiles (/osoba/, /profil/) now require membership
- Company detail shows limited "business card" for guests: name, logo,
category, short description — full content hidden behind membership CTA
- Banner "Złóż deklarację członkowską" for guests on company page
- Edit/AI buttons hidden for guests
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Forum: add @forum_access_required to ALL public routes (read+write)
- Reports: add @member_required to all report routes
- Announcements: add @member_required to list and detail
- Education: add @member_required to all routes
- Calendar: guests can VIEW all events but cannot RSVP (public+members_only)
- PEJ and ZOPK remain accessible (as intended for outreach)
UNAFFILIATED users (registered but not Izba members) are now properly
restricted from internal community features.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace plain textarea with Quill editor in compose and reply forms
- Support Ctrl+V paste of screenshots directly into message body
- Image toolbar button for file picker upload
- New endpoint POST /api/messages/upload-image for inline images
- Content sanitized via sanitize_html (bleach) with img tag support
- Messages rendered as HTML (|safe) instead of plain text
- Links clickable, images displayed inline in message body
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ICS export, RSS feed, and admin creation now correctly use
external_source (e.g. "Agencja Rozwoju Pomorza") as the organizer
instead of defaulting to "Norda Biznes" for external events.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
External events from partner organizations (ARP, KIG, etc.) can now
be added to the calendar with distinct visual treatment:
- Grey badge "ZEWNĘTRZNE" and muted date box in list view
- Grey color in grid view with border accent
- "Jestem zainteresowany" instead of "Zapisz się" (no commitment)
- Prominent "Przejdź do rejestracji" button linking to external organizer
- "Zainteresowani" section instead of "Uczestnicy"
- Toggle filter "Pokaż zewnętrzne" with localStorage persistence
- Admin form checkbox to mark events as external
New fields: is_external, external_url, external_source on NordaEvent.
Migration: 086_external_events.sql
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three public feeds: /feed/events.xml (upcoming events),
/feed/news.xml (announcements), /feed/pej.xml (nuclear news).
RSS 2.0 format with KIG custom fields (thumbnail, datawydarzenia).
RSS discovery links added to base.html head.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Allows company managers to set or correct the founding year. Field is
read-only when the year comes from KRS/CEIDG registry data.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds file attachment capability to NordaEvent model (attachment_filename,
attachment_path columns). Admin can upload PDF/DOCX when creating events.
Users see a download link on the event detail page.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Purple "PWA" badge next to browser name when session was from installed
PWA app. Also reflected in browser grouping as "Chrome Mobile (PWA)" etc.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Events shown as red 📅(N) above bars with attendee count
- Monday bars have dashed left border as week separator
- Weekend bars (Sat/Sun) in gray to distinguish from weekdays
- Hover tooltip shows event title and attendee count
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Company names like "Portal", "Joker", "Wakat" are also common Polish words
and cause false positive matches in event descriptions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Name linking now handles Polish declensions (Iwonę/Iwoną/Iwony → Iwona)
using stem-based regex matching. ICS and Google Calendar exports now include
full event description, speaker name, and properly formatted newlines.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Changed person linking to use User.id → /profil/<user_id> instead of
requiring Person record (person_id). This makes 57 additional users
linkable as green pill badges in event details and descriptions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix 500 error: company_detail → company_detail_by_slug (slug-based endpoint).
Add ORGANIZER field to ICS so calendar apps show "Norda Biznes" instead of
the importing user. Include speaker name in ICS description.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Match NordaGPT chat visual style — person names show as green pill badges,
company names as orange pill badges. Enriches event description with
auto-linked companies (not just persons). Speaker name also uses pill badges.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Users can now pin favorite conversations (shown at top with section header)
and rename them with inline editing. Adds is_pinned, pinned_at, custom_name
columns to ai_chat_conversations table.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
.contains() generates LIKE which fails on PG arrays.
Use .op('@>')(pg_array(...)) pattern matching existing codebase.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The codebase uses SessionLocal() with try/finally pattern,
not db_session. Import error broke all blueprint registration.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Email clients (Gmail, Outlook, Apple Mail) don't support CSS
linear-gradient(), causing white text on white background — company
name, header title, and CTA button were invisible.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Companies matched to ZOPK projects (score >= 40) now see a green
"Zielony Okrąg Przemysłowy Kaszubia" section on their profile with:
- Project name, type icon, and link type badge
- AI-generated collaboration description explaining relevance
- Relevance score percentage
- Links to project detail pages
- Hover effects for interactivity
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add /api/zopk-facts endpoint returning paginated facts from distinct
source articles, ordered by recency
- Add "Pokaż więcej" button with AJAX loading and fade-in animation
- New cards are clickable with same hover effect as initial ones
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace broken distinct() call with group_by() and add db.rollback()
in except handler to prevent failed transaction from cascading.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pick one fact from each of the 3 most recent distinct articles
instead of 3 facts from the same article.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Change from random() to published_at DESC — show newest, most
relevant facts instead of random old ones
- Translate fact type labels to Polish (investment→inwestycja,
decision→decyzja, event→wydarzenie, etc.)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Enable "Czy wiesz, że..." ZOPK facts widget on homepage for all
logged-in users (was admin-only and disabled with {% if False %})
- Add "Oś czasu" button to ZOPK admin dashboard navigation (was
only accessible from public /zopk page edit links)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
External monitoring via UptimeRobot (free tier) with internal health
logger to differentiate ISP outages from server issues. Includes:
- 4 new DB models (UptimeMonitor, UptimeCheck, UptimeIncident, InternalHealthLog)
- Migration 082 with tables, indexes, and permissions
- Internal health logger script (cron */5 min)
- UptimeRobot sync script (cron hourly) with automatic cause correlation
- Admin dashboard /admin/uptime with uptime %, response time charts,
incident log with editable notes/causes, pattern analysis, monthly report
- SLA comparison table (99.9%/99.5%/99%)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevents confusing "link invalid" error when verification link is
clicked twice (or prefetched by email clients like Outlook). The token
now expires naturally instead of being cleared on first use.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New page /admin/gbp-audit/match-places shows companies without
google_place_id. Admin searches Google Maps, reviews results, and
confirms the correct match. Adds search_places_raw() to return all
results without name filtering.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Companies with an existing google_place_id now skip the name-matching
search and use the verified Place ID directly. This fixes companies like
ALMARES ("PSB Profi Almares" in Google) whose single-word name failed
strict matching. Also removes invalid 'source' kwarg from GBPAudit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Batch audit now collects changes without saving to DB. Admin must
review before/after differences and approve or discard. Mirrors the
existing social audit enrichment review pattern.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Run audit for all active companies with background thread, live progress
panel, and optional Google Places API data refresh. Pattern mirrors
existing social audit batch implementation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Personalized guide with GBP links (write review, view profile, photos)
for each member's company. Includes tips on collecting reviews,
responding to feedback, Local Guides program, and Google policies.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Admin-sent reset emails now say "Administrator portalu wygenerował
link do ustawienia hasła" instead of generic "zażądano resetowania".
Validity period shown correctly: 24h for admin, 1h for self-service.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The admin reset-password endpoint used datetime.utcnow() while the
validation used datetime.now(), causing tokens to appear expired
immediately on CET servers. Changed to datetime.now() and extended
admin-initiated resets to 24 hours validity.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Admin can now send a password reset email directly from /admin/users
instead of manually copying and sharing the reset link.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove unused social-media analytics panel (replaced by social-audit).
Rename admin menu items for clarity:
- Forum → Moderacja forum
- Ogłoszenia → Moderacja ogłoszeń
- Rekomendacje → Moderacja rekomendacji
- Deklaracje → Zarządzanie deklaracjami
- Składki → Zarządzanie składkami
- Korzyści → Zarządzanie korzyściami
- Social Media → Audyt social media
- Social Dashboard → Publikacja social media
- Kalendarz → Zarządzanie kalendarzem
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace generic "OAuth API niedostępne" with platform-specific
instructions explaining what's needed to fetch data:
- Instagram: 3-step guide (Business account, FB link, OAuth)
- Twitter/YouTube: note that data is fetched automatically
- Facebook: prompt to connect OAuth
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- YouTubeService now fetches: subscribers, views, video count, description,
avatar, banner, country, creation date, recent 5 videos
- Enricher uses API first, falls back to scraping
- Extra YouTube data stored in content_types JSONB
- Audit detail shows view count, country, creation date, recent videos
- Requires enabling YouTube Data API v3 in Google Cloud Console
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Users can now select which platforms to scan (Facebook, Instagram,
LinkedIn, YouTube, Twitter/X, TikTok) via checkboxes in the
confirmation dialog. Backend filters both company list and profiles.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3 attempts with 2-5s random delay between retries. Detects authwall
and rate limit (429/999) responses. Updated status message to explain
LinkedIn's inconsistent availability to users.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
og:description from LinkedIn/Facebook embeds dynamic follower counts
and varies by language, causing false "changes" on every scan.
The field is still saved to DB, just not tracked for review.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of checking profile.source (which was never 'facebook_api'),
now checks SocialMediaConfig for companies with OAuth page_id.
Added progress indicator with spinner and percentage during scan.
After API sync, page auto-refreshes to show new data.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
Events with plain text (no <p>, <br>, <div>, <ul> tags) now get
newlines converted to <p>/<br> for proper formatting.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- /profil/<user_id>: simple profile for users without person_id,
redirects to /osoba/ if person_id exists
- event.html: all attendees are now clickable links (green)
- Auto-link User→Person by name match on every login (no manual scripts)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Parse HTML into tags and text nodes, only process text nodes outside
<a> tags. Uses \b word boundary instead of broken lookbehind.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Member names in event description link to person profiles
- URLs auto-linked (nordabiznes.pl, https://... etc)
- Calendar add section: blue gradient card with Google/Outlook buttons
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- POST /konto/avatar: upload, center-crop to square, resize 300x300
- POST /konto/avatar/delete: remove file and clear DB
- dane.html: interactive avatar editor with hover overlay
- person_detail.html: show photo if available, fallback to initials
- Migration 070: avatar_path column on users table
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rada Izby badge next to name
- Last active label (e.g. "Aktywny 2 dni temu")
- Forum stats (topics + replies count)
- Recent events attended (up to 5, linked)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Norda Biznes logo on featured events
- Speaker name links to person profile
- Google Calendar and ICS/Outlook export buttons
- Update dev banner: official launch April 9, 2026
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Facebook, Instagram, LinkedIn block public access - messages now explain
this and suggest OAuth API connection instead of generic "no data".
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace all _enrichment_status in-memory dict references with
file-based shared state (/tmp/nordabiz_enrichment_state.json).
Fixes enrichment appearing instantly complete in multi-worker env.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Live panel with spinner, progress bar, company counter
- Each company appears as a row with platform badges showing status
(changes/no_changes/skipped/error/no_data)
- Incremental polling (since= param) for efficient updates
- After completion: link to review page if changes found
- Blue highlighted rows for companies with new data
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Scraper now collects data to staging area (in-memory), NOT to database
- New review page (/admin/social-audit/enrichment-review) with:
- Summary stats (scanned, changes, skipped, errors)
- Per-company expandable sections with before→after diff per field
- Pending approval banner with change count
- Sticky bottom bar with Approve/Discard buttons
- Approve endpoint writes staged data to DB
- Discard endpoint clears staging without touching DB
- After scan completes, auto-redirect to review page
- Companies without changes shown in collapsed list
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Scraper no longer overwrites API data (source priority hierarchy)
- Per-platform data provenance badges (API OAuth/Scraping/Manual/Unknown)
- Expandable field-level source breakdown (which fields from API vs scraping)
- OAuth status per platform with connect/renew/sync links
- "Run audit" button on dashboard (background enrichment for all companies)
- "Run audit" button on detail view (single company enrichment)
- Enrichment progress polling with real-time status updates
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Social Health Score (0-100) composite: activity 30%, engagement 25%,
completeness 20%, growth 15%, cross-platform 10%. Add followers growth
rate from JSONB history, activity status classification (active/moderate/
slow/dormant/abandoned). Display health score in dashboard table and
detail view with color-coded ring, growth indicators per platform,
and cross-platform coverage score.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shows latest version, date, one highlight feature, and count of other
changes. Links to /release-notes. Extracted _get_releases() helper
for reuse between index and release_notes routes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New route /admin/social-audit/<company_id> showing detailed social media
audit per company: platform cards with metrics, profile checklist,
completeness bar, recommendations, invalid profiles section.
Added audit detail icon in dashboard table alongside profile link.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shows full conversation thread when replies exist, with per-message
status (sent/read with timestamps), sender→recipient flow, and
current message highlighted. Single messages show status bar at bottom.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
UserCompanyPermissions has no 'position' column, causing 500 error on
/wiadomosci/nowa. Use User.company_role as fallback for position display.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add attachment display section in view.html with inline image thumbnails and document download links
- Add eager loading of attachments in inbox, sent, and view queries
- Add paperclip emoji indicator in inbox and sent message lists
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Required for Google OAuth verification to remove "unverified app" warning.
Adds /polityka-prywatnosci and /regulamin routes with Polish legal content,
footer links, and sitemap entries.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After token exchange, fetches Google userinfo to save the email and
name of the Google account used for authorization. Displays this info
on the GBP audit page so users know which account to reconnect with.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shows whether the company has an active Google Business Profile
console connection, with clear status indicators (connected/expired/
not connected) and guidance on how to reconnect via Konto → Integracje.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix google_reviews_data bug: data is dict with 'reviews' key, not a list
(was always hidden by 'is not mapping' guard)
- Add rating distribution bar chart from Places API review data
- Display google_photos_metadata table (author, dimensions, owner photos)
- Add audit diagnostics footer (source, version, errors)
- Show owner_response_time and keywords on individual GBP reviews
- Add Polish translations for Google Places business types (40+ types)
- Use primary_type_display for human-readable category names
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add opening hours, business status, Google types, website URL, Place ID,
rating/reviews/photos summary, website tracking indicators, Google attributes,
Places API reviews, smart recommendations engine, and benchmarks comparison
with other Norda Biznes members.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Speed Index, Time to Interactive, Total Blocking Time from PageSpeed
- Benchmark table: company vs category avg vs all-members avg with arrows
- WHOIS via RDAP: domain registration, expiry, last change, registrar
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Uses ip-api.com to look up ISP name, organization, city, region,
country and AS number for the hosting IP address.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New /admin/seo/<id> detail page with full audit breakdown
- 4 score cards with visual rings and Polish descriptions
- Recommendations section: critical/warning/info issues with actionable text
- SEO checklist: meta tags, sitemap, robots, structured data, OG tags
- Technical details: SSL, mobile, CWV metrics, security headers
- Google Business Profile section when available
- Add "Szczegoly" button in company table (replaces profile link)
- Add visible column descriptions above table (not hidden tooltips)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove in-memory log viewer (DebugLogHandler, 5 routes, template,
menu links, endpoint aliases). Logs available via journalctl on server.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace complex dashboard (11 stat cards, token stats, model breakdown,
recent logs, advanced filters) with clean 3-card PLN cost view,
usage by type, user ranking, company ranking, and daily history.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace anonymized metadata ("45 chars") with real topic categories
(O firmach, Szukanie kontaktu, O wydarzeniach, etc). Remove empty
conversion stats, UTM sources, avg message length. Keep feedback
with ratings in compact two-column layout.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace complex Problem Score tables and remediation metrics with
two clear sections: "Members needing help" (actionable alerts with
buttons) and "Technical problems" (collapsed, for developer).
Removes ~200 lines of unused scoring/remediation code.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Paths tab (entry/exit pages, transitions, drop-off) provided little
actionable value for association admin. Session length distribution
moved to Overview as compact card layout. Menu: 6 tabs -> 5.
Old /paths URL redirects to /overview.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Zombie sessions (browser tab left open for days) were inflating
duration-based scores. LEAST(duration_seconds, 3600) per session
ensures only active time counts toward engagement.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Hovering over the activity bar shows breakdown: sessions, pages,
clicks, time, conversions, searches with weights and final score.
Column header explains what the metric measures.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace 3-section active/at-risk/dormant design with one clear table
showing all members sorted by activity level. Green/yellow/gray bars,
human-readable last login dates, inactive users dimmed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace single 50-row table with Active/At Risk/Dormant sections.
Remove noisy WoW% column and sparklines, add human-readable last activity.
Score displayed as colored bar instead of abstract number.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Alert improvements:
- "Send welcome email" button on never-logged-in alerts (sends activation
email with 72h reset token)
- "Reset password" button on reset-no-effect and repeat-resets alerts
- Buttons show status: sending → sent/error, prevent double-clicks
- New POST /admin/analytics/send-welcome/<user_id> endpoint
Companies needing attention:
- New section on Overview tab listing active companies with incomplete
profiles (missing description, contact, website, address, logo)
- Sorted by number of issues, shows quality badge and edit link
- Checks logo file existence on disk for webp/svg/png/jpg
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Pass search_query_id to search results template
- Add POST /api/analytics/search-click endpoint to update SearchQuery
with clicked_result_position, clicked_company_id, time_to_click_ms
- Add data-position and data-company-id attributes to company cards
- Add JS using navigator.sendBeacon for non-blocking click tracking
- Fix content engagement labels: "nowych (30 dni)" instead of "opublikowanych"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Open rate now shows percentage of active members who read at least one
piece of content, capped at 100%. Previously showed inflated numbers
because it counted reads of older content against only recent publications.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Company pages use /company/<id> paths (not slugs). Updated _humanize_path
to try numeric ID lookup first, and fixed company popularity query to
match only /company/<digits> paths.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. Dashboard admin widget: compact KPI cards (active/total members with
progress bar, sessions, security alerts, never-logged users)
2. Overview KPI: first card shows X/Y members with progress bar and %
3. Feature adoption chart: which portal modules are used by what % of
members (NordaGPT, Forum, Search, Calendar, B2B, News, Companies)
4. Event comparison table: views, unique viewers, RSVP count per event
in the last 60 days - helps plan better events
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Event paths like /kalendarz/29 were matched by the /kalendarz/
prefix before reaching the dynamic event title lookup. Reorder
to check dynamic patterns (event ID, company slug) first.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The refactor to _kpi_for_period() removed the local variables but
the return dict still referenced them. Replace with the kpi dict.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. KPI cards show trend arrows (▲▼ X%) comparing current vs previous period
2. Raw paths replaced with human-readable names throughout Pages and Paths tabs:
- /company/pixlab-sp-z-o-o → PixLab sp. z o.o.
- /kalendarz/45 → Spotkanie z posłami (event title from DB)
- /login → Logowanie, /dashboard → Panel użytkownika etc.
3. Bounce rate threshold adjusted (85%+ warning instead of 70%)
with tooltip explaining 70-85% is normal for membership portals
4. Column headers changed from technical (Ścieżka, Exit rate) to
user-friendly (Strona, Wsp. wyjścia)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add EXCLUDED_PATH_CONTAINS patterns to catch security scanners using
varied paths (.php, .env, wp-includes, aws-config, phpinfo etc.)
that bypass the prefix-based filter.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add KPI stat cards to Overview tab (active users, sessions, pageviews, bounce rate)
- Filter technical paths from Pages and Paths tabs (/sw.js, /robots.txt, /.git/, /.env, etc.)
- Cap time_on_page at 30min to exclude outlier tabs left open
- Format time as human-readable (Xm Ys) instead of raw seconds
- Mask security tokens in unused pages list (/reset-password/*** etc.)
- Fix Polish labels (period display: "7 dni" instead of "week")
- Add percentages to logged/anonymous donut chart legend
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Merged analytics_dashboard, user_insights, and chat_analytics into a
single consolidated view at /admin/analytics with 6 tabs: Overview,
Engagement, Pages, Paths, Problems, Chat & Conversions.
- Menu reduced from 5 to 3 items (Analityka, Monitoring AI, Debug)
- All queries now use bot filtering consistently
- Old URLs redirect to new consolidated view
- Removed 1,380 lines of duplicate templates
- Net reduction: -1,328 lines
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Release notes for Gemini 3.1 migration, rate limiter fix, calendar fix,
lxml parser, and announcements section.
Fix external link button in announcement detail - force white text on
primary background for readability.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace gemini-3-pro-preview with gemini-3.1-pro-preview (old deprecated March 9)
- Add gemini-3.1-flash-lite-preview as quality fallback in chain
- Remove last google.generativeai import from zopk_knowledge_service.py
- Update pricing, thinking models, and preview models sets
- Keep '3-pro' alias for backward compatibility across codebase
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add year range validation (2020-2100) on /kalendarz/ to prevent ValueError crash
- Exempt notification/message unread-count endpoints from rate limiting (shared IP via NAT)
- Replace deprecated google.generativeai SDK with google-genai in nordabiz_chat.py
- Remove dead news_service import that logged warnings on every worker startup
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds minimal service worker for PWA installability. On Android Chrome,
the smart banner and install page now trigger the native install dialog
directly instead of showing manual instructions. iOS still shows
step-by-step guide (Apple provides no install API).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Mobile-only install instructions at /zainstaluj-aplikacje with
auto-detected iPhone/Android tabs, CSS UI mockups, and step-by-step
guide. Smart banner appears after 3s on mobile (dismissible via
localStorage), hidden in standalone mode. Footer link and login
page hint also mobile-only.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sends email to admin when a user with last_login=NULL successfully
sets their password via reset link (first-time activation).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add frontend button disable on submit and backend duplicate detection
(same user, same content, within 30 seconds).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New column: users.notify_email_messages (default true)
- Send email via MS Graph when someone receives a private message
- Toggle in /konto/prywatnosc to enable/disable email notifications
- Email includes message preview, sender name, and direct link
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Merged three separate v1.36-38 entries for Feb 20 into single v1.36.0.
Renumbered: Feb 21 = v1.37.0, Feb 22 = v1.38.0.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Hovering over a reaction button (👍, ❤️) now shows who reacted.
Names are loaded server-side and updated dynamically after toggling.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
User model has 'name', not 'full_name'. The incorrect attribute caused
every hover tooltip on forum usernames to show an error.
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>
- 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>
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>
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>
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>
Show company logos with website and social media links
to unauthenticated visitors below the existing landing
page content, improving local content indexability.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
- Bulk discovery skips companies with any candidate (including rejected)
- Single discovery skips URLs from previously rejected domains
- Dashboard shows list of companies rejected by admin with note
that they won't be re-searched in bulk mode
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
In-memory _bulk_jobs dict was per-worker in gunicorn (4 workers),
causing poll requests to miss job state. Now uses /tmp JSON files
visible to all workers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Evaluate top 3 Brave results instead of just taking the first one.
Add domain name matching signal (+2 pts when domain contains company name).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Show Brave search description under company name and scraped page
text snippet (first 500 chars) as expandable row below each
candidate, helping admin verify if the URL matches the company.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bulk query now excludes companies that already have pending/accepted
candidates, so only truly new companies are processed via Brave API.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Brave free tier rate limits aggressively (429 after ~1 req/s).
Added retry logic (3 attempts: 3s, 6s, 9s waits) and increased
inter-company delay from 2s to 5s. Error candidates are now
cleaned up before retry to allow re-discovery.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace single latest_result field with cumulative log array and
offset-based polling to prevent missed entries and race conditions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Automated discovery using Brave Search API to find company websites,
scrape verification data (NIP/REGON/KRS/email/phone), and present
candidates with match badges in the data quality dashboard.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace substring matching with word-boundary tokenized matching
- Short names (1-2 words): require ALL significant words to match
- Longer names (3+): require at least 50% word overlap
- Pick best-scoring result instead of first match
- Add company_name validation to competitor_monitoring_service
- Show Google profile name in dashboard hints for admin verification
- Display mismatch warning when Google name differs from company name
Prevents cases like "IT Space" matching "Body Space" (score 0.50 < 1.00 threshold).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add clickable field coverage bars to filter companies missing specific data
- Add quick-action buttons (Registry/SEO/GBP) per company in dashboard table
- Add stale data detection (>6 months) with yellow badges
- Implement weighted priority score (contacts 34%, audits 17%)
- Add data hints in admin company detail showing where to find missing data
- Add "Available data" section showing Google Business data ready to apply
- Add POST /api/company/<id>/apply-hint endpoint for one-click data fill
- Extend website content updater with phone/email extraction (AI + regex)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Extract 12-field completeness scoring to utils/data_quality.py service
- Auto-update data_quality_score and data_quality label on company data changes
- Add /admin/data-quality dashboard with field coverage stats, quality distribution, and sortable company table
- Add bulk enrichment with background processing, step selection, and progress tracking
- Flow GBP phone/website to Company record when company fields are empty
- Display Google opening hours on public company profile
- Add BulkEnrichmentJob model and migration 075
- Refactor arm_company.py to support selective steps and progress callbacks
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove the dark/light background toggle from logo selection modal,
company detail pages (admin and public), and the toggle-logo-bg API
endpoint. The feature didn't meet UX requirements.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When choosing a logo, admins can now switch between light and dark
backgrounds to see which works better for transparent logos. The
selected background preference is automatically saved when confirming.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a per-company setting to display logos on dark background,
useful for logos with white text or light-colored elements.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The logo path was hardcoded to .webp even when the actual file was .svg,
causing broken image display for SVG logos like Orlex Design.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Logo check only looked for .webp files, missing SVG logos like
Orlex Design. Now checks both extensions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CEIDG enrichment now creates individual CompanyPKD records from the PKD
list, matching the pattern used by KRS enrichment for consistency.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
KRS API returns two date formats for za_okres field:
"01.01.2011 - 31.12.2011" (older) and "OD 01.01.2013 DO 31.12.2013" (newer).
Use regex to extract all date patterns instead of splitting on ' - '.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Map remaining KRS API fields: registration date, company agreement date,
duration, capital currency, OPP status, representation rules, shareholders
(wspólnicy), and financial reports (sprawozdania finansowe).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
KRS API returns kontakt_krs with www and email fields. These were not
being mapped to Company.website and Company.email. Now enrichment
extracts these and normalizes the URL (adds https:// prefix, lowercase).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The KRS enrichment function was only mapping forma_prawna, legal_name
and capital. Now maps REGON, address (ulica + nr_domu), city, postal,
primary PKD code on Company model, and NIP if missing. Fixed field
name mismatches: nr_domu (not numer), opis (not nazwa), glowna (not
glowny). City names are title-cased from KRS uppercase format.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New admin page at /admin/companies/{id}/detail showing company data,
completeness score, and action buttons for registry data (CEIDG/KRS),
logo fetch, SEO audit, social media audit, and GBP audit.
Includes "Uzbrój firmę" master button for sequential execution.
Company list now links to detail page with "NOWA" badge for recent entries.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PDF files: "Otwórz" (inline in browser) + "Pobierz" (download)
DOCX files: "Pobierz" only (browsers can't display DOCX inline)
Removes LibreOffice on-the-fly conversion - simpler and more reliable.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use /usr/bin/libreoffice (Gunicorn has limited PATH)
- Set HOME=/tmp (LibreOffice needs writable home dir)
- Handle FileNotFoundError with user-friendly message
- Improve error flash messages in Polish
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
DOCX/DOC documents are now converted to PDF using LibreOffice headless
when the user clicks "Otwórz". The converted PDF is cached next to the
original file so subsequent views are instant.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PDF documents now have an "Otwórz" button that opens
the file in the browser's built-in PDF viewer.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add document management routes (upload, download, soft-delete) to board blueprint,
link BoardDocument to BoardMeeting via meeting_id FK, add documents section to
meeting view template, and include import scripts for meeting 2/2026 data and PDFs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>