Appò brings every shop and service of a small town into a single app — order, book, and see real availability, without a phone call.
Android · iOS · Web — built for small towns
Screenshots are taken from a demo shop account created for testing — no real customer data is shown.
In a town of a few thousand people, every business is its own island.
The pizzeria takes orders on WhatsApp. The hairdresser answers the phone — when she isn't holding a pair of scissors. The car wash has a Facebook page nobody updates. The butcher has nothing at all. If you want to know whether the beautician has a free slot on Friday afternoon, there is exactly one way to find out: call her, during working hours, and hope she picks up.
The cost is paid twice.
Customers hold a mental map of a dozen different channels, none of which show real availability. You call to ask if they're open. You call again to book. You call a third time because you need to move it. Booking something at 11pm — when you actually remember you needed to — is simply not possible.
Business owners lose the call they couldn't answer, and never find out. They take orders on a notepad next to the till. They have no way to tell a customer about a promotion, no way to bring back someone who hasn't visited in two months, and no way to close for a day without repeating it to everyone who calls.
The tools that solve this exist — but they are built for cities, priced for chains, and require one app per business. In a town, nobody downloads eight apps.
Appò is the other approach: one app, all the businesses. A shared directory where each owner runs their own shop, and the customer only has to learn one interface.
Open the app and see the businesses around you, sorted by distance and filtered by category. Tap a food shop and you get its menu, a cart, and a choice of takeaway, home delivery, or ordering at the table. Tap a service and you get a calendar of the slots that are genuinely free — the ones already booked simply aren't offered.
You can customise a dish down to the ingredient, describe a dish that isn't on the menu at all and let the owner price it, ask an AI assistant where to eat tonight, spin a prize wheel after an order is completed, and collect stamps on a digital loyalty card. Push notifications tell you when your order is accepted, when it's ready, and — later — when it might be time to book again.
Payment happens in person. Appò handles the ordering and the booking; you pay at the counter, at the table, or on delivery, as you always have.
A dashboard that replaces the notepad. Orders arrive in real time, split by status, with a checkbox per item so the kitchen can tick off what's ready. Appointments arrive in a daily agenda built on the same availability engine the customer sees, so the two can never disagree.
Owners set their own opening hours as weekly ranges with breaks, per-date overrides for holidays and special days, slot granularity, a buffer between appointments, and — where it matters — different hours per staff member or resource. Food shops work differently on purpose: no working hours at all, just three switches the owner controls (holidays, an emergency "stop orders" toggle, and weekly closing days).
They can generate printable QR codes for their tables as a PDF, post an announcement on their public shop page, keep a private note on a customer, and switch to "customer view" to see exactly what everyone else sees. On Android, a new booking can ring the phone like an incoming call — because a notification that can be missed is a booking that gets lost.
A control panel over the whole platform: global statistics, booking analytics, approval of new businesses, and user and shop management.
Appò isn't a generic marketplace with one flow bent to fit everything. Each category carries its own configuration — the label for its staff, the wording of its call to action, the fields on its booking form, and which features are even switched on.
A pizzeria has a pizzaiolo and a "Confirm order" button. A five-a-side pitch has no staff, just the field itself, and its button reads "Book the pitch". A tour operator books an experience. The customer never sees a form that speaks the wrong language.
Ordering — catalogue, cart, delivery or pickup
![]() Pizzeria |
![]() Restaurant |
![]() Sandwich bar |
![]() Pastry shop |
![]() Bakery |
![]() Butcher |
![]() Café / Bar |
![]() Fishmonger |
| 🔥 Gas cylinders |
🛒 Supermarket |
Gas cylinders reuse the catalogue-and-delivery flow. Supermarkets are a group of their own: weighed products, substitutions and capacity-based delivery windows don't fit the food model, so they get their own catalogue structure rather than a bent one.
Booking — services, staff and time slots
![]() Beautician |
![]() Hairdresser |
![]() Car wash |
![]() Professional office |
![]() Sports pitch |
![]() Vehicle inspection |
![]() Tourism |
The heart of the app is a pure, dependency-free, unit-tested module: no Vue, no Firebase, just arithmetic on minutes. It is the single source consulted by the customer's calendar, the owner's agenda, and the AI assistant — so all three always agree.
It handles multiple opening ranges per day with breaks, configurable slot granularity, per-service durations, a buffer between appointments, minimum booking lead time, per-date overrides for closures and special hours, and separate hours per resource or staff member.
The detail that makes it correct: everything is expressed as minutes from the start of the working day, and a range that crosses midnight simply ends past 1440. A shop open 22:00 → 01:00 has a window ending at 1500, not one that wraps around to zero. The axis stays monotonic, adjacency never breaks, and the classic 23:45 → 00:00 bug can't happen.
The university bulletin board lets students photograph a printed poster stuck to a wall. The image is OCR'd on-device with tesseract.js, and the extracted text is handed to Google Gemini, which fills in the event form — title, date, place. A photo replaces a form.
"Betta" is more than a recommendation chatbot. It reads real slots from the same availability engine, evaluates holidays in local time, and writes the booking using a deterministic document ID inside a transaction — which is what makes a double booking on the same slot impossible, even under a race.
The owner generates unique QR codes for their tables and downloads them as a print-ready PDF, laid out four to a sheet or one per A4 page — the app tells them how many sheets it will take before they print. A customer scans the code at the table, gets the menu with the table already locked in, and can order as a guest without an account.
Shops are ordered by real GPS distance, with a graceful fallback chain: device location, then the town saved on the profile, then a default town. The map is Leaflet over OpenStreetMap, and shop addresses are geocoded at registration with a precision check, so a shop never lands in the middle of the wrong street.
The prize wheel unlocks only when the owner marks an order Completed — and that unlock is written server-side, so the client can't forge it. The prize itself is drawn by a transactional Cloud Function; the client only animates the wheel toward a result it was given. Prizes are then redeemed over WhatsApp. Alongside it, a stamp-based loyalty card that stamps automatically on a completed food order.
On Android, a new booking can be presented as a native incoming-call screen, delivered by a data-only push and rendered by a native service — so it works even when the app is closed and no JavaScript is running. On iOS, where CallKit is reserved for real calls, it degrades to a time-sensitive alert with a long ringtone plus an in-app overlay.
Customer, business owner and admin are not a UI convention. Firestore security rules whitelist which fields each role may write: a customer cannot touch a booking's status, total or loyalty stamp; an owner can only write to their own shop; the prize configuration is read-only to every client; and the wheel eligibility collection is deliberately absent from the rules, so it falls through to deny-all and is reachable only by the Cloud Functions.
Order totals are recalculated server-side from real prices on every food order, and a tampered total is corrected rather than trusted.
flowchart TB
subgraph clients["Clients"]
A["Android<br/>(Capacitor)"]
I["iOS<br/>(Capacitor)"]
W["Web / PWA"]
end
subgraph app["Ionic + Vue 3 application"]
UI["Views — Customer · Owner · Admin"]
ST["Pinia stores"]
AV["availability.ts<br/>pure slot engine"]
end
subgraph fb["Firebase"]
AU["Authentication"]
FS["Firestore<br/>realtime database"]
SG["Cloud Storage<br/>images"]
CF["Cloud Functions v2<br/>validation · triggers · schedulers"]
FM["Cloud Messaging<br/>push"]
end
subgraph ext["External services"]
GM["Google Gemini<br/>AI assistant · form filling"]
OSM["OpenStreetMap<br/>maps · geocoding"]
end
A --> app
I --> app
W --> app
UI --> ST
UI --> AV
ST --> FS
UI --> AU
UI --> SG
UI --> CF
FS --> CF
CF --> FM
FM --> A
FM --> I
FM --> W
UI --> GM
UI --> OSM
style app fill:#fff4e6,stroke:#e27108,stroke-width:2px
style fb fill:#fff9e6,stroke:#ff9d00,stroke-width:2px
style AV fill:#e27108,color:#fff,stroke:#d15f07
Firestore is schemaless, so the shape lives in the code and the security rules
rather than in a migration file. At the top sit the three identity collections —
customers, owners, shops — plus a single Prenotazioni collection that holds
both food orders and service appointments, discriminated by type. Catalogues
hang off the shop: food menus as one document per shop (fast to read, bounded by
Firestore's 1 MiB limit), supermarket articles as an unbounded subcollection.
Reviews, loyalty cards, waiting lists, promotions and private customer notes are
subcollections under the shop they belong to.
Two details are worth calling out. The customer home does not scan the shop collection: a scheduled function maintains a single pre-projected list document, turning N reads into one, with a staleness timestamp that falls back to a full scan if the scheduler ever stops. And prize-wheel eligibility lives in a collection with no security rule at all — falling through to the catch-all deny — so only the Admin SDK inside Cloud Functions can reach it.
Server-side automation runs as Cloud Functions v2: order-total validation, booking triggers that fan out push notifications and unlock the wheel, and schedulers that prune old bookings, remind owners of pending confirmations, reopen shops each morning, and nudge customers who haven't booked in a while.
| Layer | Technology |
|---|---|
| Framework | Vue 3 (Composition API) + TypeScript |
| UI | Ionic Framework 8, Ionicons, Swiper, Lottie |
| State | Pinia |
| Native shell | Capacitor 8 — Android & iOS |
| Native plugins | Geolocation, Camera, Filesystem, Share, Haptics, Keyboard, Network, Preferences, Local Notifications, Splash Screen, Live Update, Incoming Call Kit |
| Backend | Firebase — Authentication, Firestore, Cloud Storage, Cloud Functions v2, Cloud Messaging, Crashlytics |
| AI | Google Gemini (@google/generative-ai) |
| OCR | tesseract.js |
| Maps | Leaflet + OpenStreetMap |
| Documents | jsPDF + qrcode (printable table QR sheets) |
| Charts | Chart.js + vue-chartjs (admin statistics) |
| Gamification | vue-fortune-wheel |
| Security | DOMPurify, Firestore security rules, server-side validation |
| Build & test | Vite, vue-tsc, Vitest, ESLint (+ vuejs-accessibility) |
| Design | Design-token system on Inter, brand #e27108 |
#e27108Primary |
#f18e23Tint |
#d15f07Shade |
#ff9d00Secondary |
Warm orange and amber on Inter, driven by a design-token system (spacing,
radii, shadows, type scale) rather than hard-coded values. The system is built
around one rule: the app has two audiences and two standards. The customer
side is allowed personality, entrance motion and a single strong accent per
screen. The owner side is a tool used all day, where repetition and
predictability are features, not flaws — density and glanceability beat
character. Accessibility floors apply to both: 44×44px tap targets,
:focus-visible styling, prefers-reduced-motion support, and contrast checked
against the brand orange rather than assumed.
Appò is a real, working application, actively developed and in use. The server-side layer — Cloud Functions, strict Firestore rules and composite indexes — is deployed to production. The native Android and iOS builds are maintained alongside the web app.
This repository is a showcase, not the source. The application is closed source and proprietary; no application code, configuration, credentials or infrastructure definition is published here. What you're reading is a presentation of the product and the engineering behind it, written from the real codebase.
Store availability: not yet listed — links will be added here when the app is published.


















