Why WashBay
What makes it better
than the alternatives
- 01
Live wash board over SignalR
Every bay updates in real time across web panel and staff tablets with job status, assigned washer and customer timeline
- 02
Dual Flutter apps from one codebase
Customer booking app (main.dart) and staff tablet app (main_staff.dart) share auth, network and domain logic
- 03
Branch-specific pricing
Services have a base price per vehicle type, with optional branch overrides and a cached menu (CatalogCache) invalidated on catalog changes
- 04
Guided demo mode
In-browser sample data (demo/store.js) and a chapter-based product tour (tour/provider.js) that plays without server calls, controlled by ProductionGuards.cs
- 05
Plain JavaScript web panel
No build step; ES modules (core/api.js, ui.js, resource.js) and Razor Pages render tables and forms with Tailwind CSS
Customer Mobile App (Flutter)
- Email/password registration with OTP verification (AuthRepository
- LoginCubit
- OtpCubit)
- My vehicles list with default selection (VehiclesScreen
- VehicleRepository)
- Branch list sorted by GPS distance with hours
- directions and call (BranchesScreen
- LocationHelper)
- Service selection with live price including VAT
- package and add-on support (SelectServicesScreen
- BookingFlowCubit)
- Real-time slot availability from SlotCalculator
- 5-minute hold during checkout (SelectSlotScreen
- BookingRepository)
- Stripe hosted checkout or pay-at-counter
- PDF invoice download (SummaryScreen
- PaymentService
- InvoicePdf)
- Live wash timeline: checked in → washing → quality check → ready → picked up (BookingDetailScreen
- LiveSession widget)
- Ratings and reviews after completion (RatingsController)
- In-app notifications list and optional Firebase push (NotificationsScreen
- PushService)
- English and Arabic with full RTL support (LocalizationCubit
- assets/language/ar.json)
Staff Tablet App (Flutter)
- One-time pairing with manager email/password
- then 4-digit PIN login (PairScreen
- PinScreen
- StaffAuthController)
- PIN lock after 5 wrong attempts for 15 minutes (Operations:PinMaxAttempts
- PinLockMinutes)
- Live board with lanes per bay
- drag jobs between statuses (BoardScreen
- BoardCubit
- LaneColumn widget)
- QR code scanner for customer check-in (ScanScreen
- JobCubit.checkIn)
- Job detail drawer: customer
- vehicle
- services
- assign washer
- before/after photo upload (JobScreen
- JobSections)
- Walk-in booking: select vehicle type and services
- see price and free slots
- capture customer details (WalkInScreen
- WalkInCubit)
- Daily cash closing: counter payments total and submit (CashController)
Web Admin Panel (Razor Pages + JS)
- Dashboard: today's bookings
- revenue and average rating (Pages/Panel/Dashboard.cshtml
- pages/dashboard.js)
- Live board: all branches
- real-time job updates over SignalR (Pages/Panel/Live.cshtml
- core/live.js
- LiveHub)
- Countries: add countries with currency code and VAT rate (Pages/Panel/Countries.cshtml
- OrgController.CreateCountry)
- Branches: name
- address
- time zone
- opening hours
- holidays
- bays with category limits (Pages/Panel/Branches.cshtml
- components/branch-form.js)
- Catalog: vehicle types
- categories
- services
- packages
- add-ons with translatable names (Pages/Panel/Catalog.cshtml
- CatalogController)
- Price board: base price and duration per vehicle type
- branch overrides (components/price-board.js
- CatalogController.UpdatePrices)
- Branch menu: turn services on/off per branch (CatalogController.UpdateBranchMenu
- CatalogCache invalidation)
- Bookings list: filter by branch and day
- cancel when needed (Pages/Panel/Bookings.cshtml
- BookingsController)
- Staff management: create accounts
- set roles (Super Admin
- Country Admin
- Branch Manager
- Front Desk
- Supervisor
- Washer)
- set/clear PINs
- unpair tablets (Pages/Panel/Staff.cshtml
- StaffAdminController
- StaffAppController)
- Ratings: reviews list with summary stats (Pages/Panel/Ratings.cshtml
- RatingsController)
- Reports: daily branch report with bookings
- walk-ins
- no-shows
- cancellations
- cash/card/online revenue
- average stars
- CSV export (Pages/Panel/Reports.cshtml
- ReportsController)
- Guided product tour: 8 chapters (tour/chapters/)
- in-browser demo data (demo/store.js)
- skippable and resumable (tour/provider.js
- AuthController.UpdateTours)
- Session: signed httpOnly cookies with Data Protection
- auto-refresh (Panel/PanelSessionFilter.cs
- PanelAuthController)
- English and Arabic UI (Panel/i18n/ar.json
- core/i18n.js)
Booking Engine
- Quote calculation: services + package + add-ons
- VAT included (BookingsController.Quote
- QuoteService)
- Slot generation: SlotCalculator reads branch hours
- holidays
- bay availability and existing bookings to return free times
- 5-minute hold: BookingHold entity locks a slot while customer confirms (Booking:HoldMinutes)
- Booking creation: validates hold
- creates Booking with line items
- sends confirmation (BookingsController.Create)
- Reschedule: cancel cutoff enforced (Booking:CancelCutoffMinutes)
- new slot hold and swap (BookingsController.Reschedule)
- Cancellation: allowed until cutoff
- refund if paid online (BookingsController.Cancel)
- Max active bookings per customer (Booking:MaxActiveBookings)
- Walk-in bookings: staff create on-the-spot with immediate check-in (StaffAppController.CreateWalkIn)
Operations & Job Flow
- Check-in: QR code or typed code
- validates early check-in window (Operations:CheckInEarlyMinutes)
- creates Job entity (StaffAppController.CheckIn)
- Job statuses: Pending → InProgress → QualityCheck → Ready → PickedUp (JobStatus enum
- JobRules.CanTransition)
- Washer assignment: supervisor or front desk assigns staff
- tracked in Job.AssignedStaffId (StaffAppController.AssignWasher)
- Before/after photos: required by default (Operations:RequireBeforePhotos
- RequireAfterPhotos)
- uploaded to storage/uploads (FilesController
- JobPhotosService)
- No-show detection: manual mark or auto after 2 hours (Operations:NoShowAfterMinutes
- AutoNoShowAfterMinutes
- NoShowWorker background service)
- Live updates: SignalR LiveHub broadcasts job changes to web panel and tablets (LiveNotifier
- LiveHub.SubscribeToJobs)
Payments & Invoicing
- Payment providers: None
- Demo (try-out only with DemoPayToken signed links)
- Stripe (PaymentOptions.cs
- OnlinePaymentProviders)
- Stripe integration: hosted checkout session
- webhook signature verification (StripePaymentProvider
- StripeWebhookController)
- Demo payments: simulated success/failure with signed expiring tokens
- blocked in production (ProductionGuards.cs
- DemoPaymentProvider)
- Invoice generation: QuestPDF creates PDF with company details
- line items
- subtotal
- VAT
- total (InvoicePdf
- InvoiceService)
- Invoice storage: saved to storage/invoices
- downloadable by customer (PaymentsController.DownloadInvoice)
- Payment methods: Online (Stripe)
- Counter (cash/card at branch)
- tracked per booking (PaymentMethod enum)
Multi-Tenancy & Access Control
- Countries: separate currency and VAT rate
- Country entity (OrgController)
- Branches: belong to one country
- have time zone
- hours
- holidays
- bays (Branch entity)
- Role-based access: Super Admin (all countries)
- Country Admin (one country)
- Branch Manager (one branch)
- Front Desk/Supervisor/Washer (branch staff)
- Customer (Roles.cs
- AuthPolicies)
- Access scoping: TenantContext and AccessScope limit staff to their country or branch (ClaimsUser.CountryId
- BranchId)
- Policy enforcement: [Authorize(Policy = AuthPolicies.Panel)] for web panel
- [Authorize(Policy = AuthPolicies.Operations)] for staff tablet (AuthPolicies.cs)
- Branch filtering: operations pages (bookings
- cash
- reports
- ratings
- live) filter by branch (core/branch-picker.js)
Notifications
- In-app notifications: stored in Notification entity
- paginated list (NotificationsController
- NotificationRepository)
- Push delivery: Log (development) or FCM (production) via Notifications:PushProvider (PushService
- FcmPushService)
- Notification types: BookingConfirmed
- BookingCancelled
- BookingReminder
- JobStatusChanged
- PaymentReceived (NotificationType enum)
- User preferences: opt-in push
- device token registration (NotificationsController.RegisterDevice)
- Retention: keeps last 100 per user (Notifications:KeepPerUser)
Catalog & Pricing
- Vehicle types: Car
- SUV
- Truck
- Van
- Motorcycle (VehicleType entity
- seeded in CatalogSeeder)
- Service categories: Exterior
- Interior
- Detailing
- Protection (ServiceCategory entity)
- Services: translatable name/description (Localized<string>)
- base price and duration per vehicle type (Service entity
- ServicePrice)
- Packages: bundles of services with discount (Package entity
- PackageService)
- Add-ons: optional extras (AddOn entity)
- Branch menu: services enabled/disabled per branch (BranchService entity
- CatalogController.UpdateBranchMenu)
- Price overrides: branch-specific prices (BranchServicePrice entity
- CatalogController.UpdatePrices)
- Catalog cache: in-memory branch menu
- invalidated on any catalog change (CatalogCache
- CatalogService.InvalidateCache)
Security & Production Guards
- JWT tokens: access (60 min) and refresh (30 days) with HS256 signing (TokenService
- Jwt:SigningKey)
- Password hashing: Argon2id with salt (PasswordService)
- OTP verification: 6-digit code
- 5 attempts
- 5-minute expiry
- 30-second resend cooldown (OtpService
- Otp:* settings)
- Production guards: refuses to start in Production with DemoSite=true
- OTP demo mode
- demo passwords or Demo payment provider (ProductionGuards.cs)
- Rate limiting: 20 req/min/IP for auth routes
- 10 req/15min/device for PIN login (RateLimitPolicies
- Program.cs)
- CORS: configurable allowed origins (Cors:AllowedOrigins)
- Panel session: signed httpOnly cookies
- auto-refresh
- same-origin [RequirePanelHeader] (PanelSessionFilter
- PanelAuthController)
- Stripe webhook: HMAC signature verification before processing (StripeWebhookController)
- Authorization tests: EndpointAuthorizationTests.cs ensures every mutating route has a role or is reviewed as public
| Language | Version | Framework | Used for | Share of code |
|---|---|---|---|---|
| C# | — | — | Web, API, admin | 40.1% |
| Dart | — | — | Mobile app | 27.7% |
| JavaScript | — | — | Front-end | 19.2% |
| CSS | — | — | Styling | 6.6% |
| SQL | — | — | Database | 2.9% |
| HTML | — | — | Front-end | 1.8% |
| C# (Razor) | — | — | Web pages | 1.2% |
| Java | — | — | API | 0.2% |
| Swift | — | — | Mobile app | 0.1% |
| Shell | — | — | Scripts | 0.1% |
| Kotlin | — | — | Mobile app | 0% |
- Primary language
- C# 13 / .NET 10
- Framework
- ASP.NET Core 10 + Flutter 3.35
- Database
- Microsoft SQL Server 2019+
- Authentication
- JWT + refresh tokens, OTP email verification, 4-digit PIN for staff tablets
- Licence
- Single-site
- Screens
- 28
- Database tables
- 24
- Supported languages
- English, Arabic (full RTL)
- Browser support
- Modern browsers (Chrome, Firefox, Safari, Edge)
| Minimum | Recommended | |
|---|---|---|
| CPU | 2 vCPU | 4 vCPU |
| RAM | 4 GB RAM / 20 GB SSD | 8 GB RAM / 40 GB SSD |
| OS | Linux or Windows Server, .NET 10 runtime, SQL Server 2019+ | Linux or Windows Server, .NET 10 runtime, SQL Server 2019+ |
| Est. monthly hosting | $35 | $35 |
```bash cp .env.example .env # Edit .env: set MSSQL_SA_PASSWORD, JWT_SIGNING_KEY, ADMIN_PASSWORD docker compose up -d --build # Open http://localhost:8080 ```
```bash # Install .NET 10 SDK and SQL Server 2019+ git clone <repo> cd washbay-vibe96/NET\ Project cp appsettings.json appsettings.Production.json # Edit appsettings.Production.json: set ConnectionStrings:Default, Jwt:SigningKey dotnet user-secrets set "Seed:SuperAdminPassword" "<password>" dotnet ef database update dotnet publish -c Release -o /var/www/washbay sudo systemctl enable washbay sudo systemctl start washbay # Configure nginx reverse proxy to port 5080 ```
```bash az group create --name washbay-rg --location eastus az sql server create --name washbay-sql --resource-group washbay-rg --admin-user sqladmin --admin-password <password> az sql db create --resource-group washbay-rg --server washbay-sql --name WashBay --service-objective S1 az webapp create --resource-group washbay-rg --plan washbay-plan --name washbay-api --runtime "DOTNET:10" az webapp config appsettings set --resource-group washbay-rg --name washbay-api --settings ConnectionStrings__Default="<connection-string>" Jwt__SigningKey="<key>" Seed__SuperAdminPassword="<password>" cd "NET Project" dotnet publish -c Release az webapp deploy --resource-group washbay-rg --name washbay-api --src-path bin/Release/net10.0/publish.zip ```
```bash # Start SQL Server in Docker docker run -d --name washbay-sql -e ACCEPT_EULA=Y -e MSSQL_SA_PASSWORD='YourStrong!Pass123' -p 1433:1433 mcr.microsoft.com/mssql/server:2022-latest cd "NET Project/WashBay.Api" dotnet user-secrets set "ConnectionStrings:Default" "Server=localhost,1433;Database=WashBay;User Id=sa;Password=YourStrong!Pass123;TrustServerCertificate=True;MultipleActiveResultSets=True" dotnet user-secrets set "Jwt:SigningKey" "$(openssl rand -base64 48)" dotnet user-secrets set "Seed:SuperAdminPassword" "Admin123!" dotnet ef database update dotnet run # Open http://localhost:5080 ```
Quality report
Manually reviewedHow to read the quality report
What this is. Every project on Vibe96 goes through an automated audit of its source code before a reviewer in Dubai checks it by hand. The score is out of 100; 75 is the minimum to be listed, and any single failing check blocks a listing regardless of the score.
- Security
- No real credentials in the code, every data-changing route protected, admin areas role-gated, no SQL built from user input.
- Code structure
- A developer can navigate and extend it: clear module boundaries, no giant files, little copy-paste.
- Docs & completeness
- The setup guide, example config, schema and required files the builder committed to are actually in the archive.
- Dependency health
- Packages pinned, no known critical vulnerabilities, no abandoned libraries in production paths.
- Build & deploy
- A clean machine can restore, build and start it by following the shipped instructions.
Warnings are problems that didn't block the listing; they stay visible here until a later version clears them. The audit reads the code — it does not run it — so always try the live demo before you buy. Lines of code are counted from the same audited archive.
Reviewer notes (4 minor)
Database access is safe; minor schema gaps (missing indexes on some foreign keys); no critical vulnerabilities; flutter and .net dependencies are outdated but not exploitable; weak default passwords in example files; no real credentials found; strong auth guards on mutating endpoints; minor ownership gaps on read-only customer data; moderate duplication and some oversized files; structure is sound but maintainability could improve; build reproducible with gaps: missing flutter config step, unclear sql setup, no .net restore verification. None of these stop the software from working; the developer has been told and improvements come in a future update.
Versions & updates
No updates yet. New versions appear here with a changelog.
Buyers receive updates free for 1 year.
What you get
- </>Full source codeThe complete audited source, delivered as a download from your account. No obfuscation.
- ▤DocumentationSetup guide, architecture notes, CLAUDE.md and AGENTS.md.
- ↻One year of updatesEvery new version the builder publishes, free for 12 months.
- ♧30 days of supportInstallation and defect support through Vibe96 tickets.