ElectroCoeur
A spreadsheet on one side, clients with no answers on the other. The app connected the two.
ElectroCoeur sells and maintains defibrillators for local authorities and businesses. All the fleet data already existed — in a Google Sheet exported from their field-service tool. But a client wanting to know “is my equipment compliant today?” had no simple way to find out.
- Category
- Cross-platform client portal
- Platforms
- Web · iOS · Android
Where it started
The company ran a WordPress client area that no longer met the need. The information existed, but it was locked inside an internal file: to get an answer, a client had to call, and someone had to look up the matching row in the spreadsheet.
The real issue was not creating data but making existing data readable — without asking the company to change how it works. The spreadsheet had to stay the reference, because it is fed by the field-service tool used on site. Each client also had to see only their own units, and the PDF service reports had to be attached to the right defibrillator.
What was built
The app reads the spreadsheet every night and refreshes the fleet view. The client opens their portal — on a phone or in a browser — and immediately sees the state of their equipment: green when all is well, orange when a consumable is nearing expiry, red once it has expired. A map locates every unit, service reports are readable in the app, and three forms cover requesting a service visit, adding a unit, or asking for a quote.
What the product does
Fleet status at a glance
Three colours, one rule: a unit inherits the state of its most urgent consumable. Clients know where they stand without reading a table.
Alerts before expiry
A consumable expiring within 90 days turns orange before it goes out of service. Compliance becomes something you anticipate, not something you discover.
Map of every unit
Each defibrillator is placed on a map, with automatic grouping when there are many. Useful for authorities managing dozens of them.
Service reports, correctly filed
The PDFs produced after each visit are filed automatically under the right unit and readable inside the app.
Three request forms
Service visit, new unit or quote: each request reaches the right mailbox without going through the switchboard.
Each client sees only their fleet
Access rights are enforced server-side, unit by unit: a client cannot reach another client’s data, even by looking for it.
New-report notifications
A daily notification flags newly available reports, sent only to the people responsible for those units.
Technician and admin views
Technicians see the whole fleet; the admin side tracks sync status and client accounts.
The decisions that mattered
Never overwrite the client’s data
The sync compares each spreadsheet row against what is already stored: unchanged rows are skipped, changed rows updated, and rows that disappear are marked as deleted rather than actually destroyed. That is what makes the sync safe to re-run: a mistake in the source file destroys nothing irreversibly.
Attaching a PDF to the right unit, with no human step
Service reports arrive as PDFs in a shared folder, with no usable naming convention. The pipeline reads the text of each page to find the unit’s unique number, then files the document in the right place. It is designed to be re-run as often as needed without ever creating a duplicate.
Security written as a filter, not as a check
Rather than trusting the app to ask only for the right data, the server refuses any request that is not already narrowed to what the caller may see. The distinction matters: a modified app or direct database access cannot get around the rule.
A web update that never reached the user
On the web version, users kept seeing an old build despite new releases. The fix detects the specific case of “a new version installed while one is already running” — which means an update, not a first visit — and reloads the page exactly once, guarded against reload loops.
An invitation email must not be able to go out 262 times
The sync never sends mail itself. A separate function does, under hourly and daily quotas. A misbehaving sync therefore cannot trigger a mass send to every client — the kind of incident a business does not commercially recover from.
An unexpected number that was not a bug
The company expected around 3,000 client accounts; the sync produced 262. The cause was not in the pipeline: more than 3,000 spreadsheet rows had no email address. Telling that apart from a technical fault avoided "fixing" a pipeline that was working correctly, and surfaced a genuine data-completeness issue on the client side.
How it fits together
Google Sheets
The company’s own file stays the reference: no habit to change
Synchronisation nocturne
Every night, only changed rows are picked up — no duplicates, nothing lost
Cloud Firestore
Fleet data, filtered so each client sees only their own units
Application Flutter
One codebase for web, iOS and Android
The technologies used
Frontend
- Flutter
- Dart
- Cubit
- go_router
- flutter_map
Backend
- Cloud Functions
- TypeScript
- Node 24
Infrastructure
- Firebase
- Cloud Firestore
- Cloud Storage
Services
- Google Sheets API
- Google Drive API
- Firebase Cloud Messaging
What was delivered
Delivered solo and published on both the App Store and Google Play, from a single codebase covering web, iOS and Android. Nine server functions handle the nightly spreadsheet sync, the filing of service reports and the notifications. Roughly 13,700 lines of Dart and 5,800 lines of TypeScript. Real usage figures are not published: no audience measurement was set up on the project.
- Web
- iOS
- Android
Need a similar product?
Let’s talk about what you want to build. I reply personally, usually within 24 hours.