Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Appò

One app for the whole town.

Appò brings every shop and service of a small town into a single app — order, book, and see real availability, without a phone call.

Vue 3 Ionic Capacitor Firebase License

Android · iOS · Web — built for small towns

Italiano · Landing page


A look at the app

Discover Book Track
Home screen with category grid Shop page with services and slot calendar Booking history with status timeline
Every shop in town, grouped by category and sorted by how close it is to you. A real service, a real duration, and the time slots that are genuinely free. Orders and appointments with a live status timeline, from sent to confirmed.

Screenshots are taken from a demo shop account created for testing — no real customer data is shown.


The problem

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.


What Appò does

For the customer

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.

For the business owner

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.

For the admin

A control panel over the whole platform: global statistics, booking analytics, approval of new businesses, and user and shop management.


The categories

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

Key features

🕐 A real availability engine

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.

📸 OCR for paper flyers

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.

🤖 An AI assistant that can actually book

"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.

🔳 Printable table QR codes

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.

📍 Distance-based discovery

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.

🎡 Retention that can't be faked

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.

📞 Bookings that ring

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.

🔐 Three roles, enforced on the server

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.


Architecture

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
Loading

The data model, in one paragraph

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.


Tech stack

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

Design

 
#e27108
Primary
#f18e23
Tint
#d15f07
Shade
#ff9d00
Secondary

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.


Project status

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.


Appino, the Appò mascot

AppòTutto in un'unica App

Built for small towns 🇮🇹

Instagram Email

© 2026 Federico Traina — All rights reserved

About

Appo - one app for the whole town: order and book from every local shop and service. Product showcase (source code not included).

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors