Skip to content
Case study · 08 / 09← All work

Copiados Norte

A landing page, a back office and a client ordering app for a print and errands shop in Villa Gesell.

Role
Sole developer: landing, back office, client app and shared core
Stack
Next.js 16, React 19, TypeScript, Tailwind CSS 4, Recharts, Expo
Landing
LandingOne core package for prices, types and state machines
(01) The problem

The shop does copies, ANSES and AFIP paperwork, tax payments and stationery. Orders, appointments, stock, printers and cash each need tracking. Customers need a way to order and get a price without calling. The owner needs numbers the counter staff should not see.

My roleI built all three apps from hi-fi prototypes, plus the shared package they run on.
(02) ResultsCounted in the code
3apps on one domain: landing, back office, client app
12back office views, three of them owner-only
5tabs in the client app
(03) Architecture

Three decisions that shaped it.

  1. 01

    One core package for prices, types and state machines

    Why. The landing quote, the counter POS and the client app read the same price table. An order moves through one defined flow everywhere.

    Trade-off. Shared code is not shared data. With no backend, the client app keeps its own copy of the seed and its orders never reach the back office.

  2. 02

    A single reducer, saved to a versioned localStorage key

    Why. Every back office action goes through one reducer, so swapping in a database only changes where data comes from. Saved state is read after mount to avoid hydration errors.

    Trade-off. Each browser holds its own data. Bumping the key version throws away what was saved before.

  3. 03

    The monthly report is printable HTML, not a PDF library

    Why. Three A4 pages styled with @page keep the brand fonts, vector text and the hatch patterns that make charts readable in black and white.

    Trade-off. The PDF comes from the browser's print dialog. The owner has to pick "Save as PDF", and output can vary between browsers.

(04) GalleryKeep scrolling →
Landing
Landing · Services, a three-step online order and a quote calculator fed by the shared price table.
Owner dashboard
Owner dashboard · The owner's entry view. Recharts bars with a custom tooltip in the brand tokens.
Order queue
Order queue · One button moves each order to its next state. Marking it ready logs a customer notice.
Client app
Client app · Built with Expo, exported to web and served under /app on the same deploy.
(05) What I'd do next
01Add a backend and database so client app orders reach the back office, and move login checks to the server.
02Add a file upload endpoint. Today the order flow only sends the file name over WhatsApp.
03Darken the five color pairs that miss WCAG AA contrast, all listed in the README.
Next project · 09 / 09
Trading analytics dashboard →Heatmaps, charts and live tables for traders
Case study · 08 / 09

Copiados Norte

A landing page, a back office and a client ordering app for a print and errands shop in Villa Gesell.

Role
Sole developer: landing, back office, client app and shared core
Stack
Next.js 16, React 19, TypeScript, Tailwind CSS 4, Recharts, Expo
Copiados Norte
(01) The problem

The shop does copies, ANSES and AFIP paperwork, tax payments and stationery. Orders, appointments, stock, printers and cash each need tracking. Customers need a way to order and get a price without calling. The owner needs numbers the counter staff should not see.

My roleI built all three apps from hi-fi prototypes, plus the shared package they run on.
(02) Results
3apps on one domain: landing, back office, client app
12back office views, three of them owner-only
5tabs in the client app
(03) Architecture

Three decisions that shaped it.

01

One core package for prices, types and state machines

Why. The landing quote, the counter POS and the client app read the same price table. An order moves through one defined flow everywhere.

Trade-off. Shared code is not shared data. With no backend, the client app keeps its own copy of the seed and its orders never reach the back office.

02

A single reducer, saved to a versioned localStorage key

Why. Every back office action goes through one reducer, so swapping in a database only changes where data comes from. Saved state is read after mount to avoid hydration errors.

Trade-off. Each browser holds its own data. Bumping the key version throws away what was saved before.

03

The monthly report is printable HTML, not a PDF library

Why. Three A4 pages styled with @page keep the brand fonts, vector text and the hatch patterns that make charts readable in black and white.

Trade-off. The PDF comes from the browser's print dialog. The owner has to pick "Save as PDF", and output can vary between browsers.

(04) Gallery
Landing
Landing · Services, a three-step online order and a quote calculator fed by the shared price table.
Owner dashboard
Owner dashboard · The owner's entry view. Recharts bars with a custom tooltip in the brand tokens.
Order queue
Order queue · One button moves each order to its next state. Marking it ready logs a customer notice.
Client app
Client app · Built with Expo, exported to web and served under /app on the same deploy.
(05) What I'd do next
01Add a backend and database so client app orders reach the back office, and move login checks to the server.
02Add a file upload endpoint. Today the order flow only sends the file name over WhatsApp.
03Darken the five color pairs that miss WCAG AA contrast, all listed in the README.
Next projectTrading analytics dashboard →
Nahuel SantillánEN