diff --git a/app/layout.tsx b/app/layout.tsx index 60137432..1235fb30 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -65,8 +65,8 @@ const RootLayout = ({ {children} + - ); diff --git a/components/Toaster/Toaster.test.tsx b/components/Toaster/Toaster.test.tsx index 9222909a..fe55fad5 100644 --- a/components/Toaster/Toaster.test.tsx +++ b/components/Toaster/Toaster.test.tsx @@ -2,6 +2,38 @@ import Toaster from './Toaster'; import React from 'react'; import { render, screen, fireEvent } from '@testing-library/react'; import { useToast } from '@/hooks/use-toast'; +import { useAuthContext } from '@/context/AuthContextProvider'; +import useExchangeGroups from '@/hooks/useExchangeGroups'; + +jest.mock('@/context/AuthContextProvider'); + +const mockGroupMember = { + id: 'test-member-1', + user_id: 'test-member-1', + member: { avatar: 'https://example.com/mock-avatar.png' }, +}; + +const mockMemberSession = { + session: { + user: mockGroupMember, + }, +}; + +const mockGroups = [ + { + gift_exchange_id: 'test-group-1', + owner_id: 'test-member-1', + }, + { + gift_exchange_id: 'test-group-2', + owner_id: 'test-member-2', + }, +] + +jest.mock('@/hooks/useExchangeGroups', () => ({ + __esModule: true, + default: jest.fn(), +})); jest.mock('@/hooks/use-toast', () => ({ useToast: jest.fn(), @@ -12,18 +44,22 @@ const variantTestCases = { default: { title: 'Info!', description: 'Info message', + group: 'test-group-1', }, error: { title: 'Error!', description: 'This is an error message', + group: 'test-group-1', }, warning: { title: 'Warning', description: 'This is a warning message', + group: 'test-group-2', }, success: { title: 'Success!', description: 'This is a success message', + group: 'test-group-2', }, }; @@ -36,43 +72,55 @@ const setupToastTest = (key: string, value: any) => { title: value.title, description: value.description, variant: key, + group: value.group, onOpenChange: mockDismiss, }, ], toast: jest.fn(), dismiss: mockDismiss, }); - render(); + (useAuthContext as jest.Mock).mockReturnValue(mockMemberSession); + (useExchangeGroups as jest.Mock).mockReturnValue(mockGroups); + render( + + ); }; describe('ToastNotification', () => { it.each(Object.entries(variantTestCases))( - 'should render the %s toast correctly', + 'should render the %s toast correctly if the member is the group owner', (key, value) => { setupToastTest(key, value); - expect(screen.getByText(value.title)).toBeInTheDocument(); - expect(screen.getByText(value.description)).toBeInTheDocument(); + if (value.group === 'test-group-1') { + expect(screen.getByText(value.title)).toBeInTheDocument(); + expect(screen.getByText(value.description)).toBeInTheDocument(); + } + else if (value.group === 'test-group-2') { + expect(screen.queryByText(value.title)).not.toBeInTheDocument(); + expect(screen.queryByText(value.description)).not.toBeInTheDocument(); + } }, ); it.each(Object.entries(variantTestCases))( - 'should render the dismiss button for the %s variant', + 'should render the dismiss button for the %s variant if the member is the group owner', async (key, value) => { setupToastTest(key, value); - - const dismissButton = screen.queryByRole('button', { name: 'Close' }); - expect(dismissButton).toBeInTheDocument(); + if (value.group === 'test-group-1') { + const dismissButton = screen.queryByRole('button', { name: 'Close' }); + expect(dismissButton).toBeInTheDocument(); + } }, ); it.each(Object.entries(variantTestCases))( 'should dismiss the %s toast when the dismiss button is clicked', async (key, value) => { setupToastTest(key, value); - - const dismissButton = screen.getByRole('button', { name: 'Close' }); - fireEvent.click(dismissButton); - - expect(mockDismiss).toHaveBeenCalled(); + if (value.group === 'test-group-1') { + const dismissButton = screen.getByRole('button', { name: 'Close' }); + fireEvent.click(dismissButton); + expect(mockDismiss).toHaveBeenCalled(); + } }, ); }); diff --git a/components/Toaster/Toaster.tsx b/components/Toaster/Toaster.tsx index e5a9e870..bac8f9b9 100644 --- a/components/Toaster/Toaster.tsx +++ b/components/Toaster/Toaster.tsx @@ -11,6 +11,8 @@ import { ToastClose } from "../ToastClose/ToastClose" import { ToastViewport } from "../ToastViewport/ToastViewport" import { ToastProvider } from "../ToastProvider/ToastProvider" import { JSX } from "react" +import { useAuthContext } from '@/context/AuthContextProvider'; +import useExchangeGroups from "@/hooks/useExchangeGroups"; /** * Renders and manages display of toast notifications. @@ -18,10 +20,18 @@ import { JSX } from "react" */ const Toaster = (): JSX.Element => { const { toasts } = useToast() + const { session } = useAuthContext() + const userExchangeGroups = useExchangeGroups() + + const filteredToasts = toasts.filter((toast) => { + return userExchangeGroups.some((group) => { + return (group.gift_exchange_id === toast.group && group.owner_id === session?.user.id) + }) + }) return ( - {toasts.map(function ({ id, title, description, action, ...props }) { + {filteredToasts.map(function ({ id, title, description, action, ...props }) { return (
diff --git a/hooks/useExchangeGroups.ts b/hooks/useExchangeGroups.ts new file mode 100644 index 00000000..fdb9dd66 --- /dev/null +++ b/hooks/useExchangeGroups.ts @@ -0,0 +1,45 @@ +// Copyright (c) Gridiron Survivor. +// Licensed under the MIT License. + +import { useState, useEffect } from 'react'; +import { GiftExchangeWithMemberCount } from '../app/types/giftExchange'; + +/** + * A React hook that manages the current users exchange groups. + * @returns {array} An array containing the current list of user gift exchange groups. + */ + +const useExchangeGroups = (): Array => { + const [giftExchanges, setGiftExchanges] = useState< + GiftExchangeWithMemberCount[] + >([]); + + async function fetchGiftExchanges() { + try { + const response = await fetch(`/api/gift-exchanges`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + setGiftExchanges(data); + + } catch (error) { + console.error('Failed to fetch gift exchanges:', error); + } + }; + + useEffect(() => { + fetchGiftExchanges(); + }, []); + + return giftExchanges; +}; + +export default useExchangeGroups;