-
+ const AdminInfoComponent = () => {
+ return (
+ <>
+
Don't have an API key?
+
+ ToolJet Copilot
+ is currently in beta and provided on request.
+ Join our waitlist to be notified when API keys become available, or sign up for beta access to get started
+ today.
-
+
handleOnSave(inputValue)}
+ onClick={() => window.open('https://tooljet.com/copilot', '_blank')}
darkMode={darkMode}
- size="md"
- isLoading={isLoading}
- styles={{ backgroundColor: '#3E63DD', color: '#fff' }}
- disabled={!isCopilotEnabled}
+ size="sm"
+ styles={{ width: '100%', fontSize: '12px', fontWeight: 500, borderColor: darkMode && 'transparent' }}
>
-
+
-
+ >
+ );
+ };
+
+ return (
+
+ {isAdmin && (
+
+
+
+ API KEY
+
+
+
+
+
+ handleOnSave(inputValue)}
+ darkMode={darkMode}
+ size="md"
+ isLoading={isLoading}
+ styles={{ backgroundColor: '#3E63DD', color: '#fff' }}
+ disabled={!isCopilotEnabled}
+ >
+
+
+
+
+ )}
- Don't have an API key?
-
- ToolJet Copilot
- is currently in beta and provided on request.
- Join our waitlist to be notified when API keys become available, or sign up for beta access to get started
- today.
-
-
- window.open('https://tooljet.com/copilot', '_blank')}
- darkMode={darkMode}
- size="sm"
- styles={{ width: '100%', fontSize: '12px', fontWeight: 500, borderColor: darkMode && 'transparent' }}
- >
-
-
-
+ {isAdmin ? (
+
+ ) : (
+ <>
+
+ ToolJet Copilot
+ is currently in beta and provided on
+ request. Join our waitlist to be notified when API keys become available, or sign up for beta access to
+ get started today.
+
+
+ Please note :
+ Copilot functionality is dependent on your workspace admin completing the setup process.
+
+ >
+ )}
diff --git a/frontend/src/CopilotSettings/CopilotSetting.jsx b/frontend/src/CopilotSettings/CopilotSetting.jsx
index f4940befa3..b49ae1194d 100644
--- a/frontend/src/CopilotSettings/CopilotSetting.jsx
+++ b/frontend/src/CopilotSettings/CopilotSetting.jsx
@@ -8,10 +8,12 @@ import { Button } from '@/_ui/LeftSidebar';
import { useLocalStorageState } from '@/_hooks/use-local-storage';
export const CopilotSetting = () => {
- const { current_organization_id } = authenticationService.currentSessionValue;
+ const { current_organization_id, current_organization_name, admin } = authenticationService.currentSessionValue;
+ const currentOrgName = current_organization_name.replace(/\s/g, '').toLowerCase();
+
const [copilotApiKey, setCopilotApiKey] = useState('');
const [isLoading, setIsLoading] = useState(false);
- const [state, setState] = useLocalStorageState(`copilotEnabled-${current_organization_id}`, false);
+ const [state, setState] = useLocalStorageState(`copilotEnabled-${currentOrgName}`, false);
const [copilotWorkspaceVarId, set] = useState(null);
const saveCopilotApiKey = async (apikey) => {
@@ -30,7 +32,10 @@ export const CopilotSetting = () => {
console.log(err);
return toast.error('Something went wrong');
})
- .finally(() => setIsLoading(false));
+ .finally(() => {
+ setIsLoading(false);
+ orgEnvironmentVariableService.create(`copilot_enabled-${current_organization_id}`, 'true', 'client', false);
+ });
}
if (isCopilotApiKeyPresent === true && copilotWorkspaceVarId) {
@@ -58,7 +63,7 @@ export const CopilotSetting = () => {
const validateApiKey = (apiKey) => {
return new Promise((resolve, reject) => {
copilotService
- .validateCopilotAPIKey(apiKey)
+ .validateCopilotAPIKey(apiKey, current_organization_id)
.then(({ status }) => {
if (status === 'ok') {
return resolve(true);
@@ -72,33 +77,56 @@ export const CopilotSetting = () => {
});
};
- useEffect(() => {
- if (!state) {
- return;
- }
-
- orgEnvironmentVariableService.getVariables().then((data) => {
- const isCopilotApiKeyPresent = data.variables.some(
- (variable) => variable.variable_name === `copilot_api_key-${current_organization_id}`
- );
-
- const shouldUpdate = isCopilotApiKeyPresent;
- if (shouldUpdate) {
- const copilotVariableId = data.variables.find(
- (variable) => variable.variable_name === `copilot_api_key-${current_organization_id}`
- )?.id;
- const key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
- set(copilotVariableId);
- setCopilotApiKey(key);
- }
+ const updateCopilotEnabled = (id, value, variableName) => {
+ return orgEnvironmentVariableService.update(id, variableName, `${value}`).catch((err) => {
+ console.log(err);
});
+ };
+
+ useEffect(() => {
+ if (!admin) {
+ orgEnvironmentVariableService.getVariables().then((data) => {
+ const { value } = data.variables.find(
+ (variable) => variable.variable_name === `copilot_enabled-${current_organization_id}`
+ );
+
+ setState(value === 'true');
+ });
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ useEffect(() => {
+ if (admin) {
+ orgEnvironmentVariableService.getVariables().then((data) => {
+ const isCopilotApiKeyPresent = data.variables.some(
+ (variable) => variable.variable_name === `copilot_api_key-${current_organization_id}`
+ );
+
+ const { id, variable_name, value } = data.variables.find(
+ (variable) => variable.variable_name === `copilot_enabled-${current_organization_id}`
+ );
+
+ if (value !== `${state}`) updateCopilotEnabled(id, state, variable_name);
+
+ const shouldUpdate = state && isCopilotApiKeyPresent;
+ if (shouldUpdate) {
+ const copilotVariableId = data.variables.find(
+ (variable) => variable.variable_name === `copilot_api_key-${current_organization_id}`
+ )?.id;
+ const key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
+ set(copilotVariableId);
+ setCopilotApiKey(key);
+ }
+ });
+ }
return () => {
setCopilotApiKey('');
set(null);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
+ }, [state]);
const darkMode = localStorage.getItem('darkMode') === 'true';
@@ -111,7 +139,7 @@ export const CopilotSetting = () => {
padding: '4rem',
}}
>
-
+
{
isLoading={isLoading}
darkMode={darkMode}
isCopilotEnabled={state}
+ isAdmin={admin}
/>
@@ -130,7 +159,7 @@ export const CopilotSetting = () => {
);
};
-const Container = ({ children, isCopilotEnabled, handleCopilotToggle, darkMode }) => {
+const Container = ({ children, isCopilotEnabled, handleCopilotToggle, darkMode, isAdmin }) => {
return (
@@ -151,6 +180,7 @@ const Container = ({ children, isCopilotEnabled, handleCopilotToggle, darkMode }
toggleSwitchFunction={handleCopilotToggle}
action="enableTransformation"
dataCy={'copilot'}
+ disabled={!isAdmin}
/>
diff --git a/frontend/src/Editor/CodeBuilder/CodeHinter.jsx b/frontend/src/Editor/CodeBuilder/CodeHinter.jsx
index 2729c35c48..55d10832ef 100644
--- a/frontend/src/Editor/CodeBuilder/CodeHinter.jsx
+++ b/frontend/src/Editor/CodeBuilder/CodeHinter.jsx
@@ -69,6 +69,7 @@ export function CodeHinter({
popOverCallback,
cyLabel = '',
callgpt = () => null,
+ isCopilotEnabled = false,
}) {
const darkMode = localStorage.getItem('darkMode') === 'true';
const options = {
@@ -323,6 +324,7 @@ export function CodeHinter({
/>
)}
{
return (
-
+
@@ -31,6 +38,13 @@ export const CustomToggleSwitch = ({
{label}
)}
+ {disabled && dataCy === 'copilot' && (
+
+ )}
);
};
diff --git a/frontend/src/Editor/QueryManager/Transformation.jsx b/frontend/src/Editor/QueryManager/Transformation.jsx
index e2f321ab4f..881f67e131 100644
--- a/frontend/src/Editor/QueryManager/Transformation.jsx
+++ b/frontend/src/Editor/QueryManager/Transformation.jsx
@@ -19,7 +19,8 @@ import { authenticationService } from '@/_services';
export const Transformation = ({ changeOption, currentState, options, darkMode, queryId }) => {
const { t } = useTranslation();
- const { current_organization_id } = authenticationService.currentSessionValue;
+ const { current_organization_name } = authenticationService.currentSessionValue;
+ const currentOrgName = current_organization_name.replace(/\s/g, '').toLowerCase();
const [lang, setLang] = React.useState(options?.transformationLanguage ?? 'javascript');
@@ -39,7 +40,7 @@ return [row for row in data if row['amount'] > 1000]
const [state, setState] = useLocalStorageState('transformation', defaultValue);
const [fetchingRecommendation, setFetchingRecommendation] = useState(false);
- const isCopilotEnabled = localStorage.getItem(`copilotEnabled-${current_organization_id}`) === 'true';
+ const isCopilotEnabled = localStorage.getItem(`copilotEnabled-${currentOrgName}`) === 'true';
const handleCallToGPT = async () => {
setFetchingRecommendation(true);
@@ -291,7 +292,7 @@ return [row for row in data if row['amount'] > 1000]
styles={{ width: '100%', fontSize: '12px', fontWeight: 500, borderColor: darkMode && 'transparent' }}
disabled={!isCopilotEnabled}
>
-
+
@@ -317,6 +318,7 @@ return [row for row in data if row['amount'] > 1000]
componentName={`transformation`}
cyLabel={'transformation-input'}
callgpt={handleCallToGPT}
+ isCopilotEnabled={isCopilotEnabled}
/>
)}
diff --git a/frontend/src/ManageOrgVars/ManageOrgVars.jsx b/frontend/src/ManageOrgVars/ManageOrgVars.jsx
index 40d09de3b7..ba435bea8b 100644
--- a/frontend/src/ManageOrgVars/ManageOrgVars.jsx
+++ b/frontend/src/ManageOrgVars/ManageOrgVars.jsx
@@ -74,9 +74,7 @@ class ManageOrgVarsComponent extends React.Component {
});
orgEnvironmentVariableService.getVariables().then((data) => {
- const variables = _.cloneDeep(data.variables)?.filter(
- ({ variable_name }) => !/copilot_api_key/.test(variable_name)
- );
+ const variables = _.cloneDeep(data.variables)?.filter(({ variable_name }) => !/copilot_/.test(variable_name));
this.setState({
variables: variables,
isLoading: false,
diff --git a/frontend/src/OrganizationSettingsPage/index.jsx b/frontend/src/OrganizationSettingsPage/index.jsx
index 23a8b7b206..519f646f82 100644
--- a/frontend/src/OrganizationSettingsPage/index.jsx
+++ b/frontend/src/OrganizationSettingsPage/index.jsx
@@ -53,7 +53,7 @@ export function OrganizationSettings(props) {
{sideBarNavs.map((item, index) => {
return (
<>
- {(admin || item == 'Workspace variables') && (
+ {(admin || item == 'Workspace variables' || item == 'Copilot') && (
{
- const { isOpen, trigger, styles, className, componentName, dragResizePortal, callgpt } = restProps;
+ const { isOpen, trigger, styles, className, componentName, dragResizePortal, callgpt, isCopilotEnabled } = restProps;
+
const [name, setName] = React.useState(componentName);
const handleClose = (e) => {
e.stopPropagation();
@@ -45,6 +46,7 @@ const Portal = ({ children, ...restProps }) => {
componentName={name}
dragResizePortal={dragResizePortal}
callgpt={callgpt}
+ isCopilotEnabled={isCopilotEnabled}
>
{children}
@@ -57,7 +59,17 @@ const Container = ({ children, ...restProps }) => {
return {children} ;
};
-const Modal = ({ children, handleClose, portalStyles, styles, componentName, darkMode, dragResizePortal, callgpt }) => {
+const Modal = ({
+ children,
+ handleClose,
+ portalStyles,
+ styles,
+ componentName,
+ darkMode,
+ dragResizePortal,
+ callgpt,
+ isCopilotEnabled,
+}) => {
const [loading, setLoading] = React.useState(false);
const handleCallGpt = () => {
@@ -66,7 +78,6 @@ const Modal = ({ children, handleClose, portalStyles, styles, componentName, dar
callgpt().then(() => setLoading(false));
};
- const isCopilotEnabled = localStorage.getItem('copilotEnabled') === 'true';
const includeGPT = ['Runjs', 'Runpy', 'transformation'].includes(componentName) && isCopilotEnabled;
const renderModalContent = () => (
@@ -95,7 +106,7 @@ const Modal = ({ children, handleClose, portalStyles, styles, componentName, dar
classNames={`${loading ? (darkMode ? 'btn-loading' : 'button-loading') : ''}`}
styles={{ width: '100%', fontSize: '12px', fontWeight: 500, borderColor: darkMode && 'transparent' }}
>
-
+
)}
diff --git a/frontend/src/_hooks/use-portal.jsx b/frontend/src/_hooks/use-portal.jsx
index acfaa79fef..35d83174ed 100644
--- a/frontend/src/_hooks/use-portal.jsx
+++ b/frontend/src/_hooks/use-portal.jsx
@@ -13,6 +13,7 @@ const usePortal = ({ children, ...restProps }) => {
selectors = {},
dragResizePortal = false,
callgpt,
+ isCopilotEnabled = false,
} = restProps;
const renderCustomComponent = ({ component, ...restProps }) => {
@@ -38,6 +39,7 @@ const usePortal = ({ children, ...restProps }) => {
componentName={componentName}
dragResizePortal={dragResizePortal}
callgpt={callgpt}
+ isCopilotEnabled={isCopilotEnabled}
>
{React.cloneElement(children, { ...styleProps })}
diff --git a/frontend/src/_services/copilot.service.js b/frontend/src/_services/copilot.service.js
index b1cb01e4f1..776edb7078 100644
--- a/frontend/src/_services/copilot.service.js
+++ b/frontend/src/_services/copilot.service.js
@@ -20,9 +20,10 @@ async function getCopilotRecommendations(options) {
return data || {};
}
-function validateCopilotAPIKey(key) {
+function validateCopilotAPIKey(key, organizationId) {
const body = {
secretKey: key,
+ organizationId,
};
const requestOptions = { method: 'POST', headers: authHeader(), credentials: 'include', body: JSON.stringify(body) };
diff --git a/server/src/controllers/copilot.controller.ts b/server/src/controllers/copilot.controller.ts
index deda4084f2..e95f7320a5 100644
--- a/server/src/controllers/copilot.controller.ts
+++ b/server/src/controllers/copilot.controller.ts
@@ -30,7 +30,7 @@ export class CopilotController {
@UseGuards(JwtAuthGuard)
@Post('api-key')
- async validateCopilotAPIKey(@User() user, @Body() body: { secretKey: string }) {
- return await this.copilotService.validateCopilotAPIKey(user.id, body.secretKey);
+ async validateCopilotAPIKey(@User() user, @Body() body: { secretKey: string; organizationId: string }) {
+ return await this.copilotService.validateCopilotAPIKey(body.organizationId, body.secretKey);
}
}
diff --git a/server/src/services/copilot.service.ts b/server/src/services/copilot.service.ts
index bce7357fde..a23b4e8ea3 100644
--- a/server/src/services/copilot.service.ts
+++ b/server/src/services/copilot.service.ts
@@ -32,7 +32,7 @@ export class CopilotService {
query: query,
context: context,
language: language,
- userId: userId,
+ workspaceId: orgnaizationId,
}),
});
@@ -42,13 +42,13 @@ export class CopilotService {
};
}
- async validateCopilotAPIKey(userId: string, secretKey: string) {
+ async validateCopilotAPIKey(workspaceId: string, secretKey: string) {
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
- body: JSON.stringify({ userId: userId, action: 'get', apiKey: secretKey }),
+ body: JSON.stringify({ workspaceId: workspaceId, action: 'get', apiKey: secretKey }),
};
const response = await fetch(`${process.env.COPILOT_API_ENDPOINT}/api-key`, options);