Wishlist Management
Wishlist Management
Section titled “Wishlist Management”Summary
Section titled “Summary”Wishlist Management allows users to create multiple wishlists, add items they want to receive as gifts, and control which wishlists are visible to each Secret Santa group.
User Story
Section titled “User Story”As a gift recipient,
I want to create wishlists and assign them to specific groups,
So that my Secret Santa match knows what gifts I’d like to receive.
Acceptance Criteria
Section titled “Acceptance Criteria”- ✅ User can create multiple wishlists with names and images
- ✅ User can add/remove items to wishlists
- ✅ User can assign wishlists to one or more groups
- ✅ User can hide wishlists from specific groups
- ✅ User can edit wishlist name and image
- ✅ User can delete wishlists
- ✅ Group members can view each other’s wishlists after raffle
- ✅ Wishlists show item count and preview images
Inputs and Outputs
Section titled “Inputs and Outputs”Inputs:
- Wishlist name (string, required)
- Wishlist image (file, optional)
- Group assignments (array of group IDs)
- Visibility toggles per group
Outputs:
- Created wishlist with unique ID
- List of user’s wishlists with item counts
- Wishlist assignment status per group
1. Create Wishlist
Section titled “1. Create Wishlist”User navigates to /wishlist/create │ ▼Enter wishlist name (e.g., "Christmas 2025", "Birthday Wishes") │ ▼Upload cover image (optional) │ ▼Click "Create Wishlist" │ ▼POST /wishlists │ ├─ Success ────────────────────┐ │ │ │ ▼ │ Store in Redux wishlists slice │ │ │ ▼ │ Navigate to /wishlist/:id/edit/items │ │ │ ▼ │ Show "Add items to your wishlist" │ └─ Error ──────────────────────┐ │ ▼ Show error toast2. Assign Wishlist to Group
Section titled “2. Assign Wishlist to Group”User navigates to /settings/profile/wishlists │ ▼View all wishlists with group assignment toggles │ ▼For each group, toggle visibility: - "Office Secret Santa" ☑️ Visible - "Family Exchange" ☐ Hidden │ ▼Click "Save Changes" │ ▼For each change: POST /wishlists/:id/visibility { groupId: X, visible: true/false } │ ├─ Success ────────────────────┐ │ │ │ ▼ │ Update Redux groupWishlists slice │ │ │ ▼ │ Show "Wishlist visibility updated" toast │ └─ Error ──────────────────────┐ │ ▼ Show error toast3. View Own Wishlists
Section titled “3. View Own Wishlists”User navigates to /settings/profile/wishlists │ ▼GET /wishlists/me/visibility │ ▼Display wishlists grid: ┌─────────────┬─────────────┬─────────────┐ │ Wishlist 1 │ Wishlist 2 │ Wishlist 3 │ │ [image] │ [image] │ [image] │ │ 5 items │ 3 items │ 0 items │ │ Groups: 2 │ Groups: 1 │ Groups: 0 │ │ [Edit] [Del]│ [Edit] [Del]│ [Edit] [Del]│ └─────────────┴─────────────┴─────────────┘ │ ▼Click "Edit" on Wishlist 1 │ ▼Navigate to /wishlist/1/edit4. Edit Wishlist
Section titled “4. Edit Wishlist”User navigates to /wishlist/:id/edit │ ▼Load current wishlist data │ ▼Edit name and/or upload new image │ ▼Click "Save" │ ├─ Name changed ───────────────┐ │ │ │ ▼ │ PATCH /wishlists/:id │ { name: "New Name" } │ └─ Image changed ──────────────┐ │ ▼ PUT /wishlists/:id/picture FormData: { image: File } │ ├─ Success ────────────────────┐ │ │ │ ▼ │ Update Redux state │ │ │ ▼ │ Show "Wishlist updated" toast │ └─ Error ──────────────────────┐ │ ▼ Show error toast5. Delete Wishlist
Section titled “5. Delete Wishlist”User clicks "Delete" on wishlist │ ▼Show confirmation modal: "Delete this wishlist? All items will be removed." │ ▼User confirms │ ▼DELETE /wishlists/:id │ ├─ Success ────────────────────┐ │ │ │ ▼ │ Remove from Redux state │ │ │ ▼ │ Show "Wishlist deleted" toast │ └─ Error ──────────────────────┐ │ ▼ Show error toast6. View Match’s Wishlist (From Group)
Section titled “6. View Match’s Wishlist (From Group)”User in group clicks "View Match's Wishlist" │ ▼GET /groups/:groupId/wishlists │ ▼Display match's wishlists assigned to this group │ ▼User clicks on a wishlist │ ▼Navigate to /group/:groupId/wishlists/:wishlistId │ ▼Display all items in wishlist with: - Item name - Item image - Price - Purchase link (opens in new tab)Edge Cases
Section titled “Edge Cases”- Wishlist with no items: Show “Add items to get started”
- Wishlist not assigned to any group: Show warning icon
- User tries to view own match: Not allowed (backend returns 403)
- Delete wishlist assigned to groups: Confirmation warns about visibility loss
- Upload image exceeds size limit: Show error “Image too large (max 5 MB)”
- Network error during save: Retry automatically or show retry button
API/Contracts
Section titled “API/Contracts”Create Wishlist
Section titled “Create Wishlist”Request:
POST /wishlistsAuthorization: Bearer {token}Content-Type: application/json
{ "name": "Christmas 2025"}Response:
{ "success": true, "wishlist": { "id": 1, "name": "Christmas 2025", "image": null, "imageBlurhash": null, "userId": 123, "isVisible": true, "items": [], "createdAt": "2025-10-29T12:00:00Z" }}Get User’s Wishlists with Visibility
Section titled “Get User’s Wishlists with Visibility”Request:
GET /wishlists/me/visibilityAuthorization: Bearer {token}Response:
{ "success": true, "wishlists": [ { "id": 1, "name": "Christmas 2025", "image": "https://cdn.rnb.la/wishlist1.jpg", "imageBlurhash": "L6PZfSi_.AyE_3t7t7R**0o#DgR4", "isVisible": true, "itemsCount": 5, "groupsWishlists": [ { "id": 10, "wishlistId": 1, "groupId": 5, "userId": 123 } ], "items": [ /* array of items */ ] } ]}Set Wishlist Visibility for Group
Section titled “Set Wishlist Visibility for Group”Request:
POST /wishlists/1/visibilityAuthorization: Bearer {token}Content-Type: application/json
{ "groupId": 5, "visible": true}Response:
{ "success": true, "message": "Wishlist visibility updated"}Update Wishlist Name
Section titled “Update Wishlist Name”Request:
PATCH /wishlists/1Authorization: Bearer {token}Content-Type: application/json
{ "name": "Updated Name"}Response:
{ "success": true, "wishlist": { "id": 1, "name": "Updated Name", ... }}Upload Wishlist Image
Section titled “Upload Wishlist Image”Request:
PUT /wishlists/1/pictureAuthorization: Bearer {token}Content-Type: multipart/form-data
image=<binary file data>Response:
{ "success": true, "image": "https://cdn.rnb.la/wishlist1_new.jpg", "imageBlurhash": "L6PZfSi_.AyE_3t7t7R**0o#DgR4"}Delete Wishlist
Section titled “Delete Wishlist”Request:
DELETE /wishlists/1Authorization: Bearer {token}Response:
{ "success": true, "message": "Wishlist deleted"}Error Handling and Retries
Section titled “Error Handling and Retries”Client-side Errors
Section titled “Client-side Errors”- Empty name: Form validation prevents submission
- Image too large: Validate file size before upload (max 5 MB)
- No groups available: Disable group assignment section
Server-side Errors
Section titled “Server-side Errors”- 400 Bad Request: “Invalid wishlist data”
- 404 Not Found: “Wishlist not found”
- 403 Forbidden: “You don’t have permission to edit this wishlist”
- 500 Server Error: “Failed to save wishlist”
Retry Strategy
Section titled “Retry Strategy”- Create/update: Manual retry (user clicks save again)
- Image upload: Automatic retry once, then manual
- Fetch wishlists: Automatic retry with exponential backoff
Telemetry
Section titled “Telemetry”Metrics
Section titled “Metrics”wishlist.created: Incremented on wishlist creationwishlist.deleted: Incremented on wishlist deletionwishlist.assigned_to_group: Incremented when visibility set to truewishlist.unassigned_from_group: Incremented when visibility set to falsewishlist.viewed_by_match: Incremented when match views wishlist
console.log('Wishlist created:', wishlistId);console.log('Wishlist assigned to group:', groupId);Sentry.addBreadcrumb({ category: 'wishlist', message: 'User updated wishlist visibility', data: { wishlistId, groupId, visible },});Rollout
Section titled “Rollout”Feature Flags
Section titled “Feature Flags”Assumption: No feature flags. Consider adding for:
- Beta testing new visibility UI
- A/B testing wishlist creation flow
Migrations
Section titled “Migrations”- Database:
wishliststable requiresimageBlurhashcolumn (added in migration v3.1) - Data: Existing wishlists default to
isVisible: true
Example
Section titled “Example”'use client';
import { useState } from 'react';import { useForm } from 'react-hook-form';import { wishlistHandlers } from 'src/services/handlers/wishlist/wishlistHandlers';import { useRouter } from 'next/navigation';import { paths } from 'src/routes/paths';import { toast } from 'react-toastify';
export default function CreateWishlistView() { const router = useRouter(); const [isSubmitting, setIsSubmitting] = useState(false); const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmit = async (data) => { try { setIsSubmitting(true); const result = await wishlistHandlers.createWishlist(data.name);
toast.success('Wishlist created!'); router.push(paths.home.wishlist.edit.editItems(result.wishlist.id)); } catch (error) { toast.error('Failed to create wishlist'); console.error(error); } finally { setIsSubmitting(false); } };
return ( <form onSubmit={handleSubmit(onSubmit)}> <input {...register('name', { required: 'Name is required' })} placeholder="Wishlist name" /> {errors.name && <span>{errors.name.message}</span>}
<button type="submit" disabled={isSubmitting}> {isSubmitting ? 'Creating...' : 'Create Wishlist'} </button> </form> );}Assign Wishlist to Group
Section titled “Assign Wishlist to Group”// Component for toggling visibilityconst handleToggleVisibility = async (wishlistId: number, groupId: number, visible: boolean) => { try { await wishlistHandlers.setWishlistToGroup(wishlistId, groupId, visible); toast.success(`Wishlist ${visible ? 'shown' : 'hidden'} in group`);
// Refresh wishlists dispatch(fetchWishlists()); } catch (error) { toast.error('Failed to update visibility'); console.error(error); }};
return ( <div> <h3>{wishlist.name}</h3> {userGroups.map((group) => ( <label key={group.id}> <input type="checkbox" checked={isWishlistVisibleInGroup(wishlist, group.id)} onChange={(e) => handleToggleVisibility(wishlist.id, group.id, e.target.checked)} /> {group.name} </label> ))} </div>);Further Reading
Section titled “Further Reading”- Item Management - Adding items to wishlists
- Secret Santa Groups - How matches view wishlists
- State Management - Wishlists Redux slice
- Upload Components - Image upload UI