Work / Bike Hub & Repair
Bike Hub & Repair
A bike recycling workshop runs rentals, parking, sales and repairs from one program on its own PC, and from the tablets around the counter.
In short
- Problem
- Repair sheets were written by hand at the counter and copied into an Access database on the workshop computer afterwards: the same job entered twice, and the history only where that computer was.
- Decision
- Everything on the workshop’s own computer: a local server, a database in one file, the browser as the only thing to install on tablets and phones. No cloud, no subscription, and it works with the Internet down.
- Result
- In testing at the workshop since October 2026: one program for the whole counter, on the workshop’s PC and on its tablets. It goes into use in the new premises near the station, which open in the coming months.
At a glance
- For
- Officine Ricicletta, a bike recycling workshop in Ferrara run by a social cooperative
- What changed
- From paper sheets copied into Access to one program for the whole counter
- My part
- Specification, the interface and its component kit, the server, the tests, the launcher for Windows and Mac, the manual; built with Claude Code
- Stack
-
- Node.js
- TypeScript
- React
- SQLite
- Vite
Express · TanStack Query · vitest
- When
- September 2026 – ongoing
Context
Officine Ricicletta, a bike recycling workshop in Ferrara run by a social cooperative: rentals, sales and repairs, one counter and a few people behind it. The workshop is moving to larger premises near the railway station, where bike parking with season tickets will open too: the works are finished, the move is still to come. The request came from someone who works there. The workshop has a PC, a tablet, a thermal printer and a barcode reader. The brief had one line that shaped everything else: this is not a fiscal cash register. Receipts come from the certified machine; the program keeps the workshop’s own books and prints “non-fiscal document” on every page.
| Section | What it is for |
|---|---|
| Parking (Parcheggio) | Bikes left in storage, in and out, with a printed ticket and a barcode; free entry for season-ticket holders. |
| Rentals (Noleggi) | Rental bikes out and back, deposit, accessories, the price, the contract; the fleet and the rates. |
| Repairs (Riparazioni) | Acceptance sheet, states of work, parts and labour, delivery and payment. |
| Sales (Vendita) | The counter’s till: scanning items, cart, discounts, payment, the history of sales. |
Problem
A repair started as a paper sheet at the counter and ended, later, as a record typed into an Access database on the workshop computer: two entries for one job, and a history you could read only from that one machine. Rentals and sales took the same road, a paper sheet first and Access afterwards. There was no parking: it opens in the new premises. Whatever replaced it had to run in the workshop, on a PC that is not always online, be usable from a tablet with one hand, and print a parking ticket in the time it takes a customer to lock the bike.
Exploration
Every choice was weighed against the workshop, and each has its price. Local means that backups and security are the program’s own: a daily copy of the database, a restore procedure written for someone who is not technical, and the checks a cloud platform would make, on which host answers and where a request comes from. SQLite inside Node ties the program to precise versions of Node, 22.18 or 24, never 23, and so does running TypeScript with no build, which allows only syntax that Node can strip away. React costs a build step, which the launcher runs only when the sources have changed. A PIN is weaker than a password, so PINs are stored with scrypt, attempts are limited per device with increasing waits, and a warning stays on screen while the first PIN is in use; a review of the code found that the first version of the limit let any device on the network lock every operator out, and it was rewritten. Printing from the browser brings the print dialog, which Chrome skips in kiosk mode, and roll paper, whose page height Chrome ignores: the program measures the content before it prints.
| Decision | Alternative | Why |
|---|---|---|
| A local server on the shop’s PC, reached from the tablets over the Wi-Fi | The cloud | It works when the connection drops; customers’ data, the ID documents for rentals included, stays in the shop; there is no monthly fee |
SQLite built into Node.js (node:sqlite) |
PostgreSQL or MySQL; a native library such as better-sqlite3 | Nothing to install or compile beyond Node; the database is one file, so a backup is a copy and a restore a swap; one writer at a time is no limit for one shop |
| React and Vite | Pages generated by the server with little JavaScript, or no build at all | The till with its cart and the barcode reader, the rental estimate and the parking exit are interactive, and the price shown is computed by the same function the server charges with |
| A name and a PIN of 4 to 8 digits for each operator | Passwords | Devices are shared and operators change often; every taking and every delivery stays tied to who made it |
| Printing from the browser, with print pages for 80 and 58 mm rolls, A4 and an 85 × 54 mm card | Driving the thermal printer directly (ESC/POS), through a driver or a library | It works with any installed printer, from the tablets too |
Solution
The key decision
Everything on the workshop’s own PC.
Principle: Local-first software — you own your data, in spite of the cloud (Martin Kleppmann and others, Ink & Switch).
One program, one database file, one window that stays open on the workshop’s PC. Every person signs in with a name and a PIN, and every operation records who did it. The Today page opens on four big buttons: new rental, bike in, new repair, new sale. The parking page takes a barcode scan and prints a ticket; a season-ticket holder scans a card and walks in. A repair has an acceptance sheet, states of work, parts and labour, delivery and payment. The day’s cash closes with a printed summary.
Build
Node.js runs the TypeScript server as it is, without a build step; SQLite is the one that ships inside Node, so nothing native to compile and nothing to install beyond Node itself. The interface is React with its own kit of 24 components, plain CSS with variables, system fonts, big buttons for touch, and a layout that holds down to 768 px. The launcher for Windows and Mac checks Node, installs the parts, rebuilds the interface when files change and opens the browser; if something is wrong, the window stays open and says what. Security fits a workshop: PINs with a limiter per device, requests accepted only from the program’s own pages and from the local network, an activity log. About 62,000 lines of TypeScript, of which 13,600 are tests: 56 files, 974 cases, on prices, dates, every API and the launcher itself. A 58-page manual for the staff, with 46 screenshots taken automatically from a demo copy.
What the AI did
- The whole application. It wrote the technical specification and built the server, the database and the interface, with eight modules: rentals, parking, season tickets, sales and stock, repairs, customers, cash, settings. Several agents developed the modules in parallel; in the end there are 974 automated tests.
- The review and the fixes. An independent review across five areas, plus tests in the browser, found 57 problems, all fixed. Among them:
- errors in amounts: a rate of 0 made a rental free, and the switch to daylight saving time charged an extra day;
- security holes on the local network;
- layout problems on tablets.
- The tools to use it for real. The CSV import from the old Access database, automatic backups, the launchers for Windows and Mac, PIN reset, and the 58-page PDF manual with screenshots generated automatically.
What I decided
- The problem and its scope. Which services to handle (rentals, parking with season tickets and printed tickets, sales of new and used bikes and accessories, repairs with their history from Access), and that it was for internal use only. Running everything locally came from that constraint.
- What to do with the old archive. I put the migration off, and asked first for the database’s empty structure only, without the customers’ data. I also collected the Access screens through my brother at the workshop. They showed that rentals were recorded as monthly totals, and that there were sections such as “sprint”, rickshaws and donations.
- Taking it to people. I asked for the manual, to train the workshop’s team. I showed the app to someone from outside through a tunnel, running the commands myself. I put the code in a private GitHub repository, with a pull-request workflow.
Measured: 974 test cases · 22 tables · 24 components · 58 pages of manual · 0 axe violations on 48 pages and dialogs
Result
The program is in its trial run at the workshop since October 2026, with the modules as they will stay. The workshop is moving to new premises near Ferrara’s railway station, where the works are finished, and the program is designed for them: there will be bike parking too, several operators with tablets, and a PC in the office that works as the hub.
What I would do differently
I built the program for the new workshop near Ferrara’s railway station, which was still a building site when I started, so I designed it remotely, from one operator’s account and screenshots of the old database, without ever seeing the place at work.
Next time I would gladly wait for the works to finish and for the workshop to move in, so I could see how the work is actually organised in the new space before designing for it.