Skip to content

Conversation

@jefische
Copy link
Contributor

@jefische jefische commented Nov 11, 2025

Description

Before:

When initiating a gift exchange group drawing and generating gift suggestions , the WaitingForSuggestions component loads but the user has to refresh the page repeatedly to see the AI generated gift suggestions.

After:

As soon as the gift suggestions are generated, they are sent back to the client real-time allowing for immediate rendering of the gifts.

Closes #677

Testing instructions

To test this an exchange group has to be created with 3 or more users to allow a drawing to be initiated. Once the drawing is initiated, the page will render the WaitingForSuggestions component if no gifts are available. Once all 3 gifts are generated in the database, the 3 GiftSuggestionCard components will display.

Additional information

Screenshot of the WaitingForSuggestions and GiftSuggestionCard components

image image

Pre-submission checklist

  • Code builds and passes locally
  • PR title follows Conventional Commit format (e.g. test #001: created unit test for __ component)
  • Request reviews from the Peer Code Reviewers and Senior+ Code Reviewers groups
  • Thread has been created in Discord and PR is linked in gis-code-questions

Copilot AI review requested due to automatic review settings November 11, 2025 18:06
@vercel
Copy link

vercel bot commented Nov 11, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
elecretanta Ready Ready Preview Comment Nov 13, 2025 3:23pm
elecretanta-storybook Ready Ready Preview Comment Nov 13, 2025 3:23pm
elecretanta-unit-test Ready Ready Preview Comment Nov 13, 2025 3:23pm

@shashilo shashilo requested review from a team, bethanyann and tonyb650 and removed request for a team, bethanyann, Copilot, nickytonline and shashilo November 11, 2025 18:06
Copilot finished reviewing on behalf of jefische November 11, 2025 18:08

{giftSuggestions?.length !== 0 && (

{giftSuggestions?.length < 3 ? (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if a giftSuggstion fails? I would just show the gifts as they come in. If there are any failures, we'll never have 3 gifts.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense, I'll revert this back to how it was.

table: 'gift_suggestions',
filter: `gift_exchange_id=eq.${id}`,
},
(payload: any) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No use of any.

filter: `gift_exchange_id=eq.${id}`,
},
(payload: any) => {
if (payload.new.giver_id !== session.user.id) return;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shorthand is nice, but it comes at the cost of legibility.

Suggested change
if (payload.new.giver_id !== session.user.id) return;
if (payload.new.giver_id !== session.user.id) {
return;
}

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds real-time WebSocket functionality to eliminate the need for users to manually refresh the page when gift suggestions are being generated. The implementation uses Supabase's real-time subscriptions to listen for new gift suggestion inserts and automatically updates the UI.

  • Implements WebSocket subscription to listen for gift suggestion INSERTs in real-time
  • Adds test coverage for rendering gift suggestion cards and the waiting state
  • Adds data-testid to GiftSuggestionCard for test identification

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
app/gift-exchanges/[id]/page.tsx Added Supabase real-time subscription to listen for new gift suggestions and update state automatically
app/gift-exchanges/[id]/page.test.tsx Added mock for Supabase client and two new test cases for gift suggestion rendering scenarios
components/GiftSuggestionCard/GiftSuggestionCard.tsx Added data-testid attribute for testing purposes
Comments suppressed due to low confidence (1)

app/gift-exchanges/[id]/page.tsx:159

  • The fetchGiftExchangeData callback has [id, session, isSignedIn] as dependencies but is called in a useEffect that also depends on [fetchGiftExchangeData, session, id]. This creates a potential infinite loop since fetchGiftExchangeData will be recreated whenever session or id change, triggering the effect again. The WebSocket effect (lines 54-89) should include fetchGiftExchangeData in its dependency array if it relies on having up-to-date gift suggestions, or add router and toast to the fetchGiftExchangeData dependencies to ensure stable function identity.
  }, [id, session, isSignedIn]);

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

id: String(payload.new.id),
};

setGiftSuggestions((prev) => [...prev, newSuggestion]);
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implementation could lead to duplicate gift suggestions if the WebSocket delivers the same INSERT event multiple times or if there's a race condition between the initial fetch and real-time updates. Consider checking if the suggestion already exists before adding it: setGiftSuggestions((prev) => prev.some(s => s.id === newSuggestion.id) ? prev : [...prev, newSuggestion]);

Suggested change
setGiftSuggestions((prev) => [...prev, newSuggestion]);
setGiftSuggestions((prev) =>
prev.some(s => s.id === newSuggestion.id) ? prev : [...prev, newSuggestion]
);

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use this suggestion

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed this as well, I'll add it in.


const giftSuggestionCards = screen.queryAllByTestId('gift-suggestion-card');
expect(giftSuggestionCards.length).toBe(0);
})
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing semicolon at the end of the test case.

Suggested change
})
});

Copilot uses AI. Check for mistakes.
const waitingForSuggestions = screen.queryByTestId('suggestions-waiting');
expect(waitingForSuggestions).not.toBeInTheDocument();

// Verifiy 3 gift suggestions are provided
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected spelling of 'Verifiy' to 'Verify'.

Suggested change
// Verifiy 3 gift suggestions are provided
// Verify 3 gift suggestions are provided

Copilot uses AI. Check for mistakes.
giftSuggestionCards.forEach(card => {
expect(card).toBeInTheDocument();
});
})
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing semicolon at the end of the test case.

Suggested change
})
});

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jefische Is your Linter working? Your IDE should've autoformatted and added this semi colon.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to get this figured out, I thought it was working. I'll make the changes manually for now.

