尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
Pet sitting & dog walking marketplace — Toronto first, Canada-wide later.
Next.js 16 (App Router) · Tailwind CSS 3 (design tokens from the Stitch "Warm Paw Companion" system) · Prisma · Supabase (Postgres, Auth, Storage).
Manual alternative: copy .env.example to .env, then npx prisma migrate deploy && npm run db:seed.
Demo accounts — each has its own random password, written to the git-ignored .demo-credentials file by the seed (or rotate them any time with node --env-file=.env node_modules/.bin/tsx scripts/rotate-demo-passwords.ts):檢視完整 README ↗Pet sitting & dog walking marketplace — Toronto first, Canada-wide later. Next.js 16 (App Router) · Tailwind CSS 3 (design tokens from the Stitch "Warm Paw Companion" system) · Prisma · Supabase (Postgres, Auth, Storage).
npm install
# fill .env.supabase (DB password + access token, or keys + connection strings), then:
npm run supabase:setup # writes .env, sets Auth URLs, runs migrations, seeds demo data
npm run dev -- -p 3100
Manual alternative: copy .env.example to .env, then npx prisma migrate deploy && npm run db:seed.
Demo accounts — each has its own random password, written to the git-ignored .demo-credentials file by the seed (or rotate them any time with node --env-file=.env node_modules/.bin/tsx scripts/rotate-demo-passwords.ts):
| Account | Role | What to try |
|---|---|---|
[email protected] | Pet parent | /account/bookings (upcoming, completed + reviewed), /account/pets, /messages, book Sarah |
[email protected] | Sitter | /sitter — 2 pending requests to accept/decline, profile & rates editor |
[email protected] | Admin | /admin — approve application WS-31877, cities, fees, users, reviews |
Other sitters log in as <slug>@wagstays.ca; extra owners: [email protected], [email protected], [email protected].
| Path | What |
|---|---|
src/app/(site)/ | Public pages (header + footer layout) |
src/app/actions/ | Server actions (auth, favourites, booking, applications…) |
src/lib/queries.ts | Data access used by pages |
src/lib/pricing.ts | Single source of truth for booking prices (fees, WagPoints, HST) |
src/lib/session.ts | Local JWT cookie auth — to be replaced by Supabase Auth |
prisma/schema.prisma | Data model (Postgres-compatible; enum-like fields are strings) |
docs/LOCALIZATION.md | TR design → EN-CA copy, names, places, prices |
src/app/admin/ | Admin panel (requireAdmin): dashboard, applications, sitters, bookings, users, reviews, cities, settings, audit log |
src/app/(site)/account/ · sitter/ · messages/ | Pet parent area · sitter dashboard · messaging |
src/lib/auth.ts | requireUser / requireSitter / requireAdmin guards (every server action re-checks) |
src/lib/booking-lifecycle.ts | Allowed booking status transitions per actor, WagPoints refunds, rating recompute |
src/lib/settings.ts | Platform fees from PlatformSettings (edited in /admin/settings) |
src/lib/audit.ts | Admin audit trail |
src/proxy.ts | Redirects signed-out visitors away from private areas |
PENDING (owner requests) → CONFIRMED / DECLINED (sitter or admin) → COMPLETED (sitter or admin, after start) → owner can review.
CANCELLED by the owner (pending/confirmed), the sitter (confirmed) or an admin. WagPoints used are refunded on cancel/decline.
One pipeline, used by the profile widget, checkout, the booking server action (authoritative — it recomputes from DB prices) and /pricing:
src/lib/quote.ts → quoteBooking({ service, pets, dates, holidays }) — the sitter's part: base rate × units, extra pets (additionalPetPriceCents per pet after the first, per unit; null = one pet per booking, limit petLimit() = maxPetsPerBooking), holiday rate (holidayPriceCents replaces the base on statutory holidays from src/lib/holidays.ts, by City.provinceCode) and puppy surcharge (puppyPriceCents per pet under 1 year). Each line carries a kind (+ holiday name) and is stored in Booking.priceLines.src/lib/pricing.ts → priceBooking({ subtotalCents: quote.subtotalCents, taxRateBps, fees }) — adds the flat WagShield fee and service fee (PlatformSettings), subtracts WagPoints, then tax (City.taxRateBps). Weekly series: one quote + price per occurrence.src/lib/price-details.ts — plain-words copy on top (search badge "Up to 2 dogs · +$12 each", the profile "Price details" list in src/components/pricing/PriceDetails.tsx, line explanations for the InfoTip popovers). Search (searchSitters, petCount) only keeps services where petLimit(service) >= petCount.Demo add-on rates: node --env-file=.env node_modules/.bin/tsx scripts/demo-addon-rates.ts (idempotent, demo sitters only).
Only Toronto is active (City.isActive). Other cities are seeded inactive and are switched on from /admin/cities (a city needs at least one neighbourhood). Active cities automatically appear in the home search suggestions, the search location picker (/sitters?city=<slug>) and the sitter application form; approved sitters are placed in the city of the neighbourhood they applied for. Taxes come from City.taxRateBps, time zones from City.timeZone (date formatting still defaults to Toronto time — pass city.timeZone through when a second time zone goes live).
DATABASE_URL transaction pooler at runtime, DIRECT_URL session pooler for migrations). RLS is enabled on every table with no policies, so the public Data API exposes nothing — all reads/writes go through server code. Enable RLS on every new table in future migrations.User.id = auth.users.id; roles live in our User table, never in user-editable metadata. src/proxy.ts refreshes the session; getCurrentUser() in src/lib/session.ts is the only entry point pages use. Email links land on /auth/callback (PKCE code) or /auth/confirm (token hash). Suspending a user also bans them in Supabase Auth.media (public; pet, sitter and avatar photos) via src/lib/uploads.ts; bucket documents (private) is ready for ID / police-check files — the application form still stores only file names./admin/users/<id>.prisma/sqlite-archive/.GitHub Actions (.github/workflows/ci.yml) runs on every push and pull request to main:
npm ci, npx prisma generate, npx next typegen (route types aren't committed), npm run typecheck, npm run lint, npx next build. It uses dummy DATABASE_URL / DIRECT_URL / public Supabase values, so no secrets are needed — the build must never require a database. Pages that read data are rendered on demand; generateStaticParams and sitemap.ts fall back to an empty / static list when the DB isn't reachable.E2E_BASE_URL is set (e.g. https://wagstays.vercel.app). Installs Playwright Chromium and runs tests/e2e/smoke.spec.ts against that URL: anonymous pages only (home, /sitters, a sitter profile, /become-a-sitter, /login, /terms, /pet-sitters/<city>, sitemap.xml, robots.txt) — status 200, no console errors, valid JSON-LD. No credentials live in the repo and the tests never write data.Locally: npm run typecheck, npm run lint, npm run test:e2e (defaults to production; E2E_BASE_URL=http://localhost:3100 npm run test:e2e for the dev server; first run npx playwright install chromium).
src/app/sitemap.ts / src/app/robots.ts — origin from NEXT_PUBLIC_SITE_URL (see src/lib/seo/site.ts). The sitemap lists static pages, every active sitter in an active city and all neighbourhood landing pages; robots blocks the private areas (which also carry noindex)./pet-sitters/<city>, /pet-sitters/<city>/<hood>, /dog-walkers/<city>/<hood> (active cities only, 404 otherwise). Copy, prices, FAQ and structured data are generated from live data in src/lib/seo/landing.ts; park names per neighbourhood live in its PARKS map — extend it when a new city goes live.src/lib/seo/schema.ts (Organization + WebSite on every public page, LocalBusiness + offers + BreadcrumbList on sitter profiles, ItemList + FAQPage on landing pages).