From 35e0cc7184113d6520a4607b9d5c42f766b40fb5 Mon Sep 17 00:00:00 2001 From: Mao Nakamoto <41178744+catomean@users.noreply.github.com> Date: Sat, 29 Aug 2026 11:24:46 +0200 Subject: [PATCH 1/3] chore: add a formatter, matching the style this repo already writes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit singleQuote=false was chosen by counting this repo's own imports, not by fleet decree. The fleet is genuinely split and the two repos that already had a .prettierrc disagreed with each other, so there was no standard to restore. Quote style does not cross repo boundaries; having a gate does. Markdown is ignored for now — prettier rewraps prose, which would bury the real diff. Co-Authored-By: Claude Opus 5 --- .prettierignore | 21 +++++++++++++++++++++ .prettierrc | 9 +++++++++ package.json | 5 ++++- pnpm-lock.yaml | 10 ++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 .prettierignore create mode 100644 .prettierrc diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..1ed5ea5 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,21 @@ +# Build output and vendored trees — formatting these is noise. +node_modules +.next +dist +build +out +coverage +.turbo +.vercel +*.min.js +*.min.css + +# Lockfiles are generated; prettier would rewrite them wholesale. +package-lock.json +pnpm-lock.yaml +yarn.lock + +# Markdown is deliberately out of scope for now. Prettier rewraps prose, which +# is where it is most opinionated and least useful, and it would bury the real +# diff. Remove this line when you want docs formatted too. +*.md diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..a2f11f0 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,9 @@ +{ + "semi": true, + "singleQuote": false, + "printWidth": 100, + "tabWidth": 2, + "trailingComma": "all", + "arrowParens": "always", + "endOfLine": "lf" +} diff --git a/package.json b/package.json index 1320b53..b4e9fc3 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,9 @@ "db:push": "dotenv -e .env.local -- drizzle-kit push", "db:studio": "dotenv -e .env.local -- drizzle-kit studio", "test": "vitest run", - "verify": "pnpm run lint && pnpm run typecheck && node scripts/ci/check-css-vars.mjs && pnpm run test" + "verify": "npm run format:check && pnpm run lint && pnpm run typecheck && node scripts/ci/check-css-vars.mjs && pnpm run test", + "format": "prettier --write .", + "format:check": "prettier --check ." }, "dependencies": { "@auth/drizzle-adapter": "^1.11.3", @@ -60,6 +62,7 @@ "drizzle-kit": "^0.31.10", "eslint": "^9", "eslint-config-next": "16.3.3", + "prettier": "^3.3.3", "tailwindcss": "^4", "typescript": "^6", "vitest": "^4.1.11" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b119659..6495ab2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -126,6 +126,9 @@ importers: eslint-config-next: specifier: 16.3.3 version: 16.3.3(@typescript-eslint/parser@8.68.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + prettier: + specifier: ^3.3.3 + version: 3.9.6 tailwindcss: specifier: ^4 version: 4.3.3 @@ -4150,6 +4153,11 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + prettier@3.9.6: + resolution: {integrity: sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==} + engines: {node: '>=14'} + hasBin: true + progress@2.0.3: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} @@ -8442,6 +8450,8 @@ snapshots: prelude-ls@1.2.1: {} + prettier@3.9.6: {} + progress@2.0.3: {} prop-types@15.8.1: From e558eb5adb52909a3687017dcbf46895bcc6097c Mon Sep 17 00:00:00 2001 From: Mao Nakamoto <41178744+catomean@users.noreply.github.com> Date: Sat, 29 Aug 2026 11:26:27 +0200 Subject: [PATCH 2/3] style: format with prettier (252 files) Mechanical. No behaviour change. This SHA is listed in .git-blame-ignore-revs so `git blame` skips it. Co-Authored-By: Claude Opus 5 --- .github/workflows/auto-merge.yml | 4 +- app/(admin)/admin/adoptions/page.tsx | 141 ++- app/(admin)/admin/blog/page.tsx | 86 +- app/(admin)/admin/layout.tsx | 25 +- app/(admin)/admin/orders/page.tsx | 64 +- app/(admin)/admin/page.tsx | 75 +- app/(admin)/admin/products/page.tsx | 151 ++- app/(admin)/admin/users/page.tsx | 54 +- app/(auth)/demo/page.tsx | 5 +- app/(auth)/forgot-password/page.tsx | 12 +- app/(auth)/layout.tsx | 34 +- app/(auth)/login/page.tsx | 12 +- app/(auth)/register/page.tsx | 50 +- app/(auth)/reset-password/page.tsx | 16 +- app/(portal)/layout.tsx | 10 +- app/(portal)/not-found.tsx | 4 +- .../portal/adopt/[listingId]/page.tsx | 173 ++- .../portal/adopt/applications/page.tsx | 57 +- app/(portal)/portal/adopt/page.tsx | 80 +- app/(portal)/portal/adoptions/page.tsx | 81 +- app/(portal)/portal/become-a-pro/page.tsx | 31 +- app/(portal)/portal/bookings/page.tsx | 94 +- app/(portal)/portal/checkin/page.tsx | 73 +- app/(portal)/portal/dashboard/page.tsx | 257 ++-- app/(portal)/portal/find/page.tsx | 288 +++-- .../portal/my-products/orders/page.tsx | 44 +- app/(portal)/portal/my-products/page.tsx | 113 +- app/(portal)/portal/orders/page.tsx | 71 +- .../portal/pets/[petId]/edit/page.tsx | 58 +- .../portal/pets/[petId]/health/log/page.tsx | 57 +- .../portal/pets/[petId]/health/page.tsx | 144 ++- .../pets/[petId]/list-for-adoption/page.tsx | 58 +- .../portal/pets/[petId]/medications/page.tsx | 173 ++- app/(portal)/portal/pets/[petId]/page.tsx | 94 +- .../portal/pets/[petId]/records/page.tsx | 147 ++- .../portal/pets/[petId]/vaccinations/page.tsx | 192 ++- app/(portal)/portal/pets/new/page.tsx | 70 +- app/(portal)/portal/pets/page.tsx | 49 +- .../ProfessionalProfileForm.tsx | 32 +- .../portal/professional-profile/page.tsx | 7 +- .../seller-profile/SellerProfileForm.tsx | 32 +- app/(portal)/portal/settings/page.tsx | 553 ++++---- app/(portal)/portal/shop/page.tsx | 123 +- app/[locale]/(marketing)/about/page.tsx | 56 +- app/[locale]/(marketing)/blog/[slug]/page.tsx | 14 +- app/[locale]/(marketing)/blog/page.tsx | 12 +- app/[locale]/(marketing)/features/page.tsx | 154 ++- app/[locale]/(marketing)/layout.tsx | 6 +- .../legal/_components/LegalPage.tsx | 20 +- .../(marketing)/legal/cookies/page.tsx | 38 +- .../(marketing)/legal/privacy/page.tsx | 116 +- app/[locale]/(marketing)/legal/terms/page.tsx | 75 +- app/[locale]/(marketing)/pricing/page.tsx | 200 +-- app/[locale]/(marketing)/pros/page.tsx | 96 +- .../(marketing)/species/[speciesId]/page.tsx | 130 +- .../adopt/[listingId]/opengraph-image.tsx | 166 ++- .../(public)/adopt/[listingId]/page.tsx | 80 +- app/[locale]/(public)/adopt/page.tsx | 61 +- app/[locale]/(public)/find/page.tsx | 195 ++- app/[locale]/(public)/layout.tsx | 6 +- app/[locale]/(public)/pets/[handle]/page.tsx | 87 +- .../pros/[userId]/opengraph-image.tsx | 125 +- app/[locale]/(public)/pros/[userId]/page.tsx | 72 +- .../shop/[productId]/opengraph-image.tsx | 176 ++- .../(public)/shop/[productId]/page.tsx | 18 +- .../(public)/shop/order/[token]/page.tsx | 10 +- app/[locale]/(public)/shop/page.tsx | 35 +- app/[locale]/layout.tsx | 4 +- app/[locale]/opengraph-image.tsx | 202 ++- app/[locale]/page.tsx | 1124 +++++++++-------- app/api/account/export/route.test.ts | 44 +- app/api/account/export/route.ts | 12 +- app/api/account/locale/route.ts | 5 +- app/api/account/role/route.ts | 5 +- app/api/account/route.test.ts | 40 +- app/api/account/route.ts | 31 +- app/api/admin/adoptions/route.test.ts | 33 +- app/api/admin/blog/[postId]/route.test.ts | 16 +- app/api/admin/blog/route.ts | 5 +- app/api/admin/orders/route.test.ts | 63 +- app/api/admin/products/route.test.ts | 34 +- .../professionals/[userId]/route.test.ts | 8 +- app/api/admin/professionals/[userId]/route.ts | 19 +- app/api/admin/users/[userId]/route.test.ts | 6 +- app/api/admin/users/route.test.ts | 14 +- .../[listingId]/applications/route.test.ts | 71 +- .../[listingId]/applications/route.ts | 15 +- .../adoptions/[listingId]/apply/route.test.ts | 29 +- app/api/adoptions/[listingId]/apply/route.ts | 19 +- app/api/adoptions/route.test.ts | 6 +- app/api/adoptions/route.ts | 9 +- app/api/auth/forgot-password/route.ts | 14 +- app/api/auth/reset-password/route.test.ts | 6 +- app/api/auth/reset-password/route.ts | 9 +- app/api/bookings/[bookingId]/route.test.ts | 10 +- app/api/bookings/[bookingId]/route.ts | 58 +- app/api/bookings/busy/route.ts | 5 +- app/api/bookings/route.test.ts | 53 +- app/api/bookings/route.ts | 57 +- app/api/cron/booking-reminders/route.ts | 17 +- app/api/cron/emails/route.test.ts | 24 +- app/api/cron/emails/route.ts | 20 +- app/api/cron/health-alerts/route.test.ts | 16 +- app/api/cron/health-alerts/route.ts | 27 +- app/api/cron/medication-reminders/route.ts | 24 +- app/api/cron/reset-demo/route.ts | 5 +- app/api/cron/resident-checkin/route.ts | 14 +- .../cron/vaccination-reminders/route.test.ts | 18 +- app/api/cron/vaccination-reminders/route.ts | 17 +- app/api/cron/weekly-digest/route.test.ts | 26 +- app/api/groomers/route.ts | 7 +- app/api/health/metrics/[petId]/route.test.ts | 22 +- app/api/health/metrics/[petId]/route.ts | 15 +- .../health/records/[recordId]/route.test.ts | 10 +- app/api/health/records/[recordId]/route.ts | 5 +- app/api/health/records/route.test.ts | 15 +- app/api/health/records/route.ts | 10 +- .../medications/[medicationId]/route.test.ts | 13 +- app/api/medications/[medicationId]/route.ts | 11 +- app/api/medications/route.test.ts | 16 +- app/api/medications/route.ts | 10 +- app/api/orders/[orderId]/pay/route.ts | 6 +- app/api/orders/[orderId]/route.test.ts | 5 +- app/api/orders/route.test.ts | 58 +- app/api/orders/route.ts | 11 +- app/api/orders/seller/route.test.ts | 64 +- app/api/owner-isolation.test.ts | 16 +- app/api/pets/[petId]/avatar/route.test.ts | 14 +- app/api/pets/[petId]/avatar/route.ts | 10 +- app/api/pets/[petId]/route.test.ts | 13 +- app/api/pets/[petId]/route.ts | 11 +- app/api/pets/route.test.ts | 2 +- app/api/pets/route.ts | 10 +- app/api/products/[productId]/route.test.ts | 15 +- app/api/products/route.test.ts | 5 +- app/api/products/route.ts | 2 +- app/api/reviews/route.test.ts | 34 +- app/api/reviews/route.ts | 11 +- app/api/sellers/me/route.ts | 50 +- app/api/shop/order/[token]/pay/route.ts | 5 +- app/api/sitters/me/route.test.ts | 12 +- app/api/sitters/route.test.ts | 5 +- app/api/sitters/route.ts | 7 +- app/api/uploads/product-image/route.ts | 5 +- .../[vaccinationId]/route.test.ts | 15 +- app/api/vaccinations/[vaccinationId]/route.ts | 11 +- app/api/vaccinations/route.test.ts | 12 +- app/api/vaccinations/route.ts | 10 +- app/api/vets/me/route.test.ts | 12 +- app/api/vets/route.test.ts | 7 +- app/api/vets/route.ts | 7 +- app/globals.css | 354 ++++-- app/layout.tsx | 6 +- app/not-found.tsx | 4 +- app/sitemap.ts | 32 +- app/unsubscribe/page.tsx | 26 +- components/LocaleSwitcher.tsx | 16 +- components/portal/AvailabilityManager.tsx | 11 +- components/portal/DigitalTwinCard.tsx | 68 +- components/portal/HealthLogForm.tsx | 35 +- components/portal/HealthTrendChart.tsx | 32 +- components/portal/PageState.tsx | 11 +- components/portal/QuickActions.tsx | 28 +- components/portal/SidebarNav.tsx | 84 +- components/portal/SignalHistoryTimeline.tsx | 20 +- components/sections/MarketingFooter.tsx | 46 +- components/sections/MarketingNav.tsx | 40 +- components/shop/GuestCheckout.tsx | 15 +- components/shop/ProductArt.tsx | 37 +- components/shop/ProductImageField.tsx | 11 +- drizzle/meta/0000_snapshot.json | 250 +--- drizzle/meta/0001_snapshot.json | 338 ++--- drizzle/meta/0002_snapshot.json | 351 ++--- drizzle/meta/0003_snapshot.json | 351 ++--- drizzle/meta/0004_snapshot.json | 351 ++--- drizzle/meta/0005_snapshot.json | 360 ++---- drizzle/meta/0006_snapshot.json | 360 ++---- drizzle/meta/_journal.json | 2 +- eslint.config.mjs | 13 +- hooks/useHealthList.ts | 65 +- i18n/request.ts | 4 +- lib/api/ownership.test.ts | 4 +- lib/api/signal-cache.test.ts | 13 +- lib/auth/cron.ts | 4 +- lib/auth/edge.ts | 3 +- lib/auth/guards.ts | 14 +- lib/auth/index.ts | 19 +- lib/auth/safe-redirect.test.ts | 18 +- lib/auth/unsubscribe-token.test.ts | 6 +- lib/config/adoptions.ts | 102 +- lib/config/app.ts | 3 +- lib/config/design-tokens.ts | 100 +- lib/config/digital-twin.ts | 10 +- lib/config/email.ts | 17 +- lib/config/health-metrics.test.ts | 2 +- lib/config/health-metrics.ts | 79 +- lib/config/health-records.ts | 56 +- lib/config/locales.ts | 36 +- lib/config/medications.ts | 17 +- lib/config/observations.test.ts | 15 +- lib/config/observations.ts | 13 +- lib/config/orders.ts | 72 +- lib/config/pet-signal.ts | 1 - lib/config/products.ts | 10 +- lib/config/professionals.ts | 19 +- lib/config/resident-pet.ts | 41 +- lib/config/species.ts | 19 +- lib/config/uploads.ts | 7 +- lib/config/users.ts | 47 +- lib/config/vaccinations.test.ts | 64 +- lib/config/vaccinations.ts | 19 +- lib/db/schema.ts | 654 +++++----- lib/domain/auth.ts | 7 +- lib/domain/blog-markup.test.ts | 8 +- lib/domain/blog-markup.ts | 14 +- lib/domain/blog.ts | 28 +- lib/domain/digital-twin.test.ts | 57 +- lib/domain/digital-twin.ts | 115 +- lib/domain/email-queue.test.ts | 2 +- lib/domain/health.test.ts | 16 +- lib/domain/inventory.ts | 7 +- lib/domain/orders.test.ts | 13 +- lib/domain/orders.ts | 30 +- lib/domain/pet-signal.test.ts | 8 +- lib/domain/pet-signal.ts | 24 +- lib/domain/pets.ts | 5 +- lib/domain/profile-readiness.test.ts | 19 +- lib/domain/resident-checkin.test.ts | 37 +- lib/domain/resident-checkin.ts | 19 +- lib/domain/scheduling.test.ts | 73 +- lib/email/i18n.test.ts | 31 +- lib/email/i18n.ts | 373 ++++-- lib/email/index.test.ts | 11 +- lib/email/index.ts | 3 +- lib/email/templates.ts | 379 ++++-- lib/i18n/message-keys.test.ts | 15 +- lib/i18n/portal-locale.ts | 4 +- lib/i18n/resolve-locale.test.ts | 26 +- lib/i18n/signal-reason.ts | 12 +- lib/shop/cart.ts | 5 +- lib/storage.ts | 5 +- lib/test-helpers/db-mock.ts | 23 +- lib/utils/format.test.ts | 36 +- packages/commercekit/package.json | 6 +- packages/commercekit/src/address.ts | 265 +++- packages/commercekit/src/buyer.ts | 18 +- packages/commercekit/src/inventory.test.ts | 15 +- packages/commercekit/src/money.test.ts | 28 +- packages/commercekit/src/money.ts | 60 +- pnpm-workspace.yaml | 8 +- proxy.ts | 3 +- scripts/ci/check-css-vars.mjs | 4 +- 252 files changed, 8642 insertions(+), 6630 deletions(-) diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml index 6b1008e..781407b 100644 --- a/.github/workflows/auto-merge.yml +++ b/.github/workflows/auto-merge.yml @@ -18,10 +18,10 @@ name: Auto-merge on: workflow_run: - workflows: ['CI'] + workflows: ["CI"] types: [completed] schedule: - - cron: '*/10 * * * *' + - cron: "*/10 * * * *" workflow_dispatch: {} permissions: diff --git a/app/(admin)/admin/adoptions/page.tsx b/app/(admin)/admin/adoptions/page.tsx index 585c83f..d8885be 100644 --- a/app/(admin)/admin/adoptions/page.tsx +++ b/app/(admin)/admin/adoptions/page.tsx @@ -2,8 +2,17 @@ import { useState, useEffect } from "react"; import { - Heart, MapPin, DollarSign, Users, Clock, CheckCircle, - PauseCircle, XCircle, ChevronDown, ChevronUp, PawPrint, + Heart, + MapPin, + DollarSign, + Users, + Clock, + CheckCircle, + PauseCircle, + XCircle, + ChevronDown, + ChevronUp, + PawPrint, } from "lucide-react"; import { SPECIES_CONFIG } from "@/lib/config/species"; import type { SpeciesId } from "@/lib/config/species"; @@ -35,15 +44,18 @@ interface Listing { // Icons are UI-layer; labels/colors sourced from lib/config/adoptions SSOT const STATUS_ICONS: Record = { available: CheckCircle, - on_hold: PauseCircle, - adopted: Heart, + on_hold: PauseCircle, + adopted: Heart, withdrawn: XCircle, }; -const STATUS_CONFIG: Record = { +const STATUS_CONFIG: Record< + ListingStatus, + { label: string; icon: React.ElementType; color: string; bg: string; className: string } +> = { available: { ...LISTING_STATUS_CONFIG.available, icon: STATUS_ICONS.available }, - on_hold: { ...LISTING_STATUS_CONFIG.on_hold, icon: STATUS_ICONS.on_hold }, - adopted: { ...LISTING_STATUS_CONFIG.adopted, icon: STATUS_ICONS.adopted }, + on_hold: { ...LISTING_STATUS_CONFIG.on_hold, icon: STATUS_ICONS.on_hold }, + adopted: { ...LISTING_STATUS_CONFIG.adopted, icon: STATUS_ICONS.adopted }, withdrawn: { ...LISTING_STATUS_CONFIG.withdrawn, icon: STATUS_ICONS.withdrawn }, }; @@ -91,7 +103,11 @@ function ListingRow({
{listing.pet.avatarUrl ? ( // eslint-disable-next-line @next/next/no-img-element - {listing.pet.name} + {listing.pet.name} ) : ( {SPECIES_CONFIG[listing.pet.species as SpeciesId]?.emoji ?? "🐾"} )} @@ -105,7 +121,9 @@ function ListingRow({ {/* Owner */}
-

{listing.owner.name ?? "—"}

+

+ {listing.owner.name ?? "—"} +

{listing.owner.email}

@@ -120,9 +138,7 @@ function ListingRow({ {/* Fee */}
- - {formatAdoptionFee(listing.feeCents)} - + {formatAdoptionFee(listing.feeCents)}
{/* Applications badge */} @@ -137,22 +153,38 @@ function ListingRow({
{/* Status badge */} - + {cfg.label} - {expanded ? : } + {expanded ? ( + + ) : ( + + )} {expanded && (
-

Listing ID: {listing.id}

-

Created: {formatIsoDate(listing.createdAt)}

-

Updated: {formatIsoDate(listing.updatedAt)}

-

Species: {SPECIES_CONFIG[listing.pet.species as SpeciesId]?.label ?? listing.pet.species}

+

+ Listing ID:{" "} + {listing.id} +

+

+ Created: {formatIsoDate(listing.createdAt)} +

+

+ Updated: {formatIsoDate(listing.updatedAt)} +

+

+ Species:{" "} + {SPECIES_CONFIG[listing.pet.species as SpeciesId]?.label ?? listing.pet.species} +

{/* Admin actions */} @@ -166,15 +198,17 @@ function ListingRow({ Mark available )} - {listing.status !== "on_hold" && listing.status !== "adopted" && listing.status !== "withdrawn" && ( - - )} + {listing.status !== "on_hold" && + listing.status !== "adopted" && + listing.status !== "withdrawn" && ( + + )} {listing.status !== "adopted" && (
)} diff --git a/app/(admin)/admin/blog/page.tsx b/app/(admin)/admin/blog/page.tsx index d92081e..211d262 100644 --- a/app/(admin)/admin/blog/page.tsx +++ b/app/(admin)/admin/blog/page.tsx @@ -68,7 +68,9 @@ export default function AdminBlogPage() { } } - useEffect(() => { load(); }, []); + useEffect(() => { + load(); + }, []); function openNew() { setEditingId(null); @@ -101,14 +103,11 @@ export default function AdminBlogPage() { setSaving(true); try { - const res = await fetch( - editingId ? `/api/admin/blog/${editingId}` : "/api/admin/blog", - { - method: editingId ? "PATCH" : "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(form), - }, - ); + const res = await fetch(editingId ? `/api/admin/blog/${editingId}` : "/api/admin/blog", { + method: editingId ? "PATCH" : "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(form), + }); const data = await res.json(); if (!data.success) { setError(data.error ?? "Could not save."); @@ -155,15 +154,16 @@ export default function AdminBlogPage() { {/* Editor */} {showForm && (
-
+

{editingId ? "Edit post" : "New post"}

-
@@ -181,12 +181,18 @@ export default function AdminBlogPage() {
{ setSlugTouched(true); setForm((f) => ({ ...f, slug: e.target.value })); }} + onChange={(e) => { + setSlugTouched(true); + setForm((f) => ({ ...f, slug: e.target.value })); + }} required maxLength={200} /> @@ -199,7 +205,10 @@ export default function AdminBlogPage() {