Javis OS
Javis OS is an Agentic AI that assists entrepreneurs in business tasks, helping them grow their work.
AI translation
Free booking pages for mobile detailers and appointment businesses. Clients pick a package, choose a time in your area and pay a deposit.
BusinessTypeScript · CSSGitHub @Apex-maker15OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
Micro-SaaS booking for independent beauty pros (nail techs, lash artists, barbers) and mobile services (detailers, groomers). Clients pick a service and a slot, pay a deposit or the full price with Stripe, and the appointment lands on the provider's dashboard in real time.
Stack: Next.js 16 (App Router, React 19) · Tailwind CSS · Framer Motion · Lucide · Prisma 6 (engine-less, pg driver adapter) · PostgreSQL · NextAuth.js v5 · Stripe (Payment Element + webhooks) · Zustand · Server-Sent Events with Postgres LISTEN/NOTIFY.Read the full README ↗Micro-SaaS booking for independent beauty pros (nail techs, lash artists, barbers) and mobile services (detailers, groomers). Clients pick a service and a slot, pay a deposit or the full price with Stripe, and the appointment lands on the provider's dashboard in real time.
Stack: Next.js 16 (App Router, React 19) · Tailwind CSS · Framer Motion · Lucide · Prisma 6 (engine-less, pg driver adapter) · PostgreSQL · NextAuth.js v5 · Stripe (Payment Element + webhooks) · Zustand · Server-Sent Events with Postgres LISTEN/NOTIFY.
Everything in this stack is open source or has a free tier. Stripe only takes a per-transaction fee when real money moves; test mode is free.
npm install
cp .env.example .env # defaults already point at the local PGlite database
npm run db:local # terminal 1 - WASM Postgres on 127.0.0.1:5433
npm run db:setup # terminal 2 - apply migration + seed two demo providers
npm run dev # http://localhost:3000
Demo logins (password password123):
| Provider | Booking page | |
|---|---|---|
| Polished by Apex (nails, GBP, 30% deposit) | [email protected] | /book/polished-by-apex |
| Shine Mobile Detailing (car) | [email protected] | /book/shine-mobile |
| Paws on Wheels Grooming (pet) | [email protected] | /book/paws-on-wheels |
Open the dashboard in one tab and the booking page in another - a new booking slides onto the board without a refresh.
.env.stripe listen --forward-to localhost:3000/api/webhooks/stripe
Paste the printed whsec_... into STRIPE_WEBHOOK_SECRET.4242 4242 4242 4242, any future expiry, any CVC. The webhook flips the booking to PAID and the dashboard glows.Without keys the app still runs end-to-end; the payment step shows a setup notice and the hold is kept.
| Piece | Free option | Notes |
|---|---|---|
| App | Vercel Hobby | npm run build runs prisma generate first. Set every variable from .env.example. |
| Database | Supabase free project | Use the direct connection string (session mode, port 5432). LISTEN/NOTIFY then works across serverless instances. |
| or Neon free project | Also fine. Realtime auto-detects that NOTIFY is unavailable and the dashboard still updates via SSE + 15 s polling. | |
| Payments | Stripe | No monthly fee. Add a webhook endpoint for https://<your-app>/api/webhooks/stripe with events payment_intent.succeeded, payment_intent.canceled, charge.refunded. |
Steps:
# once, against the production database
DATABASE_URL="postgresql://..." npx prisma migrate deploy
DATABASE_URL="postgresql://..." npx prisma db seed # optional demo data
Then import the repo into Vercel, paste the env vars, deploy. Set NEXT_PUBLIC_APP_URL to the Vercel URL.
prisma/schema.prisma)User - PROVIDER or CUSTOMER. Providers carry their business profile: slug, category (nails/beauty, hair, detailing, grooming, other), timezone, currency, locationMode (clients come to a studio vs. provider travels), studioAddress, depositPercent (100 = full prepay), slotIntervalMinutes, bufferMinutes, minNoticeMinutes, bookingHorizonDays. Customers are created as guests on their first booking.Service - name, duration, price in cents.Booking - PENDING → PAID | CONFIRMED | CANCELLED, UTC instants, price snapshot (amountCents) plus what was actually charged (depositCents), paymentIntentId.Availability - one row per weekday with { windows: [{start,end}], breaks: [{start,end}] } in the provider's local time.StripeEvent - processed webhook ids for idempotency./book/<slug> is the provider's own page, not a GlideBook form: cover and logo (uploaded from Your page in the dashboard, resized in the browser and stored inline as data URLs so no image host is needed), tagline, bio, Instagram, up to six work photos, a brand colour that re-tints the whole page (src/lib/color.ts derives light/dark "strong" shades), hours from availability, service areas, policies, and an embedded map for studio providers. Clients rate a booking from their manage link once the appointment has passed (POST /api/bookings/:id/review, one per booking); the average and latest reviews sit above the booking form.
Light and dark are CSS-variable themes switched by data-theme on <html> (src/components/theme/). The choice is saved in localStorage, defaults to the OS preference, and is applied by a beforeInteractive script so there is no flash. Tailwind's white/black are aliased to theme tints so existing bg-white/[0.06]-style classes work in both themes.
User.serviceAreas is free text shown on the booking page ("We come to you across Phoenix, Scottsdale..."). User.serviceAreaCodes is an optional comma-separated list of ZIP/postcode prefixes; when set, the booking form asks for the client's code and both the client (inline) and POST /api/bookings (OUT_OF_AREA) reject anything outside it. Matching is prefix-based and ignores case and spaces (src/lib/service-area.ts).
src/lib/slots.ts)Pure function. For a calendar day it walks each working window at slotIntervalMinutes, keeps a start t only if t + duration fits the window, does not intersect a break, respects the buffer around every existing booking, and is inside the notice/horizon range. All wall-clock maths goes through date-fns-tz, so DST is handled.
POST /api/bookings re-runs the same engine inside a transaction holding pg_advisory_xact_lock(hashtext(providerId)), so two customers can never take the same slot. Unpaid PENDING holds expire after BOOKING_HOLD_MINUTES; their PaymentIntents are cancelled lazily on the next read, no cron needed.
GlideBook is free for providers: PLATFORM_FEE_PERCENT defaults to 0 and the done-for-you setup has no fee (SETUP_FEE_CENTS = 0). Both can be switched on later without code changes. Deposits reach the provider one of three ways, chosen automatically per provider:
PAID. Refunds are between them.Optional revenue streams, both automatic once enabled:
/dashboard/payments (Stripe Express hosted onboarding, ~2 minutes on a phone). Deposits are created as destination charges on the platform account with transfer_data.destination = the provider and application_fee_amount = PLATFORM_FEE_PERCENT of the deposit (floor PLATFORM_FEE_MIN_CENTS). Stripe pays the provider out; the fee stays on the platform balance. Refunds reverse the transfer and the fee. Until a provider's account has charges_enabled, their page runs in pay-on-the-day mode: bookings confirm instantly with no card step, so the product is usable from minute one and the dashboard nudges them to connect.Enable Connect once in the Stripe dashboard (Connect → Get started → platform). Test mode needs no activation.
POST /api/checkout creates (or reuses) a PaymentIntent for the booking's snapshotted amount with an idempotency key, and returns the clientSecret.<PaymentElement> and confirms with redirect: "if_required". Redirect-based methods land on /book/return.POST /api/webhooks/stripe verifies the signature on the raw body, records the event id, then marks the booking PAID (or CANCELLED on cancel/refund) and publishes a realtime event./admin)The owner's email (DEFAULT_ADMIN_EMAILS in src/lib/admin.ts, extendable with ADMIN_EMAILS) sees an Owner link: platform revenue and deposits per currency, every provider with their Stripe state and activity, the latest bookings across the platform, and a per-provider workbench (/admin/providers/:id) with that provider's bookings and the same services / hours / settings editors, acting on their behalf via ?providerId=.
/dashboard/setup, /admin/setup)Providers can build their page themselves for free, or pay a one-off fee (GBP 5, SETUP_FEE_CENTS in src/lib/admin.ts) and paste their price list. The fee is a Stripe PaymentIntent with metadata.kind = "setup"; the webhook marks the SetupRequest paid. Emails listed in ADMIN_EMAILS see an Admin link with the queue: pick a request, edit that provider's services, hours and settings in place (the dashboard APIs accept ?providerId= for admins), then mark it done.
src/lib/email.ts sends over any SMTP provider (SMTP_URL, EMAIL_FROM); templates in src/emails/ are React Email components. Without SMTP configured, sends are logged and skipped..ics invite and the provider gets a new-booking alert. Cancellations notify both sides./api/cron/reminders runs daily via vercel.json (set CRON_SECRET) and emails clients whose appointment is 12-40 hours away./b/<bookingId>?t=<manageToken>, where the client can add the appointment to Google Calendar or cancel; cancelling outside cancelNoticeHours refunds the deposit automatically./book/<slug>/opengraph-image renders a branded preview card for links shared on Instagram, WhatsApp and iMessage.src/lib/realtime.ts, /api/events)The dashboard opens an EventSource. Server side, publish() emits to an in-process bus and, when the database provably delivers notifications (a probe is sent after LISTEN), through Postgres NOTIFY so every server instance sees every event. If the stream cannot be established the client falls back to 15 s polling, and it reconciles every 60 s and on tab focus regardless.
AnimatePresence mode="popLayout" with direction-aware slide + blur variants; the container animates layout so height changes never jump.layoutId so the active state morphs between elements.src/components/motion.ts), low stiffness / high damping.AnimatePresence mode="wait"; the Stripe iframe has a reserved min-height to avoid CLS.h1/h2); icons come only from src/components/icons.ts (Phosphor, duotone, hook-free SSR build so one import works in server and client components); no drop shadows, gradients, hover lifts or pill badges; corners are 3 to 8px (borderRadius scale in tailwind.config.ts); status colours are the theme-aware ok/warn/bad/info tokens rather than Tailwind's palette./terms and /privacy are linked from every footer; SUPPORT_EMAIL sets the contact address shown there.| Script | What it does |
|---|---|
npm run dev | Next.js dev server (Turbopack) |
npm run dev:webpack | Dev server with webpack - use on Windows-on-ARM / machines that block native binaries |
npm run build / start | Production build / serve |
npm run db:local | Zero-install local Postgres (PGlite) on port 5433 |
npm run db:setup | prisma migrate deploy + seed |
npm run db:migrate:dev | Create a new migration after editing the schema |
npm run db:studio | Prisma Studio |
npm run typecheck / lint | tsc --noEmit / ESLint |
engineType = "client" + @prisma/adapter-pg), which keeps serverless bundles small and avoids platform-specific binaries.src/proxy.ts (Next 16's middleware) protects /dashboard and bounces signed-in providers away from /login.