mirror of
https://github.com/Dvorinka/Bookra.git
synced 2026-10-28 07:41:16 +00:00
Class sessions rendered as a flat stack of tall cards below the stepped picker, and a full session still offered "Book now" while silently producing a waitlisted booking. Sessions now group by day with compact timetable rows; empty sessions show Full and switch the action to Join waitlist, and low capacity shows a warning count. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
397 lines
24 KiB
Markdown
397 lines
24 KiB
Markdown
# Bookra
|
|
|
|
## Delivery Tracker
|
|
- `[done]` Milestone 1: Remote-first monorepo scaffold completed
|
|
- SolidJS frontend shell
|
|
- Go + Gin backend shell
|
|
- OpenAPI-driven TS client generation
|
|
- Neon Auth adapter boundary
|
|
- remote-first env layout for Vercel, Railway, and Neon
|
|
- `[done]` Milestone 2: Repository-backed booking baseline completed
|
|
- DB/in-memory repository boundary added
|
|
- public availability now generated through backend service logic
|
|
- public booking creation now goes through real service validation
|
|
- appointment conflict detection implemented
|
|
- class capacity and waitlist fallback implemented
|
|
- backend tests added for booking and slot behavior
|
|
- frontend public booking page can submit a demo booking
|
|
- `[done/partial]` Milestone 3: Tenant bootstrap, dashboard hydration, and Neon-authenticated API flows
|
|
- tenant bootstrap endpoint now resolves through repository-backed membership lookup
|
|
- dashboard summary endpoint now resolves through repository-backed metrics lookup
|
|
- frontend dashboard now hydrates from API when a Neon Auth JWT is available
|
|
- frontend dashboard degrades to contract-shaped preview mode when auth is unavailable
|
|
- Neon Auth token acquisition remains adapter-isolated and still needs production validation against live Neon Auth
|
|
- `[done/partial]` Milestone 4: Stripe subscription sync and entitlement baseline completed
|
|
- normalized billing snapshot model added to backend and OpenAPI contract
|
|
- backend-owned subscription state now syncs through one overwrite path
|
|
- Stripe checkout launch endpoint added
|
|
- Stripe webhook endpoint added with signature verification and event filtering
|
|
- billing snapshot refresh endpoint added
|
|
- customer portal endpoint added
|
|
- plan entitlements now derive from normalized plan state
|
|
- dashboard now surfaces billing status, entitlements, and checkout actions
|
|
- mock/no-key fallback still works for local development
|
|
- `[done/partial]` Milestone 5: Reminder jobs, notification delivery abstractions, and remote job-runner baseline completed
|
|
- booking creation now schedules reminder jobs for qualifying upcoming bookings
|
|
- notification service now dispatches due jobs through replaceable provider interfaces
|
|
- localized reminder copy now renders with tenant locale/timezone context
|
|
- notification delivery log persistence added
|
|
- internal reminder dispatch endpoint added for Railway-style scheduled execution
|
|
- shared job-runner key added for remote job execution
|
|
- current providers are noop-safe abstractions; real email/SMS vendors still need production integration
|
|
- `[done/partial]` Milestone 6: Production auth completion and tenant identity sync baseline completed
|
|
- verified Neon JWTs now sync app-side users automatically on protected requests
|
|
- app-side membership resolution now maps by `users.neon_subject` instead of assuming JWT subject equals DB primary key
|
|
- frontend auth provider now reads JWT from the Neon session contract instead of a fallback token hook
|
|
- bootstrap payload now returns user identity fields needed for authenticated shells
|
|
- Neon subject schema migration added for production databases
|
|
- live Neon Auth / staging validation still needs to be completed against real infrastructure
|
|
- `[done/partial]` Milestone 7: First-tenant onboarding flow added to the dashboard
|
|
- authenticated users without membership now see a real workspace creation flow in `/dashboard`
|
|
- protected tenant onboarding endpoint added with backend validation for slug, preset, locale, and timezone
|
|
- onboarding now creates the tenant, owner membership, and first default location
|
|
- dashboard can transition from empty bootstrap shell to a provisioned tenant state
|
|
- post-onboarding entity screens for locations, services, staff, blocked days, working hours, and class templates/sessions now exist (Milestones 8 and 10)
|
|
- `[done/partial]` Milestone 8: Catalog management shipped; live staging validation and operational hardening remain
|
|
- service CRUD end-to-end: repository (pgx + in-memory), catalog service with validation and tenant isolation, protected `/v1/catalog/services` routes, OpenAPI schemas, dashboard services page
|
|
- staff CRUD end-to-end with active toggle, optional location assignment, and `MaxStaff` entitlement enforcement; dashboard staff page with upgrade prompt
|
|
- dashboard summary now resolves `staffName` on bookings and computes `setupCompletion` from real state (brand, location, service, staff, schedule)
|
|
- booking modal service field is a select bound to the tenant's real services; blocked-day creation supports optional per-staff scoping
|
|
- deleting a service or staff member preserves bookings (FK `SET NULL`) and removes the member's availability rules (`CASCADE`), mirrored in the in-memory repository
|
|
- live staging validation, real Neon Auth/Stripe/SMS provider checks, and operational hardening are still open
|
|
- `[done/partial]` Milestone 9: Public booking flow with real service/staff selection
|
|
- public booking page is now a stepped flow: pick service → optional staff preference → pick day → pick time; class sessions still listed below
|
|
- `GET /v1/public/tenants/{slug}/catalog` exposes bookable services and active staff for the booking page
|
|
- availability endpoint accepts `serviceId`, `staffId`, and `date` filters; default response is unchanged for the embeddable widget
|
|
- slots now carry `staffName`, respect tenant-wide and per-staff blocked days, and are generated per-day without the preview cap when `date` is given
|
|
- booking creation hardened: appointment requests must fit inside a matching availability rule, not overlap a blocked day, and staff/location IDs must belong to the tenant with staff active
|
|
- remaining: live staging validation
|
|
- `[done/partial]` Milestone 10: Per-staff availability and class/session management
|
|
- working-hours repository now accepts a nullable staff scope on list/update (pgx + in-memory); staff-scoped rules live in `availability_rules` and directly drive public slot generation
|
|
- protected routes added: `GET/PUT /v1/catalog/staff/{id}/working-hours[/{day}]` with tenant-ownership checks
|
|
- class template + session CRUD end-to-end: repository (`classes.go`), catalog service with validation, protected `/v1/catalog/class-templates` and `/v1/catalog/class-sessions` routes, OpenAPI + regenerated client
|
|
- sessions derive `endsAt` from template duration, default capacity from the template, validate future start, tenant-owned template and optional location; list resolves template title, location name, and live booked count
|
|
- deletion semantics mirror the schema: deleting a template cascades its sessions; deleting a session preserves bookings via `class_session_id SET NULL` (mirrored in the in-memory repo)
|
|
- dashboard zones page gained a staff-scope selector on the working-hours tab (business-wide vs individual schedule) and a Classes tab with template create/delete and session scheduling
|
|
- normalized `availability_rules` time columns (`HH:MM:SS`) down to the `HH:MM` API contract in the service layer
|
|
- remaining: live staging validation
|
|
- `[done/partial]` Milestone 11: Booking lifecycle — dashboard cancellation and waitlist promotion
|
|
- businesses can cancel bookings from the dashboard (`POST /v1/catalog/bookings/{id}/cancel`), tenant-checked, with a two-step confirm in the booking detail modal
|
|
- cancelling a class booking (dashboard or customer self-service) now promotes the earliest waitlisted bookings while free capacity remains, ordered by booking `created_at`; promoted customers receive a confirmation email, cancelled customers a cancellation email
|
|
- shared `bookings.PromoteWaitlist` helper drives both cancel paths; waitlist ledger entries are marked resolved on promotion
|
|
- bookings surface the `waitlisted` status: stats card, filter option, warning-tone badge, demo seed entry; `dashboard.{status}` interpolation resolves the new i18n key automatically
|
|
- `BookingRecord` now carries `created_at` (needed for fair waitlist ordering); `GetBookingByID` added to the repository surface
|
|
- remaining: live staging validation; waitlist position display and manual promotion controls are not yet exposed
|
|
- `[done/partial]` Milestone 12: Real dashboard business metrics
|
|
- `DashboardMetrics` gained `CompletedCount` (confirmed bookings already ended within the window) in both pgx and in-memory repositories
|
|
- `DashboardSummary` now queries three trailing windows (next 7d, last 7d, 7-14d ago) and emits four KPIs with real trend deltas: `bookings_this_week`, `completed`, `cancellations`, `utilization` (delta in percentage points)
|
|
- `DashboardKPI` carries optional `change`/`trend` (`up|down|neutral`); the KPI card already rendered these, so real data now drives the trend arrows instead of the previous `neutral`/`—` fallback
|
|
- KPI labels are localized in the frontend via a `code` → i18n-key map (`dashboard.kpi.bookingsThisWeek`, `cancellations`, `utilization`; `completed` reuses the existing key); backend labels stay English as a fallback
|
|
- the booking-trend chart was already bucketing real `allBookings` into the current week — confirmed, no change needed
|
|
- demo mode intentionally keeps curated static KPI values; real tenants get the computed set
|
|
- remaining: live staging validation; "new clients" KPI still has no backend metric (customer entity lacks first-booking tracking)
|
|
- `[done/partial]` Milestone 13: Reminder pipeline reliability
|
|
- reminder jobs are lifecycle-aware: customer and dashboard cancellation mark pending jobs `cancelled`; reschedule deletes pending jobs and re-arms one via `reminderSchedule` for the new time (no job when moved inside the no-reminder horizon)
|
|
- dispatch now joins `bookings.status` and skips non-confirmed bookings (`skipped` job status, `skippedCount` in the response contract) — cancelled/waitlisted customers no longer receive reminders for bookings that are not happening
|
|
- reschedule hardened with the same validation as booking creation: appointment reschedules must fit an availability rule and avoid blocked days (`validateAppointmentTarget` is now a shared package-level helper); class bookings keep session-defined times
|
|
- new repository methods `CancelPendingReminderJobsForBooking` / `DeletePendingReminderJobsForBooking` in pgx and in-memory implementations
|
|
- remaining: live staging validation; SMS reminder channel exists in schema (`channel` column) but only `email` is dispatched — SMS reminders are a separate opt-in feature
|
|
- `[done/partial]` Milestone 14: Metrics completion + reminder dispatch scheduling
|
|
- `DashboardMetrics` gained `NewClientsCount` — distinct customer emails whose earliest-ever booking (`starts_at`) lands in the window; a fifth KPI `new_clients` completes the set the demo data promised
|
|
- KPI grid rebalanced for five cards (`lg:grid-cols-3 xl:grid-cols-5`); `new_clients` maps to the existing `dashboard.kpi.newClients` i18n key
|
|
- reminder dispatch now has a real trigger: `.github/workflows/reminder-dispatch.yml` calls `POST /v1/internal/jobs/reminders/dispatch` every 15 minutes with `X-Bookra-Job-Key` from secrets (`BOOKRA_API_URL`, `BOOKRA_JOB_RUNNER_KEY`); jobs are timestamp-based so scheduler lateness delays but never skips
|
|
- remaining: live staging validation; the workflow no-ops gracefully until the two secrets are configured
|
|
- `[done/partial]` Milestone 15: SMS reminder channel
|
|
- booking creation arms a second reminder job on `channel: sms` when the tenant has SMS enabled (`tenant_sms_settings.enabled`) and the customer left a phone number; email jobs are unchanged
|
|
- `notifications.Service` gained an injected `smsSender` function adapter (wired to `sms.Service.SendMessage` in the API server, keeping the package dependency-free); dispatch routes `channel: sms` through it with localized body text and the customer phone as recipient
|
|
- `sms.Service.SendMessage` still enforces enabled/plan/monthly-limit and logs usage, so SMS reminders inherit metering and billing automatically; missing provider config or phone marks the job `failed` with a delivery-log reason
|
|
- reminder lifecycle work from milestone 13 applies per-booking, so cancel/reschedule clears and re-arms both channels
|
|
- remaining: live staging validation against a real SMS Manager key
|
|
- `[done/partial]` Milestone 16: Waitlist position display
|
|
- `GetWaitlistPosition` repository method (pgx + memory) returns the 1-based queue spot: count of earlier-created waitlisted bookings on the same session + 1
|
|
- `CustomerBookingView` carries `waitlistPosition` for waitlisted class bookings; the manage page now shows a proper status badge for `waitlisted`/`cancelled` (previously both rendered as "Pending") plus a notice with the queue position and the auto-promotion explanation, in CS and EN
|
|
- fixed invalid token names along the way (`--danger-soft`/`--warning-soft` do not exist; `--error-subtle`/`--warning-subtle` do)
|
|
- remaining: live staging validation; manual waitlist promotion controls are still not exposed
|
|
- `[done/partial]` Milestone 17: Class template editing
|
|
- `PUT /v1/catalog/class-templates/{id}` end-to-end: `UpdateClassTemplate` in both repositories, catalog service method sharing `validateClassTemplateInput` with create, route, OpenAPI path, regenerated client
|
|
- classes tab template rows gained a pencil edit action that loads the template into the form; the submit button switches to save-changes with a cancel affordance
|
|
- remaining: live staging validation
|
|
- `[done/partial]` Milestone 18: Public class list polish
|
|
- class sessions on the public booking page are now grouped by day (localized weekday + date headers) with compact timetable-style rows instead of one tall card per session — consistent with the stepped appointment picker above
|
|
- capacity states read honestly: spots-left in success/warning tones, `Obsazeno`/`Full` when empty, and the action switches to `Na čekací listinu`/`Join waitlist` (previously a full session still said "Book now" while silently producing a waitlisted booking)
|
|
- remaining: live staging validation
|
|
|
|
## Overview
|
|
Bookra is a Czech-first booking SaaS for small local service businesses. It targets salons, massage therapists, small clinics, repair shops, and small studios/clubs that need a simple public booking flow, internal schedule management, reminders, and basic business reporting without becoming a full ERP.
|
|
|
|
The product is multi-tenant, calm in scope, and optimized for production quality rather than a fast prototype.
|
|
|
|
## Product Direction
|
|
Bookra uses one unified booking core with tenant presets.
|
|
|
|
This means:
|
|
- one platform for appointments and simple classes
|
|
- different business presets for salon, clinic, repair, massage, sports/studio
|
|
- shared scheduling, reminders, dashboard, and billing logic
|
|
- no industry-specific forked products in v1
|
|
|
|
## Launch Scope
|
|
### Market
|
|
- Czech Republic first
|
|
- English included at launch
|
|
- EU-friendly hosting and data handling defaults
|
|
|
|
### Supported booking models
|
|
- Appointments
|
|
- Capacity-based classes
|
|
|
|
### Explicitly out of scope for v1
|
|
- Customer payments for bookings
|
|
- Deposits or prepaid services
|
|
- Memberships, passes, or package accounting
|
|
- Regulated healthcare workflows
|
|
- Medical records or sensitive health data
|
|
- Marketplace/discovery layer
|
|
|
|
## Customer Experience
|
|
- Public booking page per business
|
|
- Guest booking supported by default
|
|
- Optional customer accounts for history and faster rebooking
|
|
- Confirmation, cancellation, and reschedule flow
|
|
- Czech and English UI from day one
|
|
- Timezone-aware booking and reminders
|
|
|
|
## Business Experience
|
|
Each business can manage:
|
|
- multiple locations
|
|
- multiple staff members
|
|
- services
|
|
- class sessions
|
|
- business hours and exceptions
|
|
- buffer times
|
|
- blackout periods
|
|
- reminder settings
|
|
- branding basics
|
|
- dashboard metrics
|
|
|
|
Owner dashboard should show:
|
|
- bookings this week
|
|
- cancellations
|
|
- utilization
|
|
- simple revenue forecast based on scheduled services
|
|
|
|
## Core Scheduling Rules
|
|
The booking engine must support:
|
|
- multi-tenant isolation
|
|
- staff and location assignment
|
|
- conflict detection
|
|
- buffer times
|
|
- timezone-safe date handling
|
|
- waitlists
|
|
- class capacity limits
|
|
- exception-based availability rules
|
|
|
|
Appointments and classes should share the same scheduling foundation where possible.
|
|
|
|
## Notifications
|
|
v1 reminder strategy:
|
|
- email is first-class
|
|
- SMS is optional and treated as a paid add-on
|
|
- SMS must be abstracted behind a provider interface
|
|
- Czech-first provider support is preferred, with room for generic fallback providers later
|
|
|
|
## Billing
|
|
Bookra uses Stripe for B2B SaaS subscription billing. (Migrated from Paddle; the Paddle integration was removed.)
|
|
|
|
Stripe scope in v1:
|
|
- tenant subscription plans
|
|
- plan upgrades/downgrades
|
|
- add-ons such as SMS or expanded limits
|
|
- webhook-based subscription state sync
|
|
- customer self-serve billing portal
|
|
|
|
Stripe is not used for end-customer booking checkout in v1.
|
|
|
|
## Remote-First Architecture
|
|
Bookra follows the `tdvorak-fullstack` profile with remote deployment overrides.
|
|
|
|
### Stack
|
|
- Frontend: SolidJS + Vite + TypeScript + Tailwind
|
|
- Backend: Go + Gin
|
|
- Database: Neon Postgres + goose (handwritten pgx repositories; sqlc removed)
|
|
- Auth: Neon Auth
|
|
- API contract: OpenAPI as source of truth
|
|
- Frontend deployment: Vercel
|
|
- Backend deployment: Railway
|
|
- Local development: direct app processes against remote services
|
|
|
|
### Structure
|
|
- modular monolith first
|
|
- monorepo layout
|
|
- generated TypeScript API client from OpenAPI
|
|
- strong tenant boundaries
|
|
- background jobs for reminders and waitlist handling
|
|
- no Docker Compose and no self-hosted infra path in v1
|
|
|
|
Suggested layout:
|
|
- `/apps/frontend`
|
|
- `/apps/backend`
|
|
- `/packages/api-client`
|
|
- `/packages/shared-types`
|
|
|
|
## Auth And Identity
|
|
- Neon Auth handles sign-up, sign-in, sessions, and browser auth state
|
|
- frontend owns browser auth flows
|
|
- Go backend verifies Neon Auth JWTs through JWKS
|
|
- backend treats Neon `sub` as the authenticated principal
|
|
- backend now syncs `neon_subject` into app-side `users` records on authenticated requests
|
|
- JWT usage is isolated to frontend-to-backend API calls across domains
|
|
- Neon-specific auth code must stay behind replaceable adapters because Neon Auth is beta
|
|
|
|
## Database And Environments
|
|
- Neon is the only database platform
|
|
- pooled connections for application traffic
|
|
- direct connections for migrations and admin tasks
|
|
- staging and production only in v1
|
|
- no per-PR backend preview environments in v1
|
|
|
|
## Main Domain Model
|
|
Key entities:
|
|
- tenants
|
|
- tenant users
|
|
- customer accounts
|
|
- locations
|
|
- staff
|
|
- services
|
|
- class templates
|
|
- class sessions
|
|
- availability rules
|
|
- availability exceptions
|
|
- bookings
|
|
- waitlist entries
|
|
- reminder jobs
|
|
- subscription accounts
|
|
- subscription events
|
|
|
|
## v1 Delivery Phases
|
|
### Phase 1
|
|
- monorepo setup
|
|
- Neon Auth integration shell
|
|
- tenant foundation
|
|
- locations, staff, services
|
|
- i18n foundation
|
|
- OpenAPI baseline
|
|
- status: completed
|
|
|
|
### Phase 2
|
|
- appointment booking flow
|
|
- availability rules
|
|
- conflict detection
|
|
- cancellation and rescheduling
|
|
- timezone-safe scheduling
|
|
- status: in progress
|
|
- current implementation:
|
|
- public availability endpoint returns generated appointment and class slots
|
|
- public booking creation validates tenant existence, timestamps, conflicts, and class capacity
|
|
- frontend public route can trigger booking creation from live slots
|
|
- repository pattern supports Neon-backed persistence with memory fallback
|
|
|
|
### Phase 3
|
|
- class sessions
|
|
- capacity limits
|
|
- waitlists
|
|
- email reminders
|
|
- dashboard metrics
|
|
- status: materially implemented
|
|
- current implementation:
|
|
- class sessions appear in public availability
|
|
- class capacity and waitlist status are enforced at booking time
|
|
- dashboard metrics endpoint is repository-backed but still minimal in scope
|
|
- booking creation now schedules reminder jobs
|
|
- due reminder jobs can now be dispatched through the notifications service
|
|
- delivery logging now exists for reminder execution
|
|
|
|
### Phase 4
|
|
- Stripe subscriptions
|
|
- SMS add-on support
|
|
- plan enforcement
|
|
- hardening, logging, and operational tooling
|
|
- status: in progress
|
|
- current implementation:
|
|
- billing snapshot persistence added
|
|
- Stripe checkout route added (Checkout Sessions)
|
|
- Stripe webhook route added with signature verification
|
|
- Stripe portal route added (customer portal sessions)
|
|
- subscription refresh route added
|
|
- entitlement mapping is now available to the frontend
|
|
- API edge now includes security headers and public route rate limiting
|
|
- remote job-runner endpoint added for reminder execution
|
|
- goose commands are wired into repeatable npm scripts
|
|
|
|
## Current Implementation Notes
|
|
- Backend is no longer static-demo-only for booking flows.
|
|
- The repository layer supports:
|
|
- Neon-backed pgx access when `BOOKRA_DATABASE_URL` is configured
|
|
- deterministic in-memory fallback when Neon is not configured
|
|
- OpenAPI remains the frontend/backend contract source.
|
|
- Frontend public booking flow is now interactive, not just presentational.
|
|
- Route structure is now explicit:
|
|
- `/` is the primary marketing landing page
|
|
- `/dashboard` is the main application entry
|
|
- `/book/:tenantSlug` stays public and customer-facing
|
|
- Neon Auth remains intentionally isolated behind adapters because the managed product is still beta.
|
|
- Billing now follows a backend-owned snapshot model rather than trusting incremental webhook payloads.
|
|
- `apps/auth-service` is now retired from active runtime and docs.
|
|
- Reminder delivery now has a working runtime path instead of just a queued schema.
|
|
- goose migration execution is runnable through workspace scripts.
|
|
- Protected backend requests now perform automatic app-side identity sync before tenant membership resolution.
|
|
- `/dashboard` now includes first-run onboarding instead of only a passive empty state for unassigned authenticated users.
|
|
- Catalog management now covers services and staff end-to-end; staff creation enforces `MaxStaff` entitlements and staff location assignment validates tenant ownership.
|
|
- Dashboard summary computes `setupCompletion` from real configured state (brand, location, service, staff, availability rules) rather than a static value.
|
|
- Public booking flow offers real service and staff selection via a stepped picker backed by `GET /v1/public/tenants/{slug}/catalog` and filtered availability (`serviceId`, `staffId`, `date`).
|
|
- Appointment creation validates that requested times fit inside availability rules, skip blocked days, and reference tenant-owned staff and locations.
|
|
|
|
## Current Gaps
|
|
- Neon Auth token extraction is wired structurally, but still needs live browser validation against a real Neon Auth environment.
|
|
- Tenant membership bootstrap now syncs identities structurally, but staging still needs seeded membership records and onboarding flows for first-time tenants.
|
|
- Post-onboarding CRUD now covers services, staff, locations, blocked days, working hours, and customers; class/session template management and per-staff availability rules still have no management UI.
|
|
- Stripe lifecycle sync is implemented; still needs live environment validation against real products, price IDs, and webhook signatures.
|
|
- Real email/SMS provider integrations are not implemented yet; current delivery providers are safe noops.
|
|
- The internal reminder dispatch route exists, but Railway scheduling and secret rotation still need production setup.
|
|
|
|
## Next Build Targets
|
|
- replace preview-mode dashboard hydration with fully validated Neon Auth JWT flow
|
|
- sync Neon Auth identities into tenant membership records and onboarding bootstrap
|
|
- validate Stripe checkout/webhooks/portal and reminder dispatch against live staging infrastructure
|
|
- replace noop notification providers with the first production email transport and a gated SMS transport
|
|
- add management UI for class/session templates and per-staff availability rules so service duration, capacity, and staff schedules are editable after onboarding
|
|
|
|
## Product Principles
|
|
- calm, premium, structured UX
|
|
- production-grade architecture
|
|
- no generic SaaS clutter
|
|
- deterministic scheduling behavior
|
|
- minimal operational complexity in v1
|
|
- build for maintainability, not feature sprawl
|
|
|
|
## Success Criteria For v1
|
|
Bookra is successful when a small business can:
|
|
- create its business profile
|
|
- configure staff, locations, and services
|
|
- publish a booking page
|
|
- accept bookings without double-booking
|
|
- send reminders reliably
|
|
- manage cancellations and waitlists
|
|
- view simple business health metrics
|
|
- subscribe to a paid plan through Stripe
|
|
|
|
## Final Positioning
|
|
Bookra is not an ERP, not a medical system, and not a marketplace.
|
|
|
|
It is a focused booking operating layer for small local service businesses that need:
|
|
- simple setup
|
|
- reliable scheduling
|
|
- clear reminders
|
|
- lightweight reporting
|
|
- clean SaaS billing
|