import { TOASTS } from '@/components/Toast/toasts.consts';
import type { IGiftSuggestion } from '@/app/types/giftSuggestion';

type GiftSuggestionsResponse = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we adding a new type into a test file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this originally as I was getting a typescript warning, but I see this actually isn't needed. I'll remove it.

id: String(payload.new.id),
};

setGiftSuggestions((prev) => [...prev, newSuggestion]);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use this suggestion

giftSuggestionCards.forEach(card => {
expect(card).toBeInTheDocument();
});
})
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jefische Is your Linter working? Your IDE should've autoformatted and added this semi colon.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +72 to +74
if (payload.new.giver_id !== session.user.id) {
return;
}
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] There's an extra space before the opening brace. While this doesn't affect functionality, it's inconsistent with the rest of the codebase's formatting. Consider removing the extra space for consistency.

Copilot uses AI. Check for mistakes.
Comment on lines +81 to +83
setGiftSuggestions((prev) =>
prev.some(s => s.id === newSuggestion.id) ? prev : [...prev, newSuggestion]
);
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The duplicate check compares newSuggestion.id (which is String(payload.new.id)) against existing suggestion IDs, but the existing suggestions loaded from the API also use database IDs converted to strings. However, this check may fail to prevent duplicates if the initial fetch happens while a WebSocket INSERT event is in flight. Consider also checking if the suggestion with the same ID already exists in the initial data fetch to handle potential race conditions.

Copilot uses AI. Check for mistakes.
Comment on lines +78 to +111
"price": "$50",
"title": "Bamboo Cheese Board and Knife Set",
"imageUrl": null,
"matchScore": 80,
"description": "This elegant bamboo cheese board comes with a hidden slide-out drawer that holds a cheese knife set. Perfect for hosting gatherings and enjoying a selection of cheeses.",
"matchReasons": [
"Luxurious bamboo material",
"Ideal for hosting food-related events"
],
"id": "1451",
},
{
"price": "$60",
"title": "Swarovski Crystal Watch",
"imageUrl": null,
"matchScore": 90,
"description": "A stunning Swarovski crystal watch that combines functionality with luxury. The intricate design and sparkling crystals make it a stylish accessory for any occasion.",
"matchReasons": [
"Luxurious item for a luxury watch enthusiast",
"Adds a touch of style and fashion"
],
"id": "1454",
},
{
"price": "$55",
"title": "Artisanal Gluten-Free Baking Kit",
"imageUrl": null,
"matchScore": 85,
"description": "A complete baking kit with high-quality gluten-free ingredients and recipes for creating delicious treats at home. Perfect for someone with a gluten allergy who enjoys baking.",
"matchReasons": [
"Caters to gluten allergy",
"Combines food & cooking with arts & crafts"
],
"id": "1455",
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The mock gift suggestions data uses inconsistent field ordering compared to the IGiftSuggestion interface definition. Consider reordering the fields to match the interface definition (id, title, price, description, matchReasons, matchScore, imageUrl) for better readability and maintainability.

Suggested change
"price": "$50",
"title": "Bamboo Cheese Board and Knife Set",
"imageUrl": null,
"matchScore": 80,
"description": "This elegant bamboo cheese board comes with a hidden slide-out drawer that holds a cheese knife set. Perfect for hosting gatherings and enjoying a selection of cheeses.",
"matchReasons": [
"Luxurious bamboo material",
"Ideal for hosting food-related events"
],
"id": "1451",
},
{
"price": "$60",
"title": "Swarovski Crystal Watch",
"imageUrl": null,
"matchScore": 90,
"description": "A stunning Swarovski crystal watch that combines functionality with luxury. The intricate design and sparkling crystals make it a stylish accessory for any occasion.",
"matchReasons": [
"Luxurious item for a luxury watch enthusiast",
"Adds a touch of style and fashion"
],
"id": "1454",
},
{
"price": "$55",
"title": "Artisanal Gluten-Free Baking Kit",
"imageUrl": null,
"matchScore": 85,
"description": "A complete baking kit with high-quality gluten-free ingredients and recipes for creating delicious treats at home. Perfect for someone with a gluten allergy who enjoys baking.",
"matchReasons": [
"Caters to gluten allergy",
"Combines food & cooking with arts & crafts"
],
"id": "1455",
"id": "1451",
"title": "Bamboo Cheese Board and Knife Set",
"price": "$50",
"description": "This elegant bamboo cheese board comes with a hidden slide-out drawer that holds a cheese knife set. Perfect for hosting gatherings and enjoying a selection of cheeses.",
"matchReasons": [
"Luxurious bamboo material",
"Ideal for hosting food-related events"
],
"matchScore": 80,
"imageUrl": null,
},
{
"id": "1454",
"title": "Swarovski Crystal Watch",
"price": "$60",
"description": "A stunning Swarovski crystal watch that combines functionality with luxury. The intricate design and sparkling crystals make it a stylish accessory for any occasion.",
"matchReasons": [
"Luxurious item for a luxury watch enthusiast",
"Adds a touch of style and fashion"
],
"matchScore": 90,
"imageUrl": null,
},
{
"id": "1455",
"title": "Artisanal Gluten-Free Baking Kit",
"price": "$55",
"description": "A complete baking kit with high-quality gluten-free ingredients and recipes for creating delicious treats at home. Perfect for someone with a gluten allergy who enjoys baking.",
"matchReasons": [
"Caters to gluten allergy",
"Combines food & cooking with arts & crafts"
],
"matchScore": 85,
"imageUrl": null,

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: Gift Suggestions not updating when payload comes back

3 participants