Backend (Convex)
API — productEngagement
Generated catalog of Convex functions in convex/productEngagement/. Regenerated by `bun run docs:api`.
Auto-generated from source. Do not edit by hand — run
bun run docs:api.
query (24)
| Function | Description | Args |
|---|---|---|
getAllBlocks | Get all user block relationships (admin-only, paginated) | { limit: v.optional(v.number()), offset: v.optional(v.number()), } |
getAllRatings | Get all ratings with filters and pagination (Dashboard) Used for managing product reviews/ratings in admin panel | { status: v.optional( v.union( v.literal("PENDING"), v.literal("APPROVED"), v.literal("REJECTED"), v.literal("FLAGGED") ) ), productId: v.optional(v.id("shop… |
getAllReports | Get all user reports across all reviews (admin-only, paginated) Unlike getCommentReports which is per-comment, this provides a global view. | { status: v.optional( v.union(v.literal("pending"), v.literal("reviewed"), v.literal("dismissed")) ), limit: v.optional(v.number()), offset: v.optional(v.num… |
getBasedOnPurchases | Get recommendations based on purchase history | { limit: v.optional(v.number()), } |
getBlockedUsers | Get list of blocked users with their profile names. | {} |
getCommentReports | Get reports for a specific comment (dashboard admin-only). | { commentId: v.id("productComments"), } |
getFlaggedRatings | Get flagged ratings for review (Dashboard) | { limit: v.optional(v.number()), offset: v.optional(v.number()), } |
getListProductsPaginated | Products in a list, for Convex usePaginatedQuery (mobile wishlist infinite scroll). Cursor-driven via .paginate over the by_list_sort index (sortOrder ascending), so already-loaded pages stay reactive and each page is read once — replaces the mobile expanding-limit pattern that re-read the whole window on every load-more. Product details are joined per page item. totalCount is intentionally omitted — mobile reads the list size from getUserWishlists.productCount; paging state comes from isDone. | { listId: v.id("userProductLists"), paginationOpts: paginationOptsValidator, } |
getPendingRatings | Get pending ratings awaiting moderation (Dashboard) | { limit: v.optional(v.number()), offset: v.optional(v.number()), } |
getPersonalizedRecommendations | Get personalized recommendations for user Based on: purchase history, views, saved items, beauty profile | { limit: v.optional(v.number()), excludeOwned: v.optional(v.boolean()), } |
getProductComments | — | { productId: v.string(), status: v.optional( v.union( v.literal("PENDING"), v.literal("APPROVED"), v.literal("REJECTED"), v.literal("FLAGGED") ) ), limit: v.… |
getProductEngagementAnalytics | Get product engagement analytics (admin) | { productId: v.optional(v.id("shopifyProducts")), dateFrom: v.number(), dateTo: v.number(), limit: v.optional(v.number()), } |
getProductInteractionStats | Get interaction statistics for a product | { productId: v.id("shopifyProducts"), } |
getRating | Get a single rating by ID (Dashboard) | { id: v.id("productComments"), } |
getRatingAnalytics | Get detailed rating analytics for dashboard | { productId: v.optional(v.id("shopifyProducts")), dateFrom: v.number(), dateTo: v.number(), } |
getRecentlyViewed | Get recently viewed products for user | { limit: v.optional(v.number()), } |
getTrendingProducts | Get trending products based on recent interactions | { limit: v.optional(v.number()), timeWindowDays: v.optional(v.number()), } |
getUserComments | Get comments by current user | { limit: v.optional(v.number()), status: v.optional( v.union( v.literal("PENDING"), v.literal("APPROVED"), v.literal("REJECTED"), v.literal("FLAGGED") ) ), } |
getUserCommentVote | Check if current user has voted on a comment | { commentId: v.id("productComments"), } |
getUserEngagementAnalytics | Get user engagement analytics | { userId: v.optional(v.string()), dateFrom: v.optional(v.number()), dateTo: v.optional(v.number()), } |
getUserProductReview | Get current user's review for a specific product (any status) Used by mobile to detect existing reviews regardless of moderation status. Returns same shape as getProductComments items for type compatibility. | { productId: v.string() } |
getUserRecentInteractions | Get user's recent product interactions | { interactionType: v.optional( v.union( v.literal("VIEW"), v.literal("SAVE"), v.literal("SHARE"), v.literal("CART_ADD"), v.literal("PURCHASE"), v.literal("QU… |
getUserWishlists | Get user's product lists (wishlists, favorites, etc.) | { type: v.optional( v.union( v.literal("WISHLIST"), v.literal("FAVORITES"), v.literal("COLLECTION"), v.literal("COMPARISON") ) ), } |
getWishlistedShopifyIds | Get the Shopify GIDs of products in the user's DEFAULT wishlist. Returns the Shopify product id (shopifyId, e.g. "gid://shopify/Product/…"), which is the SAME id every mobile surface already carries — product cards (shop grid, search, home carousels, recently-viewed) and the PDP. Keying the wishlist on the GID means the heart works uniformly everywhere without threading a Convex _id through the product feeds. Lightweight membership-set read: subscribed ONCE at a grid/screen level so every card does an O(1) Set lookup instead of one subscription per card. Reactive — when toggleWishlist mutates, this set updates and all hearts reconcile. Signed-out (or no wishlist yet) returns []. | {} |
mutation (25)
| Function | Description | Args |
|---|---|---|
addToWishlist | Add a product to a list | { listId: v.id("userProductLists"), productId: v.id("shopifyProducts"), notes: v.optional(v.string()), } |
blockUser | Block a user — their reviews will be hidden from the blocker's view. | { blockedUserId: v.string(), } |
bulkDeleteComments | Bulk delete comments (admin-only, no ownership check). Runs in a single transaction so the shared comments:<productId> counter and the productRatings aggregate are updated without OCC contention (the previous dashboard path fired N parallel deleteComment calls, which both threw on the ownership check and conflicted on the counter). Reports per-id outcome. | { commentIds: v.array(v.id("productComments")), } |
bulkSetCommentStatus | Bulk set comment moderation status (admin-only). Supports all four statuses (PENDING/APPROVED/REJECTED/FLAGGED) so the dashboard's "Flag" and "Mark as Pending" bulk actions work. Maintains the comment counter + productRatings aggregate on every APPROVED transition. | { commentIds: v.array(v.id("productComments")), status: COMMENT_STATUS, reason: v.optional(v.string()), } |
createComment | Create a new product comment/review Auto-approves ratings-only, requires moderation for comments with content | { productId: v.id("shopifyProducts"), rating: v.optional(v.number()), title: v.optional(v.string()), content: v.string(), images: v.optional(v.array(v.string… |
createWishlist | Create a new product list (wishlist, favorites, collection, comparison) | { name: v.string(), description: v.optional(v.string()), type: v.union( v.literal("WISHLIST"), v.literal("FAVORITES"), v.literal("COLLECTION"), v.literal("CO… |
deleteComment | Delete a comment Only owner can delete their own comment | { commentId: v.id("productComments"), } |
deleteReviewImage | Delete a review image | { commentId: v.id("productComments"), imageUrl: v.string(), } |
deleteWishlist | Delete a product list | { listId: v.id("userProductLists"), } |
moderateComment | Moderate a comment (approve/reject/flag) Admin only | { commentId: v.id("productComments"), status: v.union( v.literal("APPROVED"), v.literal("REJECTED"), v.literal("FLAGGED") ), moderationReason: v.optional(v.s… |
recordProductView | Record a product view | { productId: v.id("shopifyProducts"), sessionId: v.optional(v.string()), deviceInfo: v.optional(v.any()), durationSeconds: v.optional(v.number()), } |
removeFromWishlist | Remove a product from a list | { listId: v.id("userProductLists"), productId: v.id("shopifyProducts"), } |
reorderListItems | Reorder items in a list | { listId: v.id("userProductLists"), itemOrder: v.array( v.object({ itemId: v.id("listProducts"), sortOrder: v.number(), }) ), } |
reportComment | Report a comment for inappropriate content. Creates an audit trail in userReports and prevents duplicate reports. | { commentId: v.id("productComments"), reason: v.union( v.literal("SPAM"), v.literal("INAPPROPRIATE"), v.literal("FAKE"), v.literal("HARASSMENT"), v.literal("… |
resolveReport | Resolve a user report (mark as reviewed or dismissed) | { reportId: v.id("userReports"), resolution: v.union(v.literal("reviewed"), v.literal("dismissed")), } |
toggleFavorite | Toggle favorite status (add/remove from favorites) | { productId: v.id("shopifyProducts"), } |
toggleFeaturedComment | Feature/unfeature a comment Admin only | { commentId: v.id("productComments"), isFeatured: v.boolean(), } |
trackCartAdd | Track cart add | { productId: v.id("shopifyProducts"), variantId: v.optional(v.string()), quantity: v.optional(v.number()), } |
trackProductShare | Track product share | { productId: v.id("shopifyProducts"), platform: v.optional(v.string()), } |
unblockUser | Unblock a user. | { blockedUserId: v.string(), } |
updateComment | Update an existing comment Only owner can update, resets to pending if content changes | { commentId: v.id("productComments"), rating: v.optional(v.number()), title: v.optional(v.string()), content: v.optional(v.string()), images: v.optional(v.ar… |
updateListProductNotes | Update notes for a product in a list | { listId: v.id("userProductLists"), productId: v.id("shopifyProducts"), notes: v.string(), } |
updateWishlist | Update a product list | { listId: v.id("userProductLists"), name: v.optional(v.string()), description: v.optional(v.string()), isPrivate: v.optional(v.boolean()), sortOrder: v.optio… |
uploadReviewImages | Upload review images (returns storage URL) Note: Actual file upload handled by Convex storage | { commentId: v.id("productComments"), imageUrls: v.array(v.string()), } |
voteComment | Vote on a comment (helpful/not helpful) | { commentId: v.id("productComments"), isHelpful: v.boolean(), } |
internalMutation (2)
| Function | Description | Args |
|---|---|---|
markProductPurchased | Mark product as purchased (internal) Called after order is placed | { userId: v.string(), productId: v.id("shopifyProducts"), orderId: v.optional(v.string()), } |
quickAddToWishlistInternal | Quick add to wishlist (internal - called by wishlistToggle.ts) | { userId: v.string(), productId: v.string(), } |