diff --git a/apps/journeys-admin/src/components/JourneyVisitorsList/FilterDrawer/GoogleSheetsSyncDialog/GoogleSheetsSyncDialog.spec.tsx b/apps/journeys-admin/src/components/JourneyVisitorsList/FilterDrawer/GoogleSheetsSyncDialog/GoogleSheetsSyncDialog.spec.tsx index 07f0e64fbf6..984f538359f 100644 --- a/apps/journeys-admin/src/components/JourneyVisitorsList/FilterDrawer/GoogleSheetsSyncDialog/GoogleSheetsSyncDialog.spec.tsx +++ b/apps/journeys-admin/src/components/JourneyVisitorsList/FilterDrawer/GoogleSheetsSyncDialog/GoogleSheetsSyncDialog.spec.tsx @@ -1,8 +1,16 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react' import mockRouter from 'next-router-mock' +import { useAuth } from '../../../../libs/auth' + import { GoogleSheetsSyncDialog } from './GoogleSheetsSyncDialog' +jest.mock('../../../../libs/auth', () => ({ + useAuth: jest.fn() +})) + +const mockUseAuth = useAuth as jest.MockedFunction + jest.mock('next-i18next', () => ({ useTranslation: () => ({ t: (value: string) => value @@ -50,7 +58,8 @@ const defaultIntegrationsData = { { __typename: 'IntegrationGoogle', id: 'integration1', - accountEmail: 'test@example.com' + accountEmail: 'test@example.com', + user: { __typename: 'AuthenticatedUser', id: 'user1' } } ] } @@ -100,9 +109,14 @@ describe('GoogleSheetsSyncDialog', () => { mockUseLazyQuery.mockReset() mockUseMutation.mockReset() mockUseIntegrationQuery.mockReset() + mockUseAuth.mockReset() mockEnqueueSnackbar.mockClear() mockRouter.setCurrentUrl('/journeys') + mockUseAuth.mockReturnValue({ + user: { id: 'user1' } + } as unknown as ReturnType) + mockUseQuery.mockReturnValue({ data: defaultJourneyData }) mockUseIntegrationQuery.mockReturnValue({ data: defaultIntegrationsData @@ -218,4 +232,107 @@ describe('GoogleSheetsSyncDialog', () => { expect(onClose).toHaveBeenCalled() }) + + it('only shows current user Google integrations in the dropdown', async () => { + mockUseIntegrationQuery.mockReturnValue({ + data: { + integrations: [ + { + __typename: 'IntegrationGoogle', + id: 'integration1', + accountEmail: 'myaccount@example.com', + user: { __typename: 'AuthenticatedUser', id: 'user1' } + }, + { + __typename: 'IntegrationGoogle', + id: 'integration2', + accountEmail: 'other-manager@example.com', + user: { __typename: 'AuthenticatedUser', id: 'otherUser' } + } + ] + } + }) + setupApolloMocks() + + render( + + ) + + await screen.findByRole('button', { name: 'Create Sync' }) + + // Open the dropdown + fireEvent.mouseDown( + screen.getByRole('combobox', { name: 'Integration account' }) + ) + + const options = screen.getAllByRole('option') + const optionTexts = options.map((o) => o.textContent) + + expect(optionTexts).toContain('myaccount@example.com') + expect(optionTexts).not.toContain('other-manager@example.com') + }) + + it('shows no integration options when none belong to current user', async () => { + mockUseIntegrationQuery.mockReturnValue({ + data: { + integrations: [ + { + __typename: 'IntegrationGoogle', + id: 'integration2', + accountEmail: 'other-manager@example.com', + user: { __typename: 'AuthenticatedUser', id: 'otherUser' } + } + ] + } + }) + setupApolloMocks() + + render( + + ) + + await screen.findByRole('button', { name: 'Create Sync' }) + + fireEvent.mouseDown( + screen.getByRole('combobox', { name: 'Integration account' }) + ) + + const options = screen.getAllByRole('option') + // Only the disabled placeholder option should exist + expect(options).toHaveLength(1) + expect(options[0]).toHaveTextContent('Select integration account') + }) + + it('shows no integration options when user is not authenticated', async () => { + mockUseAuth.mockReturnValue({ + user: null + } as unknown as ReturnType) + mockUseIntegrationQuery.mockReturnValue({ + data: { + integrations: [ + { + __typename: 'IntegrationGoogle', + id: 'integration1', + accountEmail: 'someone@example.com', + user: null + } + ] + } + }) + setupApolloMocks() + + render( + + ) + + await screen.findByRole('button', { name: 'Create Sync' }) + + fireEvent.mouseDown( + screen.getByRole('combobox', { name: 'Integration account' }) + ) + + const options = screen.getAllByRole('option') + expect(options).toHaveLength(1) + expect(options[0]).toHaveTextContent('Select integration account') + }) }) diff --git a/apps/journeys-admin/src/components/JourneyVisitorsList/FilterDrawer/GoogleSheetsSyncDialog/GoogleSheetsSyncDialog.tsx b/apps/journeys-admin/src/components/JourneyVisitorsList/FilterDrawer/GoogleSheetsSyncDialog/GoogleSheetsSyncDialog.tsx index 6655fd72ac4..fc2a63c076e 100644 --- a/apps/journeys-admin/src/components/JourneyVisitorsList/FilterDrawer/GoogleSheetsSyncDialog/GoogleSheetsSyncDialog.tsx +++ b/apps/journeys-admin/src/components/JourneyVisitorsList/FilterDrawer/GoogleSheetsSyncDialog/GoogleSheetsSyncDialog.tsx @@ -39,6 +39,8 @@ import ChevronDown from '@core/shared/ui/icons/ChevronDown' import Plus2Icon from '@core/shared/ui/icons/Plus2' import Trash2Icon from '@core/shared/ui/icons/Trash2' +import { GetIntegration_integrations_IntegrationGoogle } from '../../../../../__generated__/GetIntegration' +import { useAuth } from '../../../../libs/auth' import { getGoogleOAuthUrl } from '../../../../libs/googleOAuthUrl' import { useIntegrationQuery } from '../../../../libs/useIntegrationQuery/useIntegrationQuery' @@ -158,6 +160,7 @@ export function GoogleSheetsSyncDialog({ const router = useRouter() const theme = useTheme() const isMobile = useMediaQuery(theme.breakpoints.down('sm')) + const { user } = useAuth() const { data: journeyData } = useQuery(GET_JOURNEY_CREATED_AT, { variables: { id: journeyId } @@ -166,6 +169,18 @@ export function GoogleSheetsSyncDialog({ teamId: journeyData?.journey?.team?.id as string }) + const currentUserId = user?.id + const currentUserIntegrations = + currentUserId != null + ? (integrationsData?.integrations?.filter( + ( + integration + ): integration is GetIntegration_integrations_IntegrationGoogle => + integration.__typename === 'IntegrationGoogle' && + integration.user?.id === currentUserId + ) ?? []) + : [] + const [googleDialogOpen, setGoogleDialogOpen] = useState(false) const [pickerActive, setPickerActive] = useState(false) @@ -1189,28 +1204,20 @@ export function GoogleSheetsSyncDialog({ renderValue={(selected) => { if (selected === '') return t('Select integration account') - const found = integrationsData?.integrations.find( + const found = currentUserIntegrations.find( (integration) => integration.id === selected ) - if (found?.__typename === 'IntegrationGoogle') { - return found.accountEmail ?? t('Unknown email') - } - return t('Unknown integration') + return found?.accountEmail ?? t('Unknown email') }} > {t('Select integration account')} - {integrationsData?.integrations - ?.filter( - (integration) => - integration.__typename === 'IntegrationGoogle' - ) - .map((integration) => ( - - {integration.accountEmail ?? t('Unknown email')} - - ))} + {currentUserIntegrations.map((integration) => ( + + {integration.accountEmail ?? t('Unknown email')} + + ))} {touched.integrationId != null && errors.integrationId != null && ( diff --git a/libs/locales/en/apps-journeys-admin.json b/libs/locales/en/apps-journeys-admin.json index 4b91d59efa9..9f90da7bba0 100644 --- a/libs/locales/en/apps-journeys-admin.json +++ b/libs/locales/en/apps-journeys-admin.json @@ -835,7 +835,6 @@ "Integration account": "Integration account", "Select integration account": "Select integration account", "Unknown email": "Unknown email", - "Unknown integration": "Unknown integration", "Add New Google Account": "Add New Google Account", "Spreadsheet Setup": "Spreadsheet Setup", "Destination": "Destination",