Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
c2025de
moving prefs up nit from prev ticket
ruchitharajaghatta Nov 8, 2024
c8ae0da
new modal and plugin
ruchitharajaghatta Nov 13, 2024
ae6fa98
removing console log
ruchitharajaghatta Nov 13, 2024
ab8a8eb
review comments and checking for userPref
ruchitharajaghatta Nov 13, 2024
842007a
review comments and checking for userPref
ruchitharajaghatta Nov 13, 2024
be9d03d
PR comments
ruchitharajaghatta Nov 14, 2024
f116d9e
PR comments
ruchitharajaghatta Nov 14, 2024
ee684c7
reducer name change bug
ruchitharajaghatta Nov 14, 2024
cdbfa92
fixing post request
ruchitharajaghatta Nov 14, 2024
c09dd67
fixing test setup failures
ruchitharajaghatta Nov 14, 2024
8cda9e9
new test and sign in test fixes
ruchitharajaghatta Nov 14, 2024
102306b
Merge branch 'main' of github.com:mongodb-js/compass into COMPASS-8378
ruchitharajaghatta Nov 14, 2024
514816c
test fixes and package.json fix
ruchitharajaghatta Nov 15, 2024
59bdf04
state name update
ruchitharajaghatta Nov 15, 2024
d2c0290
test and reducer bug fixes
ruchitharajaghatta Nov 15, 2024
3803589
commenting out errors for evg patch
ruchitharajaghatta Nov 15, 2024
ce2cccd
fixing reducer type and entrypoint
ruchitharajaghatta Nov 18, 2024
b41dbda
npm check fix
ruchitharajaghatta Nov 18, 2024
618d1de
removing ts-expect-errors
ruchitharajaghatta Nov 18, 2024
222487e
Merge branch 'main' of github.com:mongodb-js/compass into COMPASS-8378
ruchitharajaghatta Nov 18, 2024
5feb8fd
prettier fix
ruchitharajaghatta Nov 18, 2024
ce93dd2
taking out duplicated function
ruchitharajaghatta Nov 18, 2024
587e4ec
addressing changes to modal
ruchitharajaghatta Nov 19, 2024
e0c897d
fixing flag for disabling opt in
ruchitharajaghatta Nov 19, 2024
5fbf83f
nit:
ruchitharajaghatta Nov 19, 2024
0f554dc
nits and fixing optin modal/refctoring for projid
ruchitharajaghatta Nov 20, 2024
712aefb
merge main
ruchitharajaghatta Nov 20, 2024
072e8d6
fixing projectID prop
ruchitharajaghatta Nov 20, 2024
b69f37f
optin modal test
ruchitharajaghatta Nov 21, 2024
f93424a
test tweak
ruchitharajaghatta Nov 21, 2024
baedb84
nit comment
ruchitharajaghatta Nov 21, 2024
2860198
Merge branch 'main' of github.com:mongodb-js/compass into COMPASS-8378
ruchitharajaghatta Nov 22, 2024
70d3566
fixing projectId
ruchitharajaghatta Nov 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion packages/compass-generative-ai/src/atlas-ai-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import type { Document } from 'mongodb';
import type { Logger } from '@mongodb-js/compass-logging';
import { EJSON } from 'bson';
import { signIntoAtlasWithModalPrompt } from './store/atlas-signin-reducer';
import { getStore } from './store/atlas-signin-store';
import { getStore } from './store/atlas-ai-store';
import { optIntoGenAIWithModalPrompt } from './store/atlas-optin-reducer';

type GenerativeAiInput = {
userInput: string;
Expand Down Expand Up @@ -329,6 +330,10 @@ export class AtlasAiService {
async ensureAiFeatureAccess({ signal }: { signal?: AbortSignal } = {}) {
// When the ai feature is attempted to be opened we make sure
// the user is signed into Atlas and opted in.

if (this.apiURLPreset === 'cloud') {
return getStore().dispatch(optIntoGenAIWithModalPrompt({ signal }));
}
return getStore().dispatch(signIntoAtlasWithModalPrompt({ signal }));
}

Expand Down Expand Up @@ -437,6 +442,26 @@ export class AtlasAiService {
);
}

// Performs a post request to atlas to set the user opt in preference to true.
async optIntoGenAIFeaturesAtlas() {
await this.atlasService.authenticatedFetch(
this.atlasService.cloudEndpoint(
'/settings/optInDataExplorerGenAIFeatures'
),
{
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json',
},
body: new URLSearchParams([['value', 'true']]),
}
);
await this.preferences.savePreferences({
optInDataExplorerGenAIFeatures: true,
});
}

private validateAIFeatureEnablementResponse(
response: any
): asserts response is AIFeatureEnablement {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const bannerStyles = css({
height: 263,
});

export const AISignInImageBanner = () => {
export const AiImageBanner = () => {
return (
<svg
className={bannerStyles}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import React from 'react';
import { render, screen, cleanup } from '@mongodb-js/testing-library-compass';
import { expect } from 'chai';
import { AIOptInModal } from './ai-optin-modal';
import type { PreferencesAccess } from 'compass-preferences-model';
import { createSandboxFromDefaultPreferences } from 'compass-preferences-model';
import { PreferencesProvider } from 'compass-preferences-model/provider';

let mockPreferences: PreferencesAccess;

describe('AIOptInModal Component', function () {
beforeEach(async function () {
mockPreferences = await createSandboxFromDefaultPreferences();
});

afterEach(function () {
cleanup();
});

it('should show the modal title', function () {
render(
<PreferencesProvider value={mockPreferences}>
<AIOptInModal
projectId="ab123"
isOptInModalVisible={true}
isOptInInProgress={false}
onOptInModalClose={() => {}}
onOptInClick={() => {}}
></AIOptInModal>
</PreferencesProvider>
);
expect(
screen.getByRole('heading', {
name: 'Use natural language to generate queries and pipelines',
})
).to.exist;
});
it('should show the cancel button', function () {
render(
<PreferencesProvider value={mockPreferences}>
<AIOptInModal
projectId="ab123"
isOptInModalVisible={true}
isOptInInProgress={false}
onOptInModalClose={() => {}}
onOptInClick={() => {}}
>
{' '}
</AIOptInModal>
</PreferencesProvider>
);
const button = screen.getByText('Cancel').closest('button');
expect(button).to.not.match('disabled');
});

it('should show the opt in button enabled when project AI setting is enabled', async function () {
await mockPreferences.savePreferences({
enableGenAIFeaturesAtlasProject: true,
});
render(
<PreferencesProvider value={mockPreferences}>
<AIOptInModal
projectId="ab123"
isOptInModalVisible={true}
isOptInInProgress={false}
onOptInModalClose={() => {}}
onOptInClick={() => {}}
>
{' '}
</AIOptInModal>
</PreferencesProvider>
);
const button = screen.getByText('Use Natural Language').closest('button');
expect(button?.getAttribute('aria-disabled')).to.equal('false');
});

it('should disable the opt in button if project AI setting is disabled ', async function () {
await mockPreferences.savePreferences({
enableGenAIFeaturesAtlasProject: false,
});
render(
<PreferencesProvider value={mockPreferences}>
<AIOptInModal
projectId="ab123"
isOptInModalVisible={true}
isOptInInProgress={false}
onOptInModalClose={() => {}}
onOptInClick={() => {}}
>
{' '}
</AIOptInModal>
</PreferencesProvider>
);
const button = screen.getByText('Use Natural Language').closest('button');
expect(button?.getAttribute('aria-disabled')).to.equal('true');
});
});
150 changes: 150 additions & 0 deletions packages/compass-generative-ai/src/components/ai-optin-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import React from 'react';
import { connect } from 'react-redux';
import {
Banner,
Body,
Link,
ConfirmationModal,
SpinLoader,
css,
spacing,
H3,
palette,
} from '@mongodb-js/compass-components';
import { AiImageBanner } from './ai-image-banner';
import { closeOptInModal, optIn } from '../store/atlas-optin-reducer';
import type { RootState } from '../store/atlas-ai-store';
import { usePreference } from 'compass-preferences-model/provider';

const GEN_AI_FAQ_LINK = 'https://www.mongodb.com/docs/generative-ai-faq/';

type OptInModalProps = {
isOptInModalVisible: boolean;
isOptInInProgress: boolean;
onOptInModalClose: () => void;
onOptInClick: () => void;
projectId?: string;
};

const titleStyles = css({
marginBottom: spacing[400],
marginTop: spacing[400],
marginLeft: spacing[500],
marginRight: spacing[500],
textAlign: 'center',
});

const bodyStyles = css({
marginBottom: spacing[400],
marginTop: spacing[400],
marginLeft: spacing[300],
marginRight: spacing[300],
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
textAlign: 'center',
});

const disclaimerStyles = css({
color: palette.gray.dark1,
marginTop: spacing[400],
marginLeft: spacing[800],
marginRight: spacing[800],
});

const bannerStyles = css({
padding: spacing[400],
marginTop: spacing[400],
textAlign: 'left',
});
const getButtonText = (isOptInInProgress: boolean) => {
return (
<>
&nbsp;Use Natural Language
{isOptInInProgress && (
<>
&nbsp;
<SpinLoader darkMode={true}></SpinLoader>
</>
)}
</>
);
};

export const AIOptInModal: React.FunctionComponent<OptInModalProps> = ({
isOptInModalVisible,
isOptInInProgress,
onOptInModalClose,
onOptInClick,
projectId,
}) => {
const isProjectAIEnabled = usePreference('enableGenAIFeaturesAtlasProject');
const PROJECT_SETTINGS_LINK = projectId
? window.location.origin + '/v2/' + projectId + '#/settings/groupSettings'
: null;

const onConfirmClick = () => {
if (isOptInInProgress) {
return;
}
onOptInClick();
};
return (
<ConfirmationModal
open={isOptInModalVisible}
title=""
confirmButtonProps={{
children: getButtonText(isOptInInProgress),
disabled: !isProjectAIEnabled,
onClick: onConfirmClick,
}}
cancelButtonProps={{
onClick: onOptInModalClose,
}}
>
<Body className={bodyStyles}>
<AiImageBanner></AiImageBanner>
<H3 className={titleStyles}>
Use natural language to generate queries and pipelines
</H3>
Atlas users can now quickly create queries and aggregations with
MongoDB&apos;s&nbsp; intelligent AI-powered feature, available today.
<Banner
variant={isProjectAIEnabled ? 'info' : 'warning'}
className={bannerStyles}
>
{isProjectAIEnabled
? 'AI features are enabled for project users with data access.'
: 'AI features are disabled for project users.'}{' '}
Project Owners can change this setting in the{' '}
{PROJECT_SETTINGS_LINK !== null ? (
<Link href={PROJECT_SETTINGS_LINK} target="_blank">
AI features
</Link>
) : (
'AI features '
)}
section.
</Banner>
<div className={disclaimerStyles}>
This is a feature powered by generative AI, and may give inaccurate
responses. Please see our{' '}
<Link hideExternalIcon={false} href={GEN_AI_FAQ_LINK} target="_blank">
FAQ
</Link>{' '}
for more information.
</div>
</Body>
</ConfirmationModal>
);
};

export default connect(
(state: RootState) => {
return {
isOptInModalVisible: state.optIn.isModalOpen,
isOptInInProgress: state.optIn.state === 'in-progress',
};
},
{ onOptInModalClose: closeOptInModal, onOptInClick: optIn }
)(AIOptInModal);
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import {
spacing,
useDarkMode,
} from '@mongodb-js/compass-components';
import { AISignInImageBanner } from './ai-signin-banner-image';
import type { AtlasSignInState } from '../../store/atlas-signin-reducer';
import { closeSignInModal, signIn } from '../../store/atlas-signin-reducer';
import { AiImageBanner } from './ai-image-banner';
import { closeSignInModal, signIn } from '../store/atlas-signin-reducer';
import type { RootState } from '../store/atlas-ai-store';

const GEN_AI_FAQ_LINK = 'https://www.mongodb.com/docs/generative-ai-faq/';

Expand All @@ -30,7 +30,7 @@ const titleStyles = css({
alignItems: 'center',
});

const disclaimer = css({
const disclaimerStyles = css({
padding: `0 ${spacing[900]}px`,
});

Expand All @@ -46,7 +46,7 @@ const AISignInModal: React.FunctionComponent<SignInModalProps> = ({
<MarketingModal
darkMode={darkMode}
disclaimer={
<div className={disclaimer}>
<div className={disclaimerStyles}>
This is a feature powered by generative AI, and may give inaccurate
responses. Please see our{' '}
<Link hideExternalIcon={false} href={GEN_AI_FAQ_LINK} target="_blank">
Expand All @@ -55,7 +55,7 @@ const AISignInModal: React.FunctionComponent<SignInModalProps> = ({
for more information.
</div>
}
graphic={<AISignInImageBanner></AISignInImageBanner>}
graphic={<AiImageBanner></AiImageBanner>}
title={
<div className={titleStyles}>
Use natural language to generate queries and pipelines
Expand Down Expand Up @@ -100,10 +100,10 @@ const AISignInModal: React.FunctionComponent<SignInModalProps> = ({
};

export default connect(
(state: AtlasSignInState) => {
(state: RootState) => {
return {
isSignInModalVisible: state.isModalOpen,
isSignInInProgress: state.state === 'in-progress',
isSignInModalVisible: state.signIn.isModalOpen,
isSignInInProgress: state.signIn.state === 'in-progress',
};
},
{ onSignInModalClose: closeSignInModal, onSignInClick: signIn }
Expand Down

This file was deleted.

2 changes: 1 addition & 1 deletion packages/compass-generative-ai/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ export {
AIExperienceEntry,
createAIPlaceholderHTMLPlaceholder,
} from './ai-experience-entry';
export { AtlasSignIn } from './atlas-signin';
export { AtlasAiPlugin } from './plugin';
19 changes: 19 additions & 0 deletions packages/compass-generative-ai/src/components/plugin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';
import AISignInModal from './ai-signin-modal';
import AIOptInModal from './ai-optin-modal';
import { ConfirmationModalArea } from '@mongodb-js/compass-components';

export interface AtlasAiPluginProps {
projectId?: string;
}

export const AtlasAiPlugin: React.FunctionComponent<AtlasAiPluginProps> = ({
projectId,
}) => {
return (
<ConfirmationModalArea>
<AISignInModal></AISignInModal>
<AIOptInModal projectId={projectId}></AIOptInModal>
</ConfirmationModalArea>
);
};
Loading
Loading