WallpaperMachine
당신이 사랑하는 배경화면, 맥에서 새롭게 만나보세요
Vig Barber는 온라인 이발소 예약 플랫폼으로, 이발소와 서비스·전문가를 선택해 시간을 예약한다
Vig Barber é a plataforma de agendamento online para barbearias. Encontre uma barbearia, escolha o serviço e o profissional e reserve seu horário.
라이프스타일TypeScript · CSSGitHub @Gabriel-Vignoli온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기A booking platform for barbershops — browse services, schedule appointments, and manage bookings. Built with Next.js 16, React 19, Prisma ORM, and PostgreSQL (Supabase).
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗A multi-tenant SaaS for barbershops, built with Next.js, TypeScript, Prisma and PostgreSQL. Many shops run on one deployment, each at its own URL (/b/<slug>) with its own data, branding and staff. Clients have one global account: they find a shop on the landing page and book a service with a specific barber. Staff work in role-specific areas of their shop, and shops pay a monthly subscription through Stripe.
Live: vig-barber.vercel.app (billing runs in Stripe test mode, so use the test card 4242 4242 4242 4242)
For clients
For shop staff (roles come from a membership in each shop, so one person can be an admin in one shop and a client in another)
/painel ("Minhas barbearias") lists every shop a person works atFor the platform
/platform) to create, suspend and reactivate shops, invite owners, see each shop's billing status and extend trialsBilling
pg driver adapterslug (or from a row the user already owns, like their booking). A barbershopId sent by the browser is never trusted.barbershopId. Updates and deletes filter by both the record id and the shop id, then check that a row was actually changed.Membership per user and shop, and are read from the database on every request. Platform admins get no implicit access to shops.The full design and the migration from a single-shop app are in docs/multi-tenant-plan.md; billing is in docs/billing-plan.md.
git clone https://github.com/Gabriel-Vignoli/vig-barber.git
cd vig-barber
# Also generates the Prisma client into app/generated/prisma
npm install
cp .env.example .env
Fill in .env. Only the database and NextAuth variables are required to start; the other services turn themselves off while their keys are missing.
| Variable | What it is for |
|---|---|
DATABASE_URL | Transaction pooler (port 6543), used by the app |
DIRECT_URL | Session pooler (port 5432), used by the Prisma CLI |
NEXT_AUTH_SECRET | NextAuth session secret |
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET | Google login |
RESEND_API_KEY, EMAIL_FROM | Password reset and invitation emails |
APP_URL | Absolute origin for links in emails (custom domain) |
UPLOADTHING_TOKEN | Shop logo and banner uploads |
CONTACT_EMAIL, CONTACT_WHATSAPP | Contact buttons on the landing page (hidden when unset) |
STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, STRIPE_PRICE_BASIC | Subscriptions and the webhook |
# Apply migrations and seed 3 demo shops
npx prisma migrate dev
npx prisma db seed
npm run dev
Open http://localhost:3000. The demo users (a platform admin, shop owners, barbers, a front-desk user and clients) are listed in prisma/seed.ts. You choose their logins in .env: SEED_ADMIN_EMAIL and SEED_ADMIN_PASSWORD for the platform admin, SEED_PASSWORD for everyone else. A password left empty is generated at random and printed at the end of the seed.
Warning: the seed deletes every row in the database before it inserts the demo data. Never run it against a database with real shops.
To test billing locally, forward Stripe events to the webhook and put the secret it prints in STRIPE_WEBHOOK_SECRET:
stripe listen --forward-to localhost:3000/api/stripe/webhook --events checkout.session.completed,customer.subscription.created,customer.subscription.updated,customer.subscription.deleted,invoice.paid,invoice.payment_failed
Prisma 7:
migrate devno longer runsprisma generateor the seed. After you changeprisma/schema.prisma, runnpx prisma generateyourself. The CLI reads its settings fromprisma.config.ts.Connection errors? Some networks (school or corporate wifi) block port 6543. If you get P1017 (
Server has closed the connection) locally, try another network or point your localDATABASE_URLat the 5432 URL.
npm run test
The suite (400+ tests) runs against a throwaway PostgreSQL started by embedded-postgres, with the real migrations applied, so it needs no Docker and never touches your .env database. It covers cross-shop isolation for every mutation, booking rules and availability, reviews, invitations, plan limits, the billing gate and the Stripe webhook sync.
Before pushing: npm run lint, npm run build and npm run test.
vig-barber/
├── app/
│ ├── (landing)/ # Platform landing page and shop finder at /
│ ├── b/[slug]/ # One shop
│ │ ├── (client)/ # Shop home, service and barber pages
│ │ ├── admin/ # Admin panel (dashboard, services, staff, expenses, billing...)
│ │ ├── atendente/ # Front-desk walk-in screen and bookings
│ │ ├── staff/ # Barber's own bookings
│ │ └── assinatura-pendente/ # Shown to staff while the shop is unpaid
│ ├── platform/ # Super admin panel
│ ├── painel/ # "Minhas barbearias"
│ ├── convite/[token]/ # Accept an invitation
│ ├── login/ bookings/ profile/
│ ├── api/ # NextAuth, UploadThing and the Stripe webhook
│ ├── _actions/ # Server Actions, one per file
│ ├── _data/ # Server-only data fetchers used by pages
│ ├── _lib/ # Tenant helpers, auth, billing, Stripe, time logic, validations
│ └── components/ # Feature components (ui/ holds the shadcn primitives)
├── prisma/ # Schema, migrations and seed
├── tests/ # Vitest suite
├── docs/ # Multi-tenant and billing plans
├── prisma.config.ts # Prisma CLI config
└── proxy.ts # Requires a session on protected routes (roles are checked by pages)
Date math.{ ok, data } or { ok: false, error } instead, so the Portuguese messages reach the user.authorize(), so failed attempts are tracked by a separate Server Action called before sign-in.Decimal serialization: Prisma Decimal values are converted to number before they reach Client Components.Live and in active development. Billing still runs in Stripe test mode while the first shops try the system.