mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-24 09:28:31 +00:00
Added option for query permission in the popup and integration app permissions modal
This commit is contained in:
parent
cbf908362c
commit
c2fc4fe60f
4 changed files with 78 additions and 13 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
import React, { useState, useCallback } from 'react';
|
import React, { useState, useCallback } from 'react';
|
||||||
import { Tooltip } from 'react-tooltip';
|
import { Tooltip } from 'react-tooltip';
|
||||||
|
import { ToolTip } from '@/_components/ToolTip';
|
||||||
import { updateQuerySuggestions } from '@/_helpers/appUtils';
|
import { updateQuerySuggestions } from '@/_helpers/appUtils';
|
||||||
// import { Confirm } from '../Viewer/Confirm';
|
// import { Confirm } from '../Viewer/Confirm';
|
||||||
import { toast } from 'react-hot-toast';
|
import { toast } from 'react-hot-toast';
|
||||||
|
|
@ -18,6 +19,7 @@ import Edit from '@/_ui/Icon/bulkIcons/Edit';
|
||||||
import Trash from '@/_ui/Icon/solidIcons/Trash';
|
import Trash from '@/_ui/Icon/solidIcons/Trash';
|
||||||
import { OverlayTrigger, Popover } from 'react-bootstrap';
|
import { OverlayTrigger, Popover } from 'react-bootstrap';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import SolidIcon from '@/_ui/Icon/SolidIcons';
|
||||||
|
|
||||||
export const QueryCard = ({ dataQuery, darkMode = false, localDs }) => {
|
export const QueryCard = ({ dataQuery, darkMode = false, localDs }) => {
|
||||||
const appId = useStore((state) => state.app.appId);
|
const appId = useStore((state) => state.app.appId);
|
||||||
|
|
@ -31,6 +33,7 @@ export const QueryCard = ({ dataQuery, darkMode = false, localDs }) => {
|
||||||
const deleteDataQueries = useStore((state) => state.dataQuery.deleteDataQueries);
|
const deleteDataQueries = useStore((state) => state.dataQuery.deleteDataQueries);
|
||||||
const duplicateQuery = useStore((state) => state.dataQuery.duplicateQuery);
|
const duplicateQuery = useStore((state) => state.dataQuery.duplicateQuery);
|
||||||
const setPreviewData = useStore((state) => state.queryPanel.setPreviewData);
|
const setPreviewData = useStore((state) => state.queryPanel.setPreviewData);
|
||||||
|
const toggleQueryPermissionModal = useStore((state) => state.queryPanel.toggleQueryPermissionModal);
|
||||||
const [showDeleteConfirmation, setShowDeleteConfirmation] = useState(false);
|
const [showDeleteConfirmation, setShowDeleteConfirmation] = useState(false);
|
||||||
const [showQueryMenu, setShowQueryMenu] = useState(false);
|
const [showQueryMenu, setShowQueryMenu] = useState(false);
|
||||||
const hasPermissions =
|
const hasPermissions =
|
||||||
|
|
@ -40,6 +43,9 @@ export const QueryCard = ({ dataQuery, darkMode = false, localDs }) => {
|
||||||
canDeleteDataSource()
|
canDeleteDataSource()
|
||||||
: true;
|
: true;
|
||||||
|
|
||||||
|
const featureAccess = useStore((state) => state?.license?.featureAccess, shallow);
|
||||||
|
const licenseValid = !featureAccess?.licenseStatus?.isExpired && featureAccess?.licenseStatus?.isLicenseValid;
|
||||||
|
|
||||||
const shouldFreeze = useStore((state) => state.getShouldFreeze());
|
const shouldFreeze = useStore((state) => state.getShouldFreeze());
|
||||||
|
|
||||||
const QUERY_MENU_OPTIONS = [
|
const QUERY_MENU_OPTIONS = [
|
||||||
|
|
@ -47,16 +53,34 @@ export const QueryCard = ({ dataQuery, darkMode = false, localDs }) => {
|
||||||
label: 'Rename',
|
label: 'Rename',
|
||||||
value: 'rename',
|
value: 'rename',
|
||||||
icon: <Edit width={16} />,
|
icon: <Edit width={16} />,
|
||||||
|
showTooltip: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Duplicate',
|
label: 'Duplicate',
|
||||||
value: 'duplicate',
|
value: 'duplicate',
|
||||||
icon: <Copy width={16} />,
|
icon: <Copy width={16} />,
|
||||||
|
showTooltip: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Query permission',
|
||||||
|
value: 'permission',
|
||||||
|
icon: (
|
||||||
|
<img
|
||||||
|
alt="permission-icon"
|
||||||
|
src="assets/images/icons/editor/left-sidebar/authorization.svg"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
trailingIcon: !licenseValid ? <SolidIcon width={16} name="enterprisecrown" className="mx-1" /> : undefined,
|
||||||
|
tooltipText: 'Query permissions are available only in paid plans',
|
||||||
|
showTooltip: !licenseValid,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Delete',
|
label: 'Delete',
|
||||||
value: 'delete',
|
value: 'delete',
|
||||||
icon: <Trash width={16} fill={'#E54D2E'} />,
|
icon: <Trash width={16} fill={'#E54D2E'} />,
|
||||||
|
showTooltip: false,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -67,6 +91,10 @@ export const QueryCard = ({ dataQuery, darkMode = false, localDs }) => {
|
||||||
if (value === 'duplicate') {
|
if (value === 'duplicate') {
|
||||||
debouncedDuplicateQuery(dataQuery?.id, appId);
|
debouncedDuplicateQuery(dataQuery?.id, appId);
|
||||||
}
|
}
|
||||||
|
if (value === 'permission') {
|
||||||
|
if (!licenseValid) return;
|
||||||
|
toggleQueryPermissionModal(true);
|
||||||
|
}
|
||||||
if (value === 'delete') {
|
if (value === 'delete') {
|
||||||
deleteDataQuery();
|
deleteDataQuery();
|
||||||
}
|
}
|
||||||
|
|
@ -198,24 +226,32 @@ export const QueryCard = ({ dataQuery, darkMode = false, localDs }) => {
|
||||||
<Popover id="list-menu" className={darkMode && 'dark-theme'}>
|
<Popover id="list-menu" className={darkMode && 'dark-theme'}>
|
||||||
<Popover.Body bsPrefix="list-item-popover-body">
|
<Popover.Body bsPrefix="list-item-popover-body">
|
||||||
{QUERY_MENU_OPTIONS.map((option) => (
|
{QUERY_MENU_OPTIONS.map((option) => (
|
||||||
<div
|
<ToolTip
|
||||||
data-cy={`query-menu-${String(option?.value).toLowerCase()}-button`}
|
|
||||||
className="list-item-popover-option"
|
|
||||||
key={option?.value}
|
key={option?.value}
|
||||||
onClick={(e) => {
|
message={option?.tooltipText}
|
||||||
e.stopPropagation();
|
placement="right"
|
||||||
handleQueryMenuActions(option.value);
|
show={option?.showTooltip}
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div className="list-item-popover-menu-option-icon">{option.icon}</div>
|
|
||||||
<div
|
<div
|
||||||
className={classNames('list-item-option-menu-label', {
|
data-cy={`query-menu-${String(option?.value).toLowerCase()}-button`}
|
||||||
'color-tomato9': option.value === 'delete',
|
className="list-item-popover-option"
|
||||||
})}
|
key={option?.value}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleQueryMenuActions(option.value);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{option?.label}
|
<div className="list-item-popover-menu-option-icon">{option.icon}</div>
|
||||||
|
<div
|
||||||
|
className={classNames('list-item-option-menu-label', {
|
||||||
|
'color-tomato9': option.value === 'delete',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{option?.label}
|
||||||
|
</div>
|
||||||
|
{option.trailingIcon && option.trailingIcon}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ToolTip>
|
||||||
))}
|
))}
|
||||||
</Popover.Body>
|
</Popover.Body>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,9 @@ import DataSourceSelect from '../QueryManager/Components/DataSourceSelect';
|
||||||
import { OverlayTrigger, Popover } from 'react-bootstrap';
|
import { OverlayTrigger, Popover } from 'react-bootstrap';
|
||||||
import FolderEmpty from '@/_ui/Icon/solidIcons/FolderEmpty';
|
import FolderEmpty from '@/_ui/Icon/solidIcons/FolderEmpty';
|
||||||
import useStore from '@/AppBuilder/_stores/store';
|
import useStore from '@/AppBuilder/_stores/store';
|
||||||
|
import AppPermissionsModal from '@/modules/Appbuilder/components/AppPermissionsModal';
|
||||||
|
import { shallow } from 'zustand/shallow';
|
||||||
|
import { appPermissionService } from '@/_services';
|
||||||
|
|
||||||
export const QueryDataPane = ({ darkMode }) => {
|
export const QueryDataPane = ({ darkMode }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
@ -34,6 +37,11 @@ export const QueryDataPane = ({ darkMode }) => {
|
||||||
function isDataSourceLocal(dataQuery) {
|
function isDataSourceLocal(dataQuery) {
|
||||||
return dataSources.some((dataSource) => dataSource.id === dataQuery.data_source_id);
|
return dataSources.some((dataSource) => dataSource.id === dataQuery.data_source_id);
|
||||||
}
|
}
|
||||||
|
const featureAccess = useStore((state) => state?.license?.featureAccess, shallow);
|
||||||
|
const licenseValid = !featureAccess?.licenseStatus?.isExpired && featureAccess?.licenseStatus?.isLicenseValid;
|
||||||
|
const selectedQuery = useStore((state) => state.queryPanel.selectedQuery);
|
||||||
|
const showQueryPermissionModal = useStore((state) => state.queryPanel.showQueryPermissionModal);
|
||||||
|
const toggleQueryPermissionModal = useStore((state) => state.queryPanel.toggleQueryPermissionModal);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setQueryPanelSearchTerm(searchTermForFilters);
|
setQueryPanelSearchTerm(searchTermForFilters);
|
||||||
|
|
@ -171,6 +179,20 @@ export const QueryDataPane = ({ darkMode }) => {
|
||||||
{filteredQueries.map((query) => (
|
{filteredQueries.map((query) => (
|
||||||
<QueryCard key={query.id} dataQuery={query} darkMode={darkMode} localDs={!!isDataSourceLocal(query)} />
|
<QueryCard key={query.id} dataQuery={query} darkMode={darkMode} localDs={!!isDataSourceLocal(query)} />
|
||||||
))}
|
))}
|
||||||
|
{licenseValid && (
|
||||||
|
<AppPermissionsModal
|
||||||
|
modalType="query"
|
||||||
|
resourceId={selectedQuery?.id}
|
||||||
|
showModal={showQueryPermissionModal}
|
||||||
|
toggleModal={toggleQueryPermissionModal}
|
||||||
|
darkMode={darkMode}
|
||||||
|
fetchPermission={(id, appId) => appPermissionService.getQueryPermission(appId, id)}
|
||||||
|
createPermission={(id, appId, body) => appPermissionService.createQueryPermission(appId, id, body)}
|
||||||
|
updatePermission={(id, appId, body) => appPermissionService.updateQueryPermission(appId, id, body)}
|
||||||
|
deletePermission={(id, appId) => appPermissionService.deleteQueryPermission(appId, id)}
|
||||||
|
// onSuccess={(data) => updateQueryWithPermissions(selectedQuery?.id, data)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
id="query-card-name-tooltip"
|
id="query-card-name-tooltip"
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ const initialState = {
|
||||||
loadingDataQueries: false,
|
loadingDataQueries: false,
|
||||||
isPreviewQueryLoading: false,
|
isPreviewQueryLoading: false,
|
||||||
queryPanelSearchTem: '',
|
queryPanelSearchTem: '',
|
||||||
|
showQueryPermissionModal: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createQueryPanelSlice = (set, get) => ({
|
export const createQueryPanelSlice = (set, get) => ({
|
||||||
|
|
@ -1113,5 +1114,10 @@ export const createQueryPanelSlice = (set, get) => ({
|
||||||
};
|
};
|
||||||
previewQuery(query, false, undefined, moduleId);
|
previewQuery(query, false, undefined, moduleId);
|
||||||
},
|
},
|
||||||
|
toggleQueryPermissionModal: (show) => {
|
||||||
|
set((state) => {
|
||||||
|
state.queryPanel.showQueryPermissionModal = show;
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ button:focus:not(:focus-visible) {
|
||||||
padding: 10px 14px;
|
padding: 10px 14px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--slate3);
|
background-color: var(--slate3);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue