Modernizing a pricing intelligence SaaS inside a legacy Django product.
Priceindx is a B2B pricing intelligence platform: retailers use it to monitor competitors, analyze product performance, and manage automated price suggestions. From 2022 to 2026 I was its sole frontend developer, growing a modern Vue 3 layer inside the legacy Django product and building the workflows customers spend their day in.

- Role
- Sole frontend developer
- Timeframe
- 2022 - 2026
- Product
- B2B pricing intelligence SaaS
- Core stack
- Vue 3, Vite, Pinia, Quasar, Django
Overview
Priceindx serves e-commerce retailers and pricing teams. Customers monitor competitor offers across large product catalogs, review price suggestions, build reports and analytics, and make pricing decisions that feed directly into their shops.
The product began as a legacy Django application with real customers and working workflows, so a rewrite was never on the table. The modernization had to happen incrementally: new Vue surfaces mounted into existing Django pages, sharing one app foundation, while the backend and customer routines kept running untouched.
I owned that modern frontend layer end to end, from build architecture and shared bootstrap to the dense product surfaces themselves: dashboard reporting, the product table, scheduled reports, Dynamic Pricing, Visual Analytics, add-ons, onboarding, and customer documentation.
Modernization without a rewrite
The business needed a more interactive product, but the existing Django app was the business: real customers, real workflows, no appetite for a risky rewrite. So the modern layer had to grow inside it. Each major product surface became its own Vue app, compiled by a multi-entry Vite build into Django's static files and mounted into the server-rendered page it belongs to.
A shared bootstrap keeps those apps feeling like one product. Every entrypoint gets the same Pinia setup, Quasar config, axios conventions, localization, motion, and directives from a single app-creation utility. The integration seams got real attention: the active locale is read from the Django-rendered page so translations follow the account language, and every request carries a per-tab ID header so the backend can tell browser tabs apart.
- Nine Vite entrypoints, one per surface: dashboard, product table, scheduled reports, Dynamic Pricing, Visual Analytics, price change log, add-ons, configuration, and subscription.
- Manifest-based builds into Django's static pipeline rather than a separately deployed frontend.
- Shared Pinia stores, UI primitives, and translation files in English, French, and German across every surface.
Product table as the daily workspace
The product table is where pricing teams spend their day. One surface has to act as catalog browser, competitor comparison, export tool, and pricing console at once: global filters, table filters, quick filters, column preferences, saved views, date controls, and expandable competitor offer history all live on it.
The hard part was letting those dimensions combine without the table becoming fragile. A single Pinia store owns filter state, competitor columns, offer loading, saved views, and the export lifecycle, so every control stays consistent with the data on screen. Exports run in the background with status polling, retry, share, and download flows while the table stays usable.

- Saved views carrying filters, sorting, column visibility, and date presets.
- Expandable competitor offer history with its own date sequence controls.
- Price-suggestion actions applied directly from the table, including bulk apply.
Visual Analytics as a configurable workbench
Visual Analytics is the most ambitious surface: a charting workbench where users compose their own analysis instead of consuming fixed dashboard widgets. A chart combines up to three datasets with independent y-axes, drawn as line, bar, segmented bar, or boxplot.
Each dataset can be aggregated, segmented, and grouped across product, competitor, sales, and Dynamic Pricing dimensions, in static or over-time mode, with a ratio mode when exactly two datasets are selected. The chart and its data table share one configuration, so the same view supports visual exploration and spreadsheet-style analysis, with CSV and image export on top.
That configuration space made state and performance the real engineering problems. Heavy data transformation runs in a web worker to keep the UI responsive, and finished configurations can be saved as private views, shared with the team, or set as defaults.
Scheduled reports
Scheduled Reports turns the same data into recurring delivery for people who live in BI tools and spreadsheets. I built the report list and a multi-step create/edit wizard covering report type, setup, schedule, delivery method including SFTP, notification emails, column customization, and a confirmation overview, with timezone handling and validation throughout.

Dynamic Pricing
Dynamic Pricing is a rules engine, and the UI had to make its operational state legible rather than wrap it in generic CRUD screens. The interface covers group sets and groups, drag-based priority reordering, enable/disable states, settings, suggestion outcomes (raise, lower, keep, no price), and the price change log that shows what the engine actually did.
Where legacy Django pages still handled edge configuration, the new UI bridged into them instead of pretending they did not exist. Pragmatic seams like that were part of the migration strategy.

Onboarding, documentation, and support UX
A product this dense needs explanation built in. I wrote and maintained the customer documentation as a VitePress site in English, French, and German, and built the in-app layer connecting it to the product: contextual help targets driven by a custom directive, guided tours for new users, and eleven short walkthrough videos embedded where the table workflows get complicated.
The same care went into the code itself: Vitest suites around the table and analytics stores guard the state logic that every other surface depends on.
Outcome
By 2026 the modern layer covered every core customer workflow, and the incremental bet held: no rewrite, no parallel app, no cutover event. The product was modernized surface by surface while customers kept working in it the whole time.
What I value most about this work is the shape of the ownership. Architecture, state management, charting, UX, onboarding, documentation, and translations were one job, done inside a production system with real constraints, over three and a half years.