Skip to content

Epic 4 — Pet Owner Profile


BE-PETOWNER-001 — Extend PetOwnerProfile schema + Pet model

  • [x] Implemented

Files:

  • Edit: prisma/schema.prisma
  • Run: pnpm db:migrate
  • Edit: src/graphql/schema/index.ts
  • Create: src/graphql/resolvers/petOwner.ts
  • Edit: src/graphql/resolvers/index.ts

Schema additions:

prisma
model PetOwnerProfile {
  // existing scaffold fields stay
  displayName   String?
  bio           String?
  location      String?
  socialLinks   Json?       // { instagram, tiktok, website }
  pets          Pet[]
  dreamJobs     Json?       // DreamJobEntry[] — see BE-TALENT-002
  createdAt     DateTime    @default(now())
  updatedAt     DateTime    @updatedAt
}

model Pet {
  id              String          @id @default(cuid())
  ownerId         String
  owner           PetOwnerProfile @relation(fields: [ownerId], references: [id], onDelete: Cascade)

  // Identity
  name            String
  species         PetSpecies      // DOG | CAT | BIRD | HORSE | RABBIT | REPTILE | OTHER
  breed           String?
  gender          PetGender?      // MALE | FEMALE | UNKNOWN
  dateOfBirth     DateTime?
  estimatedAge    String?         // fallback when exact DOB unknown: "~3 years"
  colorMarkings   String?

  // Appearance
  photos          String[]        @default([])   // CDN URLs, first is primary
  heightCm        Float?
  weightKg        Float?

  // Casting-relevant
  trainedSkills   String[]        @default([])   // e.g. ["sit", "roll over", "fetch"]
  experienceLevel PetExperience   @default(NONE) // NONE | BEGINNER | EXPERIENCED | PROFESSIONAL
  hasAgentRep     Boolean         @default(false)
  isAvailable     Boolean         @default(true)

  // Work history
  notableWorks    Json?           // [{ title, role, year, productionType }]

  // Health & compliance
  vaccinated      Boolean         @default(false)
  insured         Boolean         @default(false)
  certifications  String[]        @default([])

  createdAt       DateTime        @default(now())
  updatedAt       DateTime        @updatedAt

  @@map("pets")
}

enum PetSpecies {
  DOG CAT BIRD HORSE RABBIT REPTILE OTHER
}

enum PetGender {
  MALE FEMALE UNKNOWN
}

enum PetExperience {
  NONE BEGINNER EXPERIENCED PROFESSIONAL
}

GraphQL mutations:

graphql
createPet(input: CreatePetInput!): Pet!
updatePet(id: ID!, input: UpdatePetInput!): Pet!
deletePet(id: ID!): Boolean!

GraphQL queries:

graphql
myPets: [Pet!]!
pet(id: ID!): Pet

Acceptance criteria:

  • A PetOwner can have unlimited pets
  • Only the owning user can mutate their pets (requireProfile(ctx, 'PET_OWNER') + ownership check)
  • myPets returns all pets for the authenticated PetOwner

BE-PETOWNER-002 — Pet photo upload

  • [x] Implemented

Files:

  • Edit: src/graphql/schema/index.ts — add uploadPetPhoto(petId: ID!, file: Upload!): String!
  • Edit: src/graphql/resolvers/petOwner.ts
  • Edit: src/services/media/index.ts

Description: Upload pet photo to S3 (castyou-media/pet-photos/{petId}/{filename}). Returns CDN URL. Appends URL to Pet.photos. Max 10 photos per pet, 5 MB each. Accepted: jpg, png, webp.


FE-PETOWNER-001 — Pet management page

  • [x] Implemented

Files:

  • Create: apps/app/src/pages/petowner/PetOwnerProfilePage.tsx
  • Create: apps/app/src/pages/petowner/PetsPage.tsx — list of all pets
  • Create: apps/app/src/pages/petowner/PetDetailPage.tsx — view a single pet's full profile
  • Create: apps/app/src/pages/petowner/PetEditPage.tsx — create / edit form
  • Create: apps/app/src/lib/queries/petOwner.tsMY_PETS_QUERY, PET_QUERY
  • Create: apps/app/src/hooks/usePets.ts
  • Create: apps/app/src/hooks/useCreatePet.ts, useUpdatePet.ts, useDeletePet.ts
  • Edit: apps/app/src/App.tsx — add routes /pets, /pets/new, /pets/:id, /pets/:id/edit

Description: Pet management hub for PetOwner-profile users.

  • Pets list (/pets): grid of pet cards showing primary photo, name, species badge, breed, experience level badge, and availability indicator. "Add pet" CTA. Uses Skeleton during load, EmptyState when no pets yet.
  • Pet detail (/pets/:id): full view — photo gallery, identity section (species, breed, age, color), casting info (trained skills as Chip list, experience level, availability), notable works, health/compliance badges. "Edit" button.
  • Pet edit form (/pets/:id/edit and /pets/new): single-page form with sections for identity, appearance, trained skills (MultiSelect), experience, work history, and health. FileUpload for photos. All DS components.

Acceptance criteria:

  • Only visible to users with the PET_OWNER profile
  • Route guard redirects non-PetOwners
  • Photo upload wired up (BE-PETOWNER-002)
  • Delete pet with confirmation dialog

FE-PETOWNER-002 — Pet card component (Design System)

  • [x] Implemented

Files:

  • Create: packages/design-system/src/components/ui/PetCard.tsx
  • Edit: packages/design-system/src/index.ts

Description: Reusable card for the pet grid. Shows primary photo, name, species icon, breed, experience badge, and availability dot. Mirrors the TalentCard structure. Props: pet: PetCardData, onClick, className?.