The production admin of a commercial visual site builder, running entirely in the browser on mocked data.
This is the real front end of Aurion Editor, a product I run in production, with the backend deleted and every data call served from committed fixtures. Not a lookalike, not a rebuild: the components in src/ are the ones the live product ships. Sign in and you are clicking through the actual admin console on fictional data.
The visual editor on the fictional Saltloft Coffee Roasters home page. More screens:
The UI is in Portuguese because production is in Portuguese: the product was built for Brazilian marketing operators. That is not a demo artifact, it is what my users see.
npm install
npm run devOpen http://localhost:5173. The bare origin redirects to /admin (same path as production) and lands on the login screen. Sign in with:
| User | Password |
|---|---|
demo@example.com |
demo |
You should see the dashboard ("Visão Geral") with seven fixture pages across two fictional tenants. The credentials are also printed in the badge at the bottom right of every screen. No environment variables, no backend, no network requests.
npm run build emits a static dist/ that works from any host with SPA fallback.
To regenerate the screenshots in docs/ (requires Playwright's Chromium, npx playwright install chromium on a first run):
npm run build
npm run screenshotExpected output: six saved docs/*.png lines followed by persistence check passed: edit survived reload. I ran all of these commands before committing this README.
Aurion Editor is a private commercial product: a React 19 + Tailwind v4 admin in front, a Node backend behind it, and a Cloudflare Workers + R2 publishing pipeline underneath. I cannot open-source it, but I wanted to show the real front-end work rather than a watered-down replica. So I took the production admin, removed the backend, and re-implemented exactly one module (the API client) over committed fixtures. The UI code does not know it is running in a demo, which is precisely the point: what you see is what my users see.
Setting aside the demo-only type shim (src/types/grapesjs-plugins.d.ts), src/ holds 46 files; 30 of them are byte identical to the production repo, and the rest carry the demo adaptations below.
| Piece | Status | Detail |
|---|---|---|
| Admin UI (all screens, components, layouts, routing) | Real | Production code copied over: pages, modals, drawers, sidebar, funnel nodes, toasts. |
API client (src/lib/api.ts) |
Swapped | Same exported surface as production (api.get/post/put/delete, ApiError, demoFetch); 60 route branches served from an in-browser store instead of the backend. |
Editor canvas (src/editor/GrapesEditor.tsx) |
Swapped, disclosed | Production embeds the licensed GrapesJS Studio SDK; a license key cannot ship in a public repo, so this build drives the open-source grapesjs engine behind the same component contract. |
| Data | Fictional | Two invented tenants (Saltloft Coffee Roasters, Peak & Pedal Cycling Supply), domains on the reserved .example TLD, every id and token a visible placeholder. |
| Authentication | Stubbed | One hardcoded credential pair checked client side. Production authenticates sessions against the backend. |
| Publishing pipeline | Simulated | Production renders pages to static HTML and deploys them to Cloudflare Workers + R2 per domain. Here, Publish flips a status flag and the domain screens simulate provision and deprovision transitions. |
| Translation | Simulated | Production calls Google Translate or OpenAI through stored provider credentials; the demo creates a copy of the page tagged with the target language. |
| Internal operator pages | Removed | The production sidebar has extra operator tools (AI assistant, page importer, A/B boosters, script generator, internal FAQ). Those are commercially sensitive and were removed outright: pages, routes and menu entries. |
Two mock behaviors worth knowing about, because the UI depends on them:
- Persistence. Every change is written to
localStorageand survives a reload; the screenshot harness asserts this. Clear site data (or runlocalStorage.clear()) to restore the fixtures. - Duplicate slugs return 409. Duplicating or translating a page into a slug that already exists throws an
ApiErrorwith status 409, matching the production API, so the real error handling in the modals gets exercised.
| Screen | Route | What it does |
|---|---|---|
| Login | /admin/login |
The production login form against the demo auth stub. |
| Dashboard | /admin |
Totals for pages, published and drafts, the recently edited list, quick create. |
| Páginas de Venda | /admin/paginas-de-venda |
Sales pages list: search by title, status and language filters, settings drawer, duplicate, translate, delete. |
| Visual editor | /admin/editor/:pageId |
Drag and drop canvas with blocks, style manager, layers, device switching and undo/redo; the "Código" modal edits raw page HTML; the publish flow walks category and domain steps, confetti included. |
| Funis de Venda | /admin/funis |
Funnel list with activate, pause, duplicate and delete. |
| Funnel editor | /admin/funis/:funnelId |
React Flow canvas: drag entry, page and redirect nodes from the palette, connect them, edit properties in the right panel. |
| Traduções | /admin/traducoes |
Translations grouped by language or by source page, fed by the fixture pages. |
| Healthcheck | /admin/healthcheck |
Per domain status checks, simulated deterministically (seeded by domain id and the current minute). |
| Imagens | /admin/recursos/imagens |
Image library; uploads are read with FileReader and stored as data URLs. |
| Pixels | /admin/recursos/pixels |
Tracking pixels; the fixtures ship Meta, GA4 and TikTok entries. |
| Domínios | /admin/recursos/dominios |
Domains with SSL and Worker status plus a simulated provision and deprovision cycle. |
| Scripts | /admin/recursos/scripts |
Custom scripts injected into pages. |
| Idiomas | /admin/recursos/idiomas |
Languages available for translation. |
| Provedores | /admin/integracoes/provedores |
Translation provider credentials, inert over fictional entries. |
| Cloudflare | /admin/integracoes/cloudflare |
Cloudflare accounts, inert over fictional entries. |
| Workers | /admin/integracoes/workers |
Cloudflare Workers per domain, inert over fictional entries. |
src/
App.tsx router (basename /admin), route-level code splitting
layouts/ admin shell (sidebar + outlet)
pages/ the 16 screens listed above
components/ modals, drawers, sidebar, funnel nodes, ui primitives
auth/ auth context + route guard (demo stub)
editor/ GrapesEditor.tsx, the canvas wrapper (the disclosed swap)
lib/
api.ts THE mock boundary: the demo's only swapped data module
fixtures.ts seed data for both fictional tenants
Data flow: a page calls api.get('/pages') exactly as in production. In this build, api.ts routes the call through an internal handle(method, path, body) dispatcher (60 route branches, count them with grep -c "method === '" src/lib/api.ts) against an in-memory store seeded from fixtures.ts, then persists the store to localStorage under a single key. Every call is delayed by 120 ms so the loading skeletons behave like they do against a real network. The handful of production call sites that used raw fetch() go through demoFetch, which returns real Response objects, so their surrounding code is unchanged. Image uploads never leave the browser: the FormData is read into data URLs and stored like any other record.
The mock boundary sits at exactly one module. Everything above api.ts (screens, modals, hooks) is production code; everything below it (backend, database, Cloudflare) is gone.
There is no unit test suite and npm test is not defined. The executable check is the screenshot harness (npm run screenshot, source in scripts/screenshot.mjs), which:
- serves the built
dist/and logs in through the real UI flow; - captures the six screenshots in
docs/from live pages, so the images in this README are generated output, never staged mockups; - fails the whole run if any request leaves localhost, which turns the zero-network claim into an enforced invariant;
- asserts persistence: an edit made through the HTML code modal must survive a full page reload.
That is one assertion plus one global invariant, not a test suite. For a demo whose claim is "this is the production UI on mocked data", the harness checks the claim itself.
- One module swapped, everything else copied.
src/lib/api.tskeeps the production client's exported surface but serves from fixtures. Fidelity of the demo degrades at exactly one place, and that place is documented. - Zero network by construction and by test. Fonts are self-hosted (
@fontsource), the editor's CDN icon stylesheet is disabled (cssIcons: '') and bundled instead, fixture images are local SVGs, and the screenshot harness aborts on any non-localhost request. - Fictional data that says so. Invented tenants, the reserved
.exampleTLD, visible placeholder tokens, and a permanent badge on every screen reading "Demo: mocked data" with the credentials next to it. - Deterministic simulation over randomness. Healthcheck results come from a seeded hash of the domain id and the current minute, so the screen looks alive but reruns are stable.
- Route-level code splitting. Screens load on demand via
lazy(), andvite.config.tssplitsgrapesjsand the react vendor set into their own chunks.
- The mock is faithful to the API's shapes, not its rules: no validation beyond what the UI does, no auth on the store, and slug collisions are only checked where the UI expects the 409.
- Image uploads live as data URLs in
localStorage, so large files will hit the browser's storage quota. - The open-source canvas is not pixel identical to the licensed Studio SDK canvas (different chrome inside the editor frame); everything outside the frame is the production UI.
- Persistence is
localStorageonly: per browser, per device, no sync. - The demo login is a prop, not security; everything behind it is public by design.
- Automated coverage is the screenshot harness described above; there are no unit tests.
MIT license, see LICENSE. The fictional content and fixtures are original to this demo.