diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000..af67efe --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# Bulk reformats. `git config blame.ignoreRevsFile .git-blame-ignore-revs` +e558eb5adb52909a3687017dcbf46895bcc6097c # prettier, 252 files 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/.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/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() {