Pro Favorites

Summary

Pros can save programmes (new-build developments) or individual lots as favorites for quick access. Each ProFavorite record points to either a Programme or a Lot — never both simultaneously. The constraint is enforced at both the API layer (XOR validation) and at the DB level (unique index prevents duplicates). Favorites are listed newest-first with eager-loaded programme/lot summaries.

State Diagram

stateDiagram-v2
    state "Non sauvegardé" as NotFavorited
    state "En favoris" as Favorited

    [*] --> NotFavorited
    NotFavorited --> Favorited: pro adds a favourite
    Favorited --> NotFavorited: pro removes a favourite
    Favorited --> [*]
    NotFavorited --> [*]

Steps

1. List Favorites (Actor: pro)

GET /pro/favorites. Returns all ProFavorite records where proId = request.proId, ordered by createdAt DESC.

Each record is returned with:

  • If it’s a programme favorite: programme { id, slug, name, ville, prixMin, prixMax, photos }
  • If it’s a lot favorite: lot { id, type, surface, price, status, isComplete }

The fields not targeted (e.g. lot on a programme favorite) will be null.

Returns { data: favorites[] }.

Triggers: Pro opens their saved items view Outcome: Full list of favorites with embedded programme/lot summaries

2. Add Favorite (Actor: pro)

POST /pro/favorites with body validated by addFavoriteSchema. Must provide exactly one of programmeId or lotId — providing both or neither returns 400 INVALID_FAVORITE.

prisma.proFavorite.create({ data: { proId, programmeId, lotId } }).

On Prisma error:

  • P2002 (unique constraint) → 409 DUPLICATE (“Already in favorites”)
  • P2003 (foreign key violation — programme/lot doesn’t exist) → 400 NOT_FOUND

Returns the created ProFavorite record with HTTP 201.

Triggers: Pro clicks a “Sauvegarder” button on a programme or lot page Outcome: Favorite created; { data: favorite } returned

3. Remove Favorite (Actor: pro)

DELETE /pro/favorites/:id.

Ownership check: prisma.proFavorite.findFirst({ where: { id, proId: request.proId } }). If not found (or belongs to another pro) → 404 NOT_FOUND.

prisma.proFavorite.delete({ where: { id } }).

Returns { success: true }.

Triggers: Pro removes a saved item Outcome: Favorite record deleted

Error States

  • Both programmeId and lotId provided → 400 INVALID_FAVORITE
  • Neither programmeId nor lotId provided → 400 INVALID_FAVORITE
  • Programme or lot does not exist → 400 NOT_FOUND (P2003)
  • Already favorited → 409 DUPLICATE (P2002)
  • Favorite not found or owned by different pro (delete) → 404 NOT_FOUND
  • Unauthenticated → 401 (authenticatePro hook)
  • CNI gate active → 403 CNI_REQUIRED
  • registration — pro must be authenticated to manage favorites