CHANGELOG
What's New in Kartivo
Track every improvement, new feature, and fix we ship. We're constantly making Kartivo better for you.
August 2026
Name the seller brand in the recovery email again
Recovery email branded itself as an arbitrary seller
Make account recovery actually work; unpublish the TEST course
One-click magic login in the welcome email and on the thank-you page
Patch the vendored Requests deserialization gadget; drop dead composer metadata
Clear all 35 dependabot alerts + CI4 4.7.0 -> 4.7.4
Backport the kv-nothanks-fallback hotfix from production
Stop the in-portal reconciler reporting a false all-clear
Link the rules page to the actual abandoned customers
Wait for fbq instead of bailing out when it is not ready yet
GST preview must follow the discounted total, not the base price
Self-serve Meta CAPI settings on the Pixel & GTM page
Per-course Welcome email editor
Null-guard the IPN event checks + track the live controller
Claim before sending Purchase from the thank-you page
Course-first mobile buyer dashboard + one-tap resume + bottom nav
Heal COMPLETED-but-unprovisioned in-portal orders
One set of credentials per student, ever
Resolve payment_log by payment_intent so both senders share one event_id
Send each Purchase exactly once (send-once ledger)
Render By time of day as an ApexCharts graph (Page Views area + Sales columns) instead of a table
Per-course funnel (landing page → checkout → sale), by-time-of-day breakdown, + add to Courses sidebar menu
Add Overview section — Page Views / Unique Visitors / Sales / Conversion / Revenue KPIs + day-by-day trend (sourced from wl_payment_log like the dashboard funnel)
Add Unique Visitors column to the funnel (from pl.visitor_id) — matches the dashboard traffic-funnel visitor metric
Backfill re-selected country-only IPs forever (gated on city='') — gate on country only so rows drain
Course geo (state/city) + per-page funnel reports (seller + admin) + session-IP geo backfill
Auto-apply coupon from ?coupon=/?kvcoupon= URL param
Dedup GTM-fired Purchase pixel on thank-you page via event_id
Include required product_token in payment_log insert
Add Student tool — self-serve manual course enrollment
App:complete-manual-sale — welcome email + India INR invoice for manual sales
USD/INR currency toggle on the public pricing page
Customer 404 shim, mobile in students+transactions, PhonePe location capture
Close unauthenticated admin takeover, untrack leaked env, gate reCAPTCHA dev-bypass
Lesson page horizontal overflow on mobile (content unreadable, cut off right)
Certificates registry queried non-existent ct.created_at (column is issued_at) -> 500
Add Certificates to the Courses sidebar submenu (All Courses / Students / Analytics / Certificates)
Make Confirm&Send modal physically unable to full-reload the page
Confirm no longer blocks on email failure
Wait for DataTables lib before initialising the drafts table
Confirm & Send no longer reloads the drafts table + returns wim's number
Invoices:bulk-finalize command — bulk confirm+push India/INR drafts (wim numbers & Kartivo adopts), skips .test emails + --min-amount/--exclude filters, --dry/--email/--limit
Public certificate link fixes email 404 + enables sharing/verification
July 2026
Surface certificate on the portal course list — completed + certificate-enabled cards now show a 'View Certificate' button (was only reachable inside the course overview hero)
Rich certificate completion email — achievement hero, next-steps, dynamic 'continue learning' cross-sell of the seller's other courses, browse-all CTA; centralized in CertificateGenerator::completionEmail() so live + backfill share one template
Certs:backfill spark command — backfill certificates for pre-existing completions, with preview/test email and a confirmed email-burst mode
Certificate registry + auto-issue & email on completion
Scope funnel-product-mapping dropdown to the funnel's own collection — was hard-coded collection_id=0 (loaded all ~1,900 products across every collection, burying the funnel's own products; cross-collection picks also mismatch the saved funnel_product col
Consolidate course bumps into the main course — attribute a shared ad account's spend to its highest-sales (main) course so main+bumps show as ONE row (sales+spend), not a sales-only main + spend-only bump
Include mapped courses in getReportData — dashboard reported funnels only; now folds published courses (wl_courses.meta_ad_accounts) with course sales (product→course map) + course spend, currency-scoped, in all 3 report modes
Reconcile orphaned funnel upsells — native-gateway funnel_id=0 sales (buyer's single funnel that day) get attributed so ROAS counts them; backfilled 144 + hourly self-healing cron
Selectable course-page layouts — classic redesign + long-form landing + per-course admin toggle
GyaanShala LMS UI/UX Phases 2-6 (brand CSS, empty-state cleanup, counters, course-detail polish)
Proper content-page typography site-wide (About/Privacy/Terms/Refund readable + styled)
Mirror header nav to mobile offcanvas menu (Home/About/Contact + categorized courses)
Course SEO/OG/schema now fire on clean /course/ URLs (detect by course_id, not URI segment)
Add OG/Twitter social-preview tags + branded 1200x630 og-image; fix doubled/broken favicon path
Update hero + pricing table for 17 modules/10+ recipes; move packaging into its own dedicated section (out of the soaps grid)
Show Courses section always-visible at top (drop tabs) — SPA/HTMX swap + BS tab JS made the Courses tab unreliable; courses now render server-side above funnels
'Login as student' impersonation from students admin page — ownership-checked, issues portal auth_token, preserves seller admin session
Clear error on duplicate manual invoice number (was cryptic 'transaction rolled back'); Wim suggest-next now skips Kartivo-used numbers
'Unknown Product' on My Purchases — member plan.product_id is a wl_product_collection_users_data id, not wl_products; join the right table for product name/price/image
Show student Mobile column on students admin page (member_phone_no + payment-log fallback)
Funnels/Courses tabs so course mapping is discoverable (was buried below 31 funnel cards); lazy select2 init on courses tab
Resolve real currency symbol from wl_global_currency — customer portal showed static USD for INR/non-USD courses
Tax uses order currency (not hardcoded $0) + entity-encode mail-log body/subject so INR does not store as '?' in latin1 wl_mail_log_history
Divide payment timestamp by 1000 not 100 — /100 made order_date 10x (year 2535) on PhonePe sales
Parse 'stats yesterday|this week|last week|last 7 days|this month|last month' → date-range digest
SendRoasDigest multi-currency per-currency blocks + all funnel/course rows + --start-date/--end-date range
MetaRoasReportService::perEntity — multi-currency, funnels+courses, no currency filter (includes INR accounts)
Map Meta ad accounts to courses (not just funnels) + show account currency on the mapping page
JSON_INVALID_UTF8_SUBSTITUTE on the datatable endpoint — latin1 bytes in mail subject/product_title made json_encode return false → DataTables ajax error
SaleFinalizer reverts the atomic claim if provisioning fails, so a failed finalize stays retryable (no COMPLETED-but-unprovisioned orphan)
SaleFinalizer atomic status-claim (exactly-once across callback/webhook/reconcile) + defer Welcome email off the redirect path (review)
SaleFinalizer — instant PhonePe completion on return (member+enroll+login email), shared by callback + reconcile; idempotent, redirect-safe
App:phonepe-reconcile — verify NULL PhonePe orders against PhonePe + finalize the paid ones (fixes silent webhook-miss orphans)
Derive gateway from order (log_gateway_name) so it reconciles PhonePe/other gateways too, not just Stripe
App:send-pending-course-welcomes — auto-send login email to enrolled course buyers (idempotent, windowed) — closes the no-webhook welcome gap
Server-side DataTables (pagination/search/ordering) matching platform pattern; filters+tabs drive ajax; no page reloads
Don't full-reload after send/void — remove row in place, preserve filters/scroll (bulk-send workflow)
Resolve tenant SMTP from the sale's collection (payment_log_id) not first-ASC — fixes SMTP connect() failed
Batch WIM sync-status into one request (was ~200 per-row fetches on load + every 30s)
Server-side currency/profile/country/search filters (no datagrid)
Coupon usage-limit guard use >= so max_usages is an exact cap
Coupon usage-limit guard use >= so max_usages is an exact cap
Coupon usage-limit guard read max_discount, not max_usages
Coupon usage-limit guard read max_discount, not max_usages
Require valid email before Stripe confirm (Link can't complete email-less) + receipt_email on PaymentIntent
Use ->distinct() not select('DISTINCT ...') (CI4 escaped it into invalid SQL)
--collection all (iterate externally-provisioning collections) + relative --since for cron backstop
App:reconcile-sales — detect+heal paid-but-unprovisioned Stripe sales; load global helper in provision-sale
Error-level [ipn-dispatch] logging on IPN delivery failure (observability; no behavior change)
Authoritative name from wl_payment_log.full_name + include offer_bumps (review) + resilience plan doc
App:provision-sale — re-run provisioning for a paid-but-unprovisioned order (dry-run default, idempotent)
Clone Product always failed 'Gateway Id is required' — broken operator precedence in 5 required-field guards (!X !== null always true)
Per-level GTM container field on /conversions (writes wl_conversion_tracking.gtm_container_id)
GTM per-level dataLayer on thank-you page (value/currency/ids/hashed identity/click-ids)
TrackingConfigResolver::resolveGtm — cascade GTM container product→funnel→collection + tests
Try/catch saveLandingPixel write (graceful pre-migration) — matches saveLevelPixel (review)
Landing-page pixel selector on /conversions (writes wl_meta_connections.landing_pixel_id)
Guard landing-pixel query (never 500 a live landing page during deploy window) + 2 test cases (review)
Inject seller landing-page Meta pixel on custom-domain pages (Phase C2) + unit tests
Add landing_pixel_id to wl_meta_connections (Phase C2)
Auto-apply coupon from ?coupon_code= URL param (reuses Apply button, hides the box on success) — for exit-intent links
/conversions funnel + product Meta-pixel override UI (writes wl_conversion_tracking)
Add funnel_id to checkout $logData select (funnel cascade was dead on browser path) + de-shadow $productId (review)
Browser + server CAPI resolve pixel via TrackingConfigResolver (funnel/product cascade)
TrackingConfigResolver — cascade product→funnel→collection + collection fallback + unit tests
Wl_conversion_tracking table (multi-level Meta/GTM/Google config, Phase B)
Inject utm-capture.js into custom-domain published pages (CustomDomainFilter) so funnel landing pages decorate checkout links cross-domain
Load bridge-rehydrate.js first on checkout (before GTM/pixel)
Kartivo_capi_identity() falls back to kv_fbp/kv_fbc (cross-domain mirror) when _fbp/_fbc cookies absent
Landing side — capture _fbp/_fbc + wbraid/gbraid, decorate checkout links with kvfbp/kvfbc + node tests
Checkout-side _fbp/_fbc cookie rehydration from kvfbp/kvfbc URL params + node tests
Same wl_payment_log column fix in API v1 RefundsController — login_id from apiUserId, drop absent login_id/buyer_email select
Stop 3 live payment-flow errors — null-guard WHMCS service lookup (CRITICAL 500), correct wl_payment_log columns (no login_id; aff_id is a column not a table)
Filter/sort now use delegated events (survive HTMX swaps) + add 'With funnel' filter option
Searchable pixel dropdown (select2) — type to find a pixel by name/ID among the 74
Filter Active/Inactive/All (hide no-sale/empty collections by default) + search + sort by most sales / revenue / newest / name; per-card sales/revenue/product/funnel metrics
Only list active collections (activation_status=1) — hide archived/deleted from the conversions list
Pixel discovery via field-expansion (1 call, was 100+ → timeout/spinner) + searchable/scrollable pixel picker
Pixel discovery used invalid 'owned_data_sets' edge (returns nothing → 'No Pixel found') — use adspixels across businesses + ad accounts
ConnectMeta returns HX-Redirect for HTMX requests so the cross-origin OAuth redirect always works
Opt Connect-with-Meta link out of HTMX boost (hx-boost=false) — HTMX AJAX couldn't follow the cross-origin 302 to facebook.com (network error)
Meta one-click OAuth (Connect with Meta) — reuses platform Meta app, exchanges long-lived token, pixel picker, auto-writes+tests CAPI config; paste flow kept as manual fallback
Signal-quality dashboard on /conversions — Meta CAPI live / Google live / 30d orders + first-party match-signal fill rates (fbc/fbp/gclid/email/phone)
Add step-by-step 'how to get' guides on /conversions — Meta Pixel ID + System-User CAPI token + Google Conversion ID/Label, with inline pointers by the paste fields
Load utm-capture.js on checkout (captures gclid) + link decoration (cross-domain click-id passthrough) + wire subscription & custom-payment order inserts for first-party capture
Google Ads conversion + Enhanced Conversions — per-collection config on /conversions, self-loading gtag partial on thank-you (value/currency/txn_id + hashed email/phone)
Don't redeclare inherited public $db/$login_id (PHP visibility fatal)
Self-serve Meta CAPI setup — paste Pixel ID + System-User token per collection, live 'Send test event' validator + token health, sidebar entry (no OAuth/App-Review needed)
First-party attribution on orders — add fbclid/fbc/fbp/gclid/wbraid/gbraid to wl_payment_log, capture at checkout (Stripe+PhonePe), feed async CAPI (fix broken fbclid select, use stored fbc/fbp), hash country
Refresh also pulls client/shared ad accounts (2-pass owned-then-client, dedup by meta_ad_account_id so owned accounts aren't reassigned)
Refresh ad accounts button on funnel-mapping — rediscovers Meta businesses + re-pulls owned ad accounts on demand (fixes stale one-time sync)
Immersive lesson player — dedicated classroom shell (no portal chrome), fixed top bar w/ progress + prev-next, collapsible animated curriculum rail, sticky header, keyboard nav
Courses sidebar submenu (All Courses/Students/Analytics) + global Students datagrid with filter, pagination, CSV/print export
Course-list KPI cards match analytics style (icon badges, consistent typography)
Analytics dashboard — KPIs, enrolments-over-time, per-course performance, completion funnel, active/idle split
Lesson player — per-module progress in sidebar + auto-scroll to current lesson
Course overview — per-module progress bars + 'Up next' resume marker
World-class My Courses — resume hero, progress stats, elevated course cards
Surface validation block on Complete Payment (India-GST State field) instead of silent no-op
Welcome/reset emails greet 'Hi there,' instead of 'Hi ,' when member has no name
Auto-enroll order-bump buyers into bonus courses (map wl_payment_log.offer_bumps token -> bump product -> course)
Admin-initiated password reset — regenerate + email student password from the students screen
Admin students dashboard — fix var mismatch (showed 'no students'), add spend/purchase/last-login/login-email columns + summary cards + resend-login action
Durable product->course map so UI product edits (which wipe custom_details.course_id) can't silently break enrollment
App:send-course-welcome command — send/resend collection Welcome (login) email to specific members
Show copyable Bunny webhook URL on the connected CDN card (guarded, can't throw)
Trigger connected-list load on cdn_providers page — bundle whitelist (integrationModuleInit) omits cdn_providers so it never loaded
Cdn_providers connected-list template mirrors working whmcs template exactly (custom webhook block was throwing → silent catch hid the card)
Bunny.net Stream CDN provider — connect-form fields + connected-list card + webhook receiver (BunnyWebhook) + route
Course buyers can log in — add member-record password fallback (sha1 purchase pw + bcrypt register pw) when no wl_users_login row
Edge VPS IP 74.48.200.224 -> 69.12.71.70
Offer-bumps '+ Add' second row rendered 'undefined'
June 2026
GST preview computes on base + selected order bumps
Wire date-range picker on country/region stats + restore Region Stats to sidebar
Suppress alert email for router-rejected probes (disallowed chars / 400); keep DB log
Remove leftover print_R debug from live IPN handler
Pass funnel_id from Stripe/PhonePe/Lemonsqueezy sale dispatch
Persist customer_country + funnel_id in forSale payload
Restrict invoice email to India/INR billing profiles
May 2026
Restore Affiliate Partners link under Settings
Cap dispatcher retries + terminal Failed state + resend reset
Null-coalesce every connection_json access on DataTable
Show real FROM name + FROM email on Transactional Emails DataTable
Backfill orphan AWS SES rows to new catalogue id
Register AWS SES under email_smtp catalogue
PropelCapiHandler — Meta CAPI Purchase for every gateway (#708)
Surface auth-class errors via wl_server_error_log + SES email (#707)
Gate sale.completed dispatch in 5 gateways (defense-in-depth) (#703)
Diagnostic codes for switch_team + surfaced client error messages (#701)
Clone_payment_gateway response refactor + Select2 modal fix (#700)
Route CSV/TXT exports through CI4 Response, not raw header() + exit (#698)
Query custom_product_id from product table, not collection (#696)
Skip probe-path email notifications (#695)
Build verify-upi URL at runtime instead of broken PHP-tag (#697)
Gate EventBus sale.completed dispatch on payload.state == COMPLETED (#702)
EventBus dispatch now populates login_id / amount / currency
Honor is_enabled toggle at every queue + fire site
/integration/cat/accounting was a forever-loading skeleton
NPE in pay_checkout when Checkout_n is instantiated via `new`
D6 profile-level tax-inclusive default
B5c extend price+symbol fallback to all 4 remaining checkout templates
Add Wim accounting integration
D5 polish — drafts list JS in right section, simulator copy, bucket-acl fallback
D4 seller dashboard for invoice drafts
D3 PaymentSuccessInvoiceHook routes via profile.auto_send
D2 InvoiceDocumentService::createDraft + confirmDraft + voidDraft
D1 schema for draft-first manual-issue workflow
T5 engine/country alignment validation (BPT-010)
Remove broken require_once(FCPATH . 'vendor/autoload.php')
B5d persist buyer state/country/GSTIN on form submit
T2 EU OSS €10k threshold opt-in (BPT-002)
T3 EU buyer-country fallback to seller country (BPT-003)
T4 rate-table hygiene (BPT-004 + BPT-005)
T1 fail-closed nexus default for US sales tax (BPT-001)
Phase 2 — US sales-tax + EU VAT engines
IndiaGstEngine CGST/SGST must be equal per GST law
P2-S Stripe subscription cycle invoices — verified + tested
B5c live tax preview — use product_price fallback in pay.php / pay_subscriptions.php
F21 24-hour TTL on profile-image prefetch cache (BP-026)
B5c close rollout gaps — pay.php + subscription path
F20 pre-fetch Wasabi-hosted logos for mPDF (BP-024)
F19 extract shared invoice-hook context builder (BP-023 + Q9)
F18 resend override no longer corrupts audit trail (BP-022)
F17 billing:resend-invoice support command (BP-021)
F16 cross-check legacy currency_id default (BP-019)
F15 sanitize PDF-render exception logging (BP-018)
F14 scope renewal-fallback by gateway connection (BP-017)
F13 per-object private ACL + bucket-check command (BP-016)
F12 hook Razorpay's transaction_paid_data_query (BP-014)
F11 transaction_id idempotency for renewals (BP-015)
F10 subscription renewal buyer-fields fallback (BP-013)
F9 migrate legacy pdf_url URLs to S3 keys (BP-012)
F8 currency check fails closed when payment currency empty (BP-011)
F7 strip brackets from IPv6 hosts in SSRF guard (BP-005-r2)
F6 render-on-retry so mailer always has attachment (BP-002-r2)
Clone-product 404 — actually ship the form.js bundle this time
F5 tax preview honors per-product overrides (Gemini BP-007)
F4 SSRF URL guard + currency-mismatch fail-closed (Gemini BP-005 + BP-006)
F3 private PDF storage + local-stage cleanup (Gemini BP-002 + BP-008)
Guard B5 billing-profile lookup behind schema check
F2 race-safe counter init + dedup (Gemini BP-003 + BP-004)
F1 anchor tax-inclusive math to gross (Gemini BP-001)
B5b extend GST buyer-fields partial to remaining checkouts
B6 central invoice hook covering all gateways
B5 checkout buyer fields + GST preview (checkout_first)
B4 invoice PDF rendering + email delivery
B3 PhonePe payment-success invoice-issuance hook
B2 gateway-connection tagging to billing profiles
B1 seller-facing UI for billing profiles
Phase 1 backend foundation for tax-compliant invoicing
ElasticEmail v4 + SparkPost + Mailgun + Alibaba DirectMail
Promo placeholder for unpublished sites
MailerLite v2 + Sendfox + ActiveCampaign v3 connectors
EmailOctopus v2 connector
SES Config Set tag only applied for us-east-1 sends
Wl_users_login column is email_id not email
Skip bulk/unsubscribe headers for operational recipients
Show actual price instead of empty currency symbol
JVZIPN v1 path now passes product_id to payment.sale.completed
Render inline where script tag is placed, not bottom-right
Purchase-gate operators in condition step dropdown
Has_purchased/has_not_purchased operators + multi-id trigger filter
Pass product_id in payment.sale.completed payload
Prefer DB-stored SMTP creds over file-based
Suppression check, jitter, dashboards, SES setup runbook
SNS webhook + email_events/suppressions tables
List-Unsubscribe header + public unsubscribe endpoint + footer
Empty collections dropdown + missing CKEditor + add 'Specific Integration' SMTP source
Lockfile + GTW pre-register CLI
Atomic claim + idempotent enqueue + Aws_email FCPATH
Steps now render on first navigation, not just hard refresh
Smart slug auto-update + soft prompt on rename
Token_status badge actually reflects Meta probe + Refresh button
'Back to Integration' opened the marketing site, not the hub
Editing project no longer demands re-selecting Project Type
Redesign hub — sidebar categories + searchable platform grid
Use Cloudflare DoH for verify so local DNS cache stops lying
'Added' column was empty — getDomains didn't select created_datetime
Per-row 'Use Cloudflare' button for existing pending domains
Modal scrolls internally — verify buttons no longer hidden
Prefetch live CSRF token before every POST
SanitizeDomain regex used # as delimiter AND literal char
Belt-and-suspenders CSRF on every POST + clearer Save errors
Auto-insert DNS records via the connected token (round 3)
Typing '.' works + 'Save failed.' on Add Domain
Visible steps + WYSIWYG body + per-step pause + drag reorder
Connect card + token modal on integrations/domains (round 2)
Repair pre-existing stub row instead of skipping
Scaffold per-user CF API-token integration (round 1 of 2)
Kill the // in URLs on Projects + Integrations pages
Attach CSRF header so /ai/chat stops returning 'Network error'
Blank Canvas as a tile in the template picker, not an auto-redirect
Blank Canvas opens the editor directly + drop // in redirect URL
Blank Canvas no longer requires Project Type + stops 404 on save
Emit contact.created at every wl_contacts insertion site
QuickStats hits /quickstats/list (data endpoint) not /quickstats (doc page)
Correct auth header format — DEV-<dev>:<clerk_verbatim>
Add ClickBank affiliate-network gateway integration
Add Resend.com as a transactional email provider
Per-webhook ON/OFF toggle in collection integrations
Countries table is (country_code, value), not (iso2, country_name)
Add Network row to cancel + dispute templates
Show real Location + add Network row in admin sale email
Tier 1 revamp — sticky shell + KPI strip + view tabs + floating bulk
Set subscribed_at + updated_at + lead_source on inserts
Submissions tab actually populates KPIs + table rows now
Required-asterisk markers + actually-useful submit errors
Default Kartivo thanks page when no custom redirect
Phase 6 — public hosted preview + per-field show/hide config
Phase 4 — builder widget can pick a Kartivo form
Phase 3 — admin UI on /forms (tabs + create/edit + embed)
Allow form_table_id + collection_id through FormSubmissionModel
Phase 2 — FormController extensions for centralised forms
Wl_public_forms.collection_id (not target_collection_id)
Phase 1 migration — centralise on wl_form_json
Public lead-capture forms - drop Sendy as the lead intake
Wl_collection -> wl_products_collections (canonical table)
Inbound webhook — Sendy subscription -> Kartivo contact -> GTW
Spark command for cron-driven queue draining
GTW webinar binding + per-attendee join_url + collection backfill
Show Dashboard/Logout in marketing nav when user is logged in
Correct insert to wl_global_integrations_users_data
Hx-boost=false on Connect button so HTMX doesn't intercept
Remove private $db redeclaration that broke visibility contract
Escape cron asterisk-slash in docblock — was terminating the comment and triggering ParseError on spark list
UI panel + sync worker + connection-status endpoint
Backend scaffold for sheet -> contacts integration
Spark meta-leads:health monitor for stuck webhooks
Drop page_id from Graph fields list — not on the Lead object
Wire Facebook Login on /login + /signup
Show collection id in picker, sort ascending by id
Stop computing scheduled_at in PHP — TZ mismatch stranded rows
Dedicated /webinar/list-collections endpoint for the picker
TestConnection shows actual user identity, not 'Zoom user'
Use /v2 date-range /webinars + vendor Accept header
TestConnection probe + replace mautic-placeholder logo
Correct redirect URL + auto-backfill organizer_key
Add missing getLoginId/getLoginType helpers
Re-ship read-only UI panel inside Ads Integration
Replace catastrophic-backtracking HTMX-extract regex
Allow hyphens in page_view path-traversal regex
Minimal read-only UI panel inside Ads Integration
Defensive try/catch around new viewAds queries
UI panel under Integrations → Ads Integration
Inbound leads → wl_contacts → webinar drip
Phase B — GoToWebinar (OAuth code flow)
Phase A — Zoom integration with drip-register queue
Gateway::index auto-fills customer_country + payment_method
Add Pass 2 — link pcmp.country_id to surface country on /transaction
Backfill country + payment_method from existing transaction_json
Initiated postback (v2) + conversion backfill
Move help panel to the actually-rendered template
Add help panel to actual underscore.js card template
Receive S2S postbacks — Purchase + Refund, per-tenant
Move CSV export to async CLI worker so it never times out
Stream CSV export so /transaction Export CSV stops timing out
Canvas content no longer clipped by top toolbar + visible empty containers
Right panel auto-closes on deselect (FlexiFunnels parity)
Plug P38 quickActions ReferenceError + undo-race in frame repair
Repair pages with zero frames + clearer insert error path
Kill legacy panel collision + 5 reported bugs
Live HTML preview in EVERY block card, not just hover
Derive block names + live preview iframe + backfill migration
Built-in Blocks now fetches /builder/blocks (real blocks, not templates)
Derive Design Block categories from data, not hard-coded spec list
Remove dual toolbar + plug drag-payload text leak
P30 — devicelet preview pin
P29 — last-action pill with one-click undo
P28 — multi-select with Shift+click + bulk Duplicate/Delete
P27 — component-tree breadcrumb pinned to canvas bottom
P26 — block content variants (8 new entries)
P25 — recently-used quick-pick strip
Route in-builder AI assistant through Nexus AI gateway
P24 — drag-cancel shake + smooth fade-back
P23 — Layers panel quick-jump on selection + L shortcut
P22 — highlight matching characters in search results
P21 — right-click context menu on canvas elements
P20 — autosave on tab/window blur
P19 — wire ? to toggle shortcuts overlay
P18 — component descriptions in hover tooltip
P17 — hover preview tooltip for cards + thumbnails
P16 — seed migration for Design Block starter library
P15 — floating save-state badge
P14 — styled drag preview chip
P13 — empty-canvas drop slot
P12 — inline toolbar size adapter for small components
P11 — keyboard shortcuts for quick insert + panel toggles
P10 — search across both tabs
P9 — drop-position precision (before/after target)
P8 — drag-from-card to canvas with positional drop
P7 — Design Blocks one-shot fetch + category counts + smarter empty states
P6 — quick-add rail icons (Section/2-Cols/Headline/Sub/Image/Video)
P5 — orange inline element toolbar with Gear shortcut
P4 — default canvas seed + bottom-rail Settings/Preview + floating canvas Save/Gear
P3 — Design Blocks tab (Built-in / My Blocks + 19 categories)
P2 — Components tab with 42 colour-coded cards
P1 — slide-in Blocks & Components panel shell
Scope flex-column to stack/composite types only — selects + colors render correctly
Scope flex-column override + constrain color picker + remove !important blocking animation
Vertically stack composite inputs + animation preview via keyframes
Kill horizontal scroll in Text + Background tabs
Hide-on-mobile/tablet/desktop checkboxes work again
Entrance animation no longer leaves element invisible
Colour / style changes now visibly take effect on canvas
Class&State accordion + Advanced UI polish + Animation Preview works
Compact Class & State box at top of right panel
Advanced + Animate tabs now render content
Production-ready right panel — top tabs, custom shadow, text/bg extras, animation panel
Kv-slider — bypass StyleManager type, inject directly into sectors
More sliders (Text + FX) + clean up FX sector
Spacing sliders — split type per side + DOM-injection fallback
Right-panel — DOM-toggle sectors + cache-bust JS modules
Right-panel sectors now all render + working spacing sliders
Right-panel rework — vertical tabs + 7 sectors + drag sliders
Right panel + AI/Pages/Assets toolbar buttons
Right-panel — capture-phase fallback so click always opens it
Right-panel regression — element-lock was over-mutating toolbars
Finish triage of QA-reported broken admin pages
Batch SPA-init + load-order fixes for 5 admin pages
/wladmin/users no-data — DOMContentLoaded never re-fires under HTMX
Delete actually deletes — switch to deleted_at column
Minor QA items — checkbox hitbox + Total counter refresh
Inline edit/delete actions + emoji + test-email validation
Extend Round 1 F-003 fix to Form/Publish/ZipImport
QA report 2026-05-04 — 10 admin-panel bugs across critical/high/medium/low
Gemini Round 1 — fix all Critical + High + Medium findings
Shimmer skeleton on slow stat cards instead of "..." / 0
Unwedge tables that didn't render under HTMX navigation
Split-test page no longer wedges sidebar navigation
Popup template gallery — 10 ready-made overlays
Per-element lock toolbar button
Per-device show/hide trait (mobile/tablet/desktop)
D3+D4 — brand-color StyleManager dropdowns + kv-* utility classes
Phase C — AI Assistant chat panel + Anthropic proxy
Phase E — Symbols v1 (linkage tagging + visual marker)
Phase J — multi-page panel in left rail
Phase D — Brand Kit / Global Design Tokens
Phase I — animation library + traits + runtime
Phase H — Assets panel surfaces GrapesJS AssetManager
Phase G — rich text editor toolbar (font/size/align/lists/colors/clear)
Phase F — granular form-element blocks (12 entries)
Phase B — descriptive layer naming
Phase A7 — section quick-actions strip + Phase A complete
Phase A6 — device-mode indicator above Style tabs
Phase A3 — gradient builder
Phase A4 — shadow visual polish (CSS-only)
Phase A5 — per-corner border-radius box
Phase A2 — visual flex helper (icon-button rows)
Phase A1 — visual padding/margin box
Explicit "Act as User" impersonation, kill implicit admin bypass
Right panel display:block, not flex — Style/Trait Manager hidden after selection
Tier 3 funnel-staple blocks — sticky CTA, exit-intent, scarcity, Wistia
Default Trait Manager fields for common element types
USER badge on user-saved blocks
Cache block panel fetches with TTL + invalidation
Explicit search-within-category UI on blocks panel
Live tab counts on blocks panel (My/Favs/Recent)
Route edge VPS via CF when cPanel firewall blocks direct IP
Skeleton loader for block-card iframe previews
Add countdown_reverse block + fix countdown.js dispatch
Always-visible right panel with proper empty state
Expose Selector Manager — class + state editing
Gemini Round 3 — H1 + K1 + B2 + I3 + F1/F3 + J2 fixpack
Prefer live screenshot over template-default thumbnail
Edit-page logo links to /projects, not /dashboard
Write to public/uploads on cPanel layout (FCPATH != public dir)
Full-URL display + auto-screenshot fallback + index-page card thumbnail
Edit-page logo links to dashboard, not marketing site
Published! URL ignores slug + is_index — always returned root
SaveCanvas hangs on custom-domain projects — drop dead funnel upload
Per-page slug + is_index flag (PENDENCY Q29)
Gemini Round 2 — F-201..F-207 hardening
Preview button opens /p/{id} for published pages, /preview/{id} only when unpublished
Gemini Round 1 — Q1 + Q2 + Q5 + Q24 hardening
Map root / to /_e/__root__ to avoid trailing-slash 301
Work around cPanel root-path 301 via /_e Caddy rewrite
Broaden trusted-edge IP detection (CF, XFF, etc.) + log on miss
Smuggle customer host via X-Original-Host header
Rewrite add-domain modal — sanitization, dedup, two-step flow
Verify endpoint + UI now point at edge VPS, not cPanel
Drop interval/burst from on_demand_tls (deprecated in v2.11)
Adapt for two-server topology — Caddy on dedicated edge VPS
Finish module — verifyDNS rewrite, Live URL banner, audit doc
Phase 1 — Caddy + LE host-routing, kill funnel-CDN upload
/integration/cat/domains showed empty list despite connected domains
Topbar logo links back to the project's pages, not site home
Port Wasabi user-scope guard to Backblaze + Amazon_store bucket_details
Post-Gemini quick wins — destination_path, Wasabi user scope, unpublish endpoint
Bulletproof entry trace + try/catch + per-user trace files
Drop UNIQUE (user_id, slug) on publishpages — root cause of /p/{id} 404
Row-not-written silent failure — disable strict trans, use Model return value
Cache-bust import-export.js so page_id-fix actually ships
Write the right page — JS now sends page_id, server returns live URL
/p/{id} 404 after republish — path stored as basename only
Surface actionable error when php-zip extension is missing
Template picker thumbnails, applied-template thumbnail, /p/{id} live route
April 2026
Close 2 pixel-coverage gaps — custom payment links + legacy subs
Hashed PII advanced matching + contents/external_id on pixel + CAPI
Callback signature uses values-only, not key=value
Silent INSERT failure + friendly retry on callback
Resolve custom_product_id → wpcu.id in initiateTransaction
Wl_global_currency (singular) — was wl_global_currencies
Paytm:attach --collection-id for exact-match disambiguation
Paytm:attach --auto picks cheapest Stripe-free product
Add paytm:attach Spark command for one-shot gateway attachment
Phase 2 — bind to wl_payment_log + redirect via pay/thanks + refund helper + JS dispatch
Google Sheets → Kartivo Contacts sync (Apps Script stop-gap)
Hx-boost=false on download links — HTMX was inlining CSV bytes
Add Contacts API v1 (CRUD + tags + bulk) + extend OpenAPI docs
Wire footer legal links to kartivo.com
Inject <base href> into folder-based pages so relative URLs resolve
Host folder-based pages at public/funnel/{slug}/ so URL=disk
Add fe / oto1 / partners landing pages (folder-based)
UX overhaul — country/state cascade, dial-code prefix, empty state, polish
Systematic-debug audit pass — 8 fixes
Batch 4 — filters, CSV export, bulk-status actions
Batch 3 — Email Campaigns audience integration
Batch 2 — hardened CSV import
Batch 1 — schema + CRUD foundation
Batch 9 final — 17 templates to reach 100/100
Batch 8 — 12 templates (membership, school, charity, church, insurance, lawyer, daycare, dealership, music, podcast, SaaS trial, subscription box)
Batch 7 — 12 templates (realtor, photographer, fitness, med spa, law, accounting, pet, career, ebook v2, high-ticket, healthtech)
Batch 6 — 12 templates (B2B, branding, agency, portfolio, marketplace, service business niches)
Batch 5 — 12 depth-add templates across 8 under-served categories
Batch 4 — 12 templates across email-optin, local-business, digital-products, consulting + depth-adds
FaqAccordion el() arg-position — id-attr was in text slot
El() arg-position bug — id-attr must be 6th arg, not 3rd
Batch 2 — 8 templates across 8 new categories + shared blocks library
Add nav + redesign Sales hero (light, split, with floating proof card) — pilot iteration after operator review
Wrap components in GrapesJS project shape
El() wrong arg position in salesPricing — attrs in 3rd slot is text, must be 6th
Pilot batch 1 — 3 hand-designed templates + capture pipeline
Wrap components in GrapesJS project shape
El() wrong arg position in salesPricing — attrs in 3rd slot is text, must be 6th
Pilot batch 1 — 3 hand-designed templates + capture pipeline
Hx-boost=false on all edit-project links
Add UTM Report link to admin profile dropdown
URL double-slash + revamp Stripe payment block
Strip icons from stat-card labels per operator request
One-time mode — target datetime + IANA timezone
Wrap init in jQuery ready + use fas icons
Full UI/UX revamp + delivery status + per-row View modal
Category filter SQL — CI4 where() doesn't bind arrays
Client-side CSV download via Blob
Revert positional cross_sale call — _n variant is array-style
Revert broken onerror escaping for logo fallback
Inline credentials + auto-download + bulk-resend (A + C)
Hardening, CSV-only validation, real success panel, email correctness
Inject db+forge via reflection (Migration has no setter)
One-shot templates:run-phase3 command (CI4 migrate didn't auto-discover the file)
Batch 3 — 12 templates across 10 untouched categories + 2 depth-adds
UTM Attribution Report (Phase 2) — admin dashboard + CSV
Per-transaction UTM + click-id capture (Phase 1)
Company dropdown opens on hover (matches mega-panel pattern)
Regroup into 5 megamenus + raise burger threshold to 1024px
IsVerifiedPurchaser SQL — wrong column blew up logged-in product views
Wire /courses + /marketplace into header and footer
Pass activeCategory slug to view (was undefined)
Correct seller-badge sales query column names
Unbreak /affiliate-marketplace + add /marketplace alias
Hero text visibility + breadcrumb overlap
Public marketing /courses index + Udemy-style detail page
Hero text legibility on bright banners
Editorial typography revamp for lesson body
Full revamp — sticky header, brand-aligned typography, polished prev/next
Load global helper in marketing layout so kartivo_gtm_id() resolves
Unify marketing GTM + always-2-pixels on checkout
Redesigned hero + progress + accordion
Delete AJAX must POST to /courses/admin/delete/{id}
Card-based grid replaces table — full 16:9 banners, search + status filter
List view reads wl_courses.thumbnail (was thumbnail_url)
Cross-panel nav — Admin Panel + Apps Switcher Customer Portal
Site-wide GTM container ID — admin-managed, replaces hardcoded GTM-TB77SJ94
Add kill-switch for per-webhook email log notifications
Unbreak paymentLog() helper + crosell_member cron idempotency
Unbreak ad-set creation + duration column + expandable errors
Full course payload (9 modules, 45 lessons, 9 quizzes)
Index swapped to DataGrid + real per-account stats
/propel/ads/{id} 500 + render full creative payload
"Today" sync no longer returns zeros (time_increment=all_days)
Full payload
Full payload
Full payload
Full payload
Full payload
Full payload
Full payload 83 steps
Full payload 83 steps
Expand MetaClient retry to 502/504/520+ + honor Retry-After
Gender + age cohort breakdowns into audit
Placement + device breakdown — new section + 2 P2 findings
AI Platforms course live (course_id=5); catalog updated
Full course payload, 83 steps
Full course payload, 84 steps
LIVE on prod — course_id=3
Sections 6-9 + final publish (full payload, 85 steps)
Sections 4-5 (Production at Scale, Campaign Architecture)
Sections 1-3 (Landscape, Measurement, Creative Strategy)
Meta Ads Mastery 2026 — Phase 1 + 2
Backfill first course + scaffolder
Preserve course meta on final publish step
Direct-DB course payload runner (prod-side)
Authoring payload runner
Delete 500 when wl_course_lesson_attachments missing
Tolerate missing event_subscriptions column on older envs
Render subscription template on initial /pay/{id} load
Store Stripe Price ID (not Product ID) for subscriptions
Remaining producers switched to multi-webhook helper
Collection::store_member SALE now fans out to all webhooks
UI opt-in toggles + Paykickstart SUBSCRIPTION_CANCELLED
New event types with per-webhook opt-in (no breakage)
Fan out cross-sell events to ALL target-collection webhooks
Webhooks tab DataTable never loaded
Server errors during admin add-plans-and-save
Fan out payment gateway IPN events to all webhooks
Fan out to ALL collection webhooks, not just the first
Plan checkboxes never reached server on save
Add missing endif closing isSuccess conditional
Stop #spinner button showing permanently on pay.php
Update stale SRI hash for jquery-validate → unblocks Stripe Elements
Php syntax gate (pre-commit + CI) + restore checkout pixel coverage
Close unclosed PHP brace in pay.php (unbreaks Stripe pay_now)
Accept legacy VCS key (warn, don't throw) to unblock tenant decryption
Record bump price on its own row instead of 0
Don't throw during FactoriesCache restoration
Resend Welcome button on each member row
Make SES configuration-set env-driven + skip header when blank
Rebuild the 8 default templates + collection-token substitution
Testimonial modal — proper cards for each entry + clean Add More
Favicon — correct path + double-slash + add full size set
Dismiss open Bootstrap modals before HTMX main.content swap
Add hard-delete button to collection members list
Boosted navigation + browser-back rehydration for legacy layout
Flip default click target to manage, rename action to Performance
Edit-collection modal 500 — hardened Wasabi upload + save path
Check public/ first in DocsController (follow-up to previous commit)
Serve public/openapi.yaml, not the stale root-level duplicate
Workflows — use getJSON() not getRawInputVar() for body parsing
Workflows create() accepts JSON bodies, not just form POST
Add Email Workflows API for programmatic drip/lifecycle sequences
Remove time() from Purchase event_id so dedup actually works
Remove time() from Purchase event_id so dedup actually works
Remove time() from Purchase event_id so dedup actually works
Restore missing [$loginId] bind param on total-customers query
Disable SSE /live-events/stream — was saturating PHP-FPM
Disable SSE /live-events/stream — was saturating PHP-FPM
Disable SSE /live-events/stream — was saturating PHP-FPM
Short-circuit vite_has_manifest to force unhashed JS serving
Short-circuit vite_has_manifest to force unhashed JS serving
Short-circuit vite_has_manifest to force unhashed JS serving
Remove stale Vite manifest — built JS predates current fixes
Remove stale Vite manifest — built JS predates current fixes
Remove stale Vite manifest — built JS predates current fixes
Stabilise prod after 524 timeouts — dashboard throttling, HTMX 524 handling, vite manifest hardening
Stabilise prod after 524 timeouts — dashboard throttling, HTMX 524 handling, vite manifest hardening
Stabilise prod after 524 timeouts — dashboard throttling, HTMX 524 handling, vite manifest hardening
Refresh expired ads access token in RunCronMetaAd
Refresh expired ads access token in RunCronMetaAd
Refresh expired ads access token in RunCronMetaAd
Correct FCPATH for launch pages — public/launch/ not launch/
Add launch funnel pages at /funnel/{slug} — static, no auth
Marketing site nav + footer — hardcode HD logos instead of DB settings (old logo path)
Login page — add visible logo above form, cache-bust auth.css, fix all auth pages
Login/signup pages now follow Kartivo brand guidelines
Replace ALL old logos/favicons with HD brand assets sitewide
Cache-bust on CSS background:url() for profile pics + app icons
Cache-bust on CSS background:url() for profile pics + app icons
Add cache-bust version to all brand asset URLs — forces browser to load updated HD images
Regenerate all social media assets using HD logos (2400px source instead of low-res originals)
Brand page — remove old low-res logos, keep only HD pack, update header to HD logo
Add Canva design templates to brand page — 24 social media designs (6 types x 4 options)
HD logo pack — SVG source + PNG at 2400px, favicons at all sizes
Rich social media covers — dashboard mockup, stats, feature pills, CTAs (not just logo on gradient)
Add collection gateway linking endpoints
Add collection gateway linking endpoints
Add collection gateway linking endpoints
Generate 19 social media assets + add to brand page
Brand page — remove Memdash logo-sm.png and logo-light.png, show only Kartivo assets
Brand page — add Kartivo logo to header, fix logo grid with all variants, remove Memdash fallbacks
Add /brand — public brand guidelines & design system reference page
Home hero text must be white — override global heading/paragraph color
Parallelize Meta Graph API calls in spend analysis (60s → 3-5s)
Add missing DB indexes for slow pages (Report Builder, Email Campaigns, Webhooks)
Update production-readiness test — fix false positive selectors, add resource error to ignore list
Phase 2E-4 — Alpine validatedForm component
Phase 2E-3 — HTMX dependent dropdown for collection payment gateways
Phase 2E-2 — auto-refresh KPIs every 30s + auto loading states
Phase 2E-1 — SSE live notifications for sales/refunds/disputes
Re-enable HTMX allowScriptTags — inline scripts must execute natively
Call ALL module inits on afterSwap — each has internal slug guard, no manual slug list needed
Dashboard1.js script tag had broken quotes — timestamp was outside src attribute
Page module scripts cannot be defer — inline view scripts depend on them
42/42 routes pass — zero JS errors
Stabilize — revert aggressive HTMX conversions, fix module init
HTMX server partials — dashboard KPIs + integration services
HTMX progress bar + global error toasts
Phase 2C continued — transaction page Alpine tabs + filters
X-collapse requires Alpine plugin — use x-transition instead
Phase 2C — jQuery→Alpine migration bridge + collection page
Load alpine-components.js BEFORE Alpine so stores exist on alpine:init
DataTable .every() not a function + let/const redeclaration in HTMX
Phase 2C — Alpine.js toast, confirm, formModal components
FCPATH points to project root not public/ — fix manifest path resolution
Phase 2A+2B — HTMX hx-boost + Alpine.js foundation
Terser build pipeline — 793KB → 436KB (45% reduction)
Member_purchase_plans JOIN pcud/gi must be LEFT JOIN for API-created records
CKEditor and Dropzone cannot be deferred — used synchronously after load
EmailReports login_id visibility must match BaseController (public)
Defer non-critical scripts + fix DataTable destroy
V2 router — bulletproof widget lifecycle + universal module init
Remove double script execution — jQuery .html() already runs inline scripts
Echo renderSection content + remove legacy sidebar handler
Implement SPA navigation — AJAX content swap without full reload
Auto-sync refunds from Stripe webhooks
Auto-sync refunds from Stripe webhooks
Auto-sync refunds from Stripe webhooks
Disable Dropzone autoDiscover globally in footer right after library loads
Disable Dropzone autoDiscover globally in footer right after library loads
Disable Dropzone autoDiscover globally in footer right after library loads
Add SweetAlert2 + disable Dropzone autoDiscover
Add SweetAlert2 + disable Dropzone autoDiscover
Add SweetAlert2 + disable Dropzone autoDiscover
Fix Stripe credential lookup + sync all accounts
Fix Stripe credential lookup + sync all accounts
Fix Stripe credential lookup + sync all accounts
Show API-created subscriptions/transactions in dashboard
Show API-created subscriptions/transactions in dashboard
Show API-created subscriptions/transactions in dashboard
Extra_data must be valid JSON (CHECK constraint), transaction_type is product/collection flag
Extra_data must be valid JSON (CHECK constraint), transaction_type is product/collection flag
Extra_data must be valid JSON (CHECK constraint), transaction_type is product/collection flag
Fix POST /transactions and POST /subscriptions DB schema mismatches
Fix POST /transactions and POST /subscriptions DB schema mismatches
Fix POST /transactions and POST /subscriptions DB schema mismatches
Add missing endpoints, fix integrations filter, add route aliases
Add missing endpoints, fix integrations filter, add route aliases
Add missing endpoints, fix integrations filter, add route aliases
Use correct gateway table (wl_product_collection_users_data) for include=gateways
Use correct gateway table (wl_product_collection_users_data) for include=gateways
Use correct gateway table (wl_product_collection_users_data) for include=gateways
Enrich products, collections, customers, integrations endpoints
Enrich products, collections, customers, integrations endpoints
Enrich products, collections, customers, integrations endpoints
Enrich transactions list with classification, currency, gateway details
Enrich transactions list with classification, currency, gateway details
Enrich transactions list with classification, currency, gateway details
Add /refunds, /disputes, /chargebacks API endpoints + fix rate limits
Add /refunds, /disputes, /chargebacks API endpoints + fix rate limits
Add /refunds, /disputes, /chargebacks API endpoints + fix rate limits
Load global helper in API BaseController — fixes my_random() undefined
Load global helper in API BaseController — fixes my_random() undefined
Load global helper in API BaseController — fixes my_random() undefined
Correct Flaxxa WAPI endpoint names and payload format
Flaxxa WAPI, SMTP providers, auto-responder connectors, communication category
Sync 43 missing migrations from main to staging
Customer-details URL rename + resolveEmailFromMd5Hash + double-slash fix
Remove double slash in customer-details search URL
Update search bar + Filters/Dashboard to use /customer-details/ URL
Rename cutomer-details → customer-details URL (keep legacy route)
Add missing resolveEmailFromMd5Hash() to Dashboard controller
Add missing routes for customer details DataTable endpoints
Add missing routes for customer details DataTable endpoints
Customer details DataTable — MD5 hash resolution, SQL injection, SPA slug
Customer details DataTable — MD5 hash resolution, SQL injection, SPA slug
Full audit + 18 fixes on Email Campaigns module
API v1 audit — security, CORS, Stripe cancel, rate limit headers
Email Campaigns module, Teams simplification, API v1 expansion
Add Products CRUD to API v1
Add Products CRUD to API v1
Detect wl_team_member users at login — set team session vars
Detect wl_team_member users at login — set team session vars
Detect wl_team_member users at login — set team session vars
Restrict access_export to main account only — team users cannot export
Restrict access_export to main account only — team users cannot export
Restrict access_export to main account only — team users cannot export
Add access_export to pages_premissionCheck — export was blocked for all users
Add access_export to pages_premissionCheck — export was blocked for all users
Add access_export to pages_premissionCheck — export was blocked for all users
CustomPaymentLink — remove broken constructor, fix validator, add currency_id to clone
CustomPaymentLink — remove broken constructor, fix validator, add currency_id to clone
CustomPaymentLink — remove broken constructor, fix validator, add currency_id to clone
Meta CAPI server-side Purchase events + client-side pixel tracking
Meta CAPI server-side Purchase events + client-side pixel tracking
Meta CAPI + client-side pixel tracking (cherry-picked to main)
Meta CAPI server-side Purchase events + client-side pixel tracking
March 2026
Expand TemplateCategoryModel with slugs, sort_order, and published counts
SharedTemplateModel with lifecycle, scopes, and consumer compat query
Create shared_templates table + extend template_categories
Admin dashboard view — categories, featured, reviews, trending tabs
MarketplaceAdmin controller — categories, featured, reviews, trending
Add routes + admin sidebar for marketplace admin
Update marketplace + product views — filters, featured, reviews, badges
Enhance PublicAffiliate — categories, filters, reviews, badges, trending
Review section partial — rating summary, form, list
Filters sidebar partial — categories, price, rating, sort
Enhanced product card partial with ratings, badges, categories
Add marketplace event constants to EventCatalog
MarketplaceTrendingModel — trending score calculation
MarketplaceReviewModel — upsert, denorm, moderation
MarketplaceCategoryModel — tree, sync, counts
Seed 5 parent + 19 subcategories
Migration — 4 new tables + product columns for categories, reviews, trending
Add drag handle indicator on component hover
Add quick actions floating toolbar (move/duplicate/delete)
Add selection breadcrumb bar showing component hierarchy
Add progressive block rendering with Show More button
Add block favorites with localStorage star toggle
Add NEW badge on recently added blocks in builder
Form configuration module for builder — traits, webhook config dialog
Update BlockModel and controllers for subcategories and template blocks
Seed 53 production-quality funnel templates across 16 categories
Seed 23 block variants for top 10 categories
Seed 29 new blocks — forms, countdown, video, FAQ, pricing, social proof, nav
Return project_id from creation, fix template picker to update existing project
Enhanced template browser — categories sidebar, full preview, append mode, project creation picker
Publish success dialog with QR, block panel lazy loading and collapse
Auto-save, section duplication, responsive breakpoint editing
Inject published page scripts conditionally at publish time
Published page scripts — forms, countdown, pricing toggle, stats counter
Form submission routes, dashboard view, sidebar link
SSRF protection, XSS fix, DataTable pagination, form_id validation in FormController
Create FormController with submit, dashboard, webhook, CSV export
Create FormSubmissionModel and FormWebhookModel
Add category cleanup to Phase 2 migration down()
Phase 2 migration — blocks columns, form_submissions, form_webhooks tables
Add CSRF token support to apiFetch in utils.js
Migrate indexpage + secondpage views to templates/app with new routes
Consolidate 90+ routes into RESTful groups with backward compat redirects
Create TemplateController with admin-only category creation
Create PageSettingsController with ownership checks on all endpoints
Create PublishController with file upload validation
Create BuilderController with ownership checks on load/save
Create ProjectController and PageController with security fixes
Extract FunnelServerService from monolith controller
Create 9 CI4 models with validation and user-scoped queries
Add sort_order to pages, drop CSV page_ids, add indexes
Want These Features?
Start your free trial and experience every feature listed above — no credit card required.