mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-23 08:58:26 +00:00
feat: add crud operatios for page permissions
This commit is contained in:
parent
54ffa99c3f
commit
8c53ac4bab
20 changed files with 850 additions and 215 deletions
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="11" height="14" viewBox="0 0 11 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.33317 3.49967C3.33317 2.30306 4.30322 1.33301 5.49984 1.33301C6.69645 1.33301 7.6665 2.30306 7.6665 3.49967V4.33301H3.33317V3.49967ZM2.33317 4.37981V3.49967C2.33317 1.75077 3.75094 0.333008 5.49984 0.333008C7.24874 0.333008 8.6665 1.75077 8.6665 3.49967V4.37981C9.90027 4.61384 10.8332 5.69781 10.8332 6.99967V10.9997C10.8332 12.4724 9.63926 13.6663 8.1665 13.6663H2.83317C1.36041 13.6663 0.166504 12.4724 0.166504 10.9997V6.99967C0.166504 5.69781 1.09941 4.61384 2.33317 4.37981ZM6.83317 8.99967C6.83317 9.73605 6.23622 10.333 5.49984 10.333C4.76346 10.333 4.1665 9.73605 4.1665 8.99967C4.1665 8.2633 4.76346 7.66634 5.49984 7.66634C6.23622 7.66634 6.83317 8.2633 6.83317 8.99967Z" fill="#ACB2B9"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 855 B |
|
|
@ -20,6 +20,8 @@ export const PageHandlerMenu = ({ darkMode }) => {
|
|||
const toggleDeleteConfirmationModal = useStore((state) => state.toggleDeleteConfirmationModal);
|
||||
const clonePage = useStore((state) => state.clonePage);
|
||||
const markAsHomePage = useStore((state) => state.markAsHomePage);
|
||||
const togglePagePermissionModal = useStore((state) => state.togglePagePermissionModal);
|
||||
|
||||
// const popoverTargetRef = null;
|
||||
// console.log(
|
||||
// {
|
||||
|
|
@ -164,6 +166,16 @@ export const PageHandlerMenu = ({ darkMode }) => {
|
|||
}}
|
||||
disabled={isHomePage}
|
||||
/>
|
||||
<Field
|
||||
id={isDisabled ? 'enable-page' : 'disable-page'}
|
||||
text={isDisabled ? 'Page permission' : 'Page permission'}
|
||||
customClass={'delete-btn'}
|
||||
iconSrc={`assets/images/icons/editor/left-sidebar/authorization.svg`}
|
||||
closeMenu={closeMenu}
|
||||
callback={(id) => {
|
||||
togglePagePermissionModal(true);
|
||||
}}
|
||||
/>
|
||||
<Field
|
||||
id="delete-page"
|
||||
text="Delete page"
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import { EditModal } from './EditModal';
|
|||
import { SettingsModal } from './SettingsModal';
|
||||
import { DeletePageConfirmationModal } from './DeletePageConfirmationModal';
|
||||
import SolidIcon from '@/_ui/Icon/SolidIcons';
|
||||
import PagePermission from './PagePermission';
|
||||
|
||||
export const PageMenu = ({ darkMode, switchPage, pinned, setPinned }) => {
|
||||
const showAddNewPageInput = useStore((state) => state.showAddNewPageInput);
|
||||
|
|
@ -94,6 +95,7 @@ export const PageMenu = ({ darkMode, switchPage, pinned, setPinned }) => {
|
|||
>
|
||||
<div>
|
||||
<PageHandlerMenu darkMode={darkMode} />
|
||||
<PagePermission darkMode={darkMode} />
|
||||
<EditModal darkMode={darkMode} />
|
||||
<SettingsModal darkMode={darkMode} />
|
||||
<DeletePageConfirmationModal darkMode={darkMode} />
|
||||
|
|
|
|||
377
frontend/src/AppBuilder/LeftSidebar/PageMenu/PagePermission.jsx
Normal file
377
frontend/src/AppBuilder/LeftSidebar/PageMenu/PagePermission.jsx
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { components } from 'react-select';
|
||||
import ModalBase from '@/_ui/Modal';
|
||||
import Select from '@/_ui/Select';
|
||||
import SolidIcon from '@/_ui/Icon/SolidIcons';
|
||||
import useStore from '@/AppBuilder/_stores/store';
|
||||
import { appPermissionService } from '@/_services';
|
||||
import { ConfirmDialog } from '@/_components';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
const PERMISSION_TYPES = {
|
||||
single: 'SINGLE',
|
||||
group: 'GROUP',
|
||||
all: 'ALL',
|
||||
};
|
||||
|
||||
export default function PagePermission({ darkMode }) {
|
||||
const showPagePermissionModal = useStore((state) => state.showPagePermissionModal);
|
||||
const togglePagePermissionModal = useStore((state) => state.togglePagePermissionModal);
|
||||
const editingPage = useStore((state) => state.editingPage);
|
||||
const appId = useStore((state) => state.app.appId);
|
||||
const selectedUserGroups = useStore((state) => state.selectedUserGroups);
|
||||
const setSelectedUserGroups = useStore((state) => state.setSelectedUserGroups);
|
||||
const selectedUsers = useStore((state) => state.selectedUsers);
|
||||
const setSelectedUsers = useStore((state) => state.setSelectedUsers);
|
||||
const pagePermission = useStore((state) => state.pagePermission);
|
||||
const setPagePermission = useStore((state) => state.setPagePermission);
|
||||
|
||||
const [pagePermissionType, setPagePermissionType] = useState('all');
|
||||
const [showUserGroupSelect, toggleUserGroupSelect] = useState(false);
|
||||
const [showUsersSelect, toggleUsersSelect] = useState(false);
|
||||
const [showConfirmDelete, setShowConfirmDelete] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
console.log({ editingPage, showUserGroupSelect });
|
||||
|
||||
useEffect(() => {
|
||||
if (!editingPage?.id && !showPagePermissionModal) return;
|
||||
const fetchPagePermission = () => {
|
||||
appPermissionService.getPagePermission(appId, editingPage?.id).then((data) => {
|
||||
if (data) {
|
||||
if (data[0]) {
|
||||
setPagePermissionType(data[0]?.type?.toLowerCase());
|
||||
setPagePermission(data);
|
||||
toggleUserGroupSelect(true);
|
||||
data?.length &&
|
||||
setSelectedUserGroups(
|
||||
data[0]?.users?.map((user) => ({
|
||||
label: user?.permissionGroup?.name,
|
||||
value: user?.permissionGroup?.id,
|
||||
}))
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
fetchPagePermission();
|
||||
}, [appId, editingPage, setPagePermission, setSelectedUserGroups, showPagePermissionModal]);
|
||||
|
||||
const permissionTypeOptions = useMemo(
|
||||
() => [
|
||||
{
|
||||
label: 'All users with access to the app',
|
||||
value: 'all',
|
||||
icon: 'globe',
|
||||
},
|
||||
{
|
||||
label: 'Users',
|
||||
value: 'single',
|
||||
icon: 'user',
|
||||
},
|
||||
{
|
||||
label: 'User groups',
|
||||
value: 'group',
|
||||
icon: 'usergroup',
|
||||
},
|
||||
],
|
||||
[]
|
||||
);
|
||||
console.log({ pagePermission });
|
||||
const handlePermissionTypeChange = (value) => {
|
||||
console.log({ value });
|
||||
switch (value) {
|
||||
case 'group': {
|
||||
toggleUserGroupSelect(true);
|
||||
toggleUsersSelect(false);
|
||||
setPagePermissionType('group');
|
||||
break;
|
||||
}
|
||||
case 'single': {
|
||||
toggleUsersSelect(true);
|
||||
toggleUserGroupSelect(false);
|
||||
setPagePermissionType('single');
|
||||
break;
|
||||
}
|
||||
case 'all': {
|
||||
toggleUsersSelect(false);
|
||||
toggleUserGroupSelect(false);
|
||||
setPagePermissionType('all');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handlePagePermissionModalClose = () => {
|
||||
togglePagePermissionModal(false);
|
||||
toggleUserGroupSelect(false);
|
||||
toggleUsersSelect(false);
|
||||
setPagePermissionType('all');
|
||||
setPagePermission(null);
|
||||
};
|
||||
|
||||
const createPagePermission = () => {
|
||||
const body = {
|
||||
pageId: editingPage?.id,
|
||||
type: PERMISSION_TYPES[pagePermissionType],
|
||||
...(pagePermissionType === 'group'
|
||||
? { groups: selectedUserGroups.map((group) => group?.value) }
|
||||
: { users: selectedUsers.map((user) => user?.value) }),
|
||||
};
|
||||
setIsLoading(true);
|
||||
appPermissionService
|
||||
.createPagePermission(appId, editingPage?.id, body)
|
||||
.then((data) => {
|
||||
console.log({ data });
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error('Permission could not be created. Please try again!');
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false);
|
||||
handlePagePermissionModalClose();
|
||||
toast.success('Permission successfully created!');
|
||||
});
|
||||
};
|
||||
|
||||
const updatePagePermission = () => {
|
||||
const body = {
|
||||
pageId: editingPage?.id,
|
||||
type: PERMISSION_TYPES[pagePermissionType],
|
||||
...(pagePermissionType === 'group'
|
||||
? { groups: selectedUserGroups.map((group) => group?.value) }
|
||||
: { users: selectedUsers.map((user) => user?.value) }),
|
||||
};
|
||||
setIsLoading(true);
|
||||
appPermissionService
|
||||
.updatePagePermission(appId, editingPage?.id, body)
|
||||
.then((data) => {
|
||||
console.log({ data });
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error('Permission could not be updated. Please try again!');
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false);
|
||||
handlePagePermissionModalClose();
|
||||
toast.success('Permission successfully updated!');
|
||||
});
|
||||
};
|
||||
|
||||
const deletePagePermission = () => {
|
||||
setIsLoading(true);
|
||||
appPermissionService
|
||||
.deletePagePermission(appId, editingPage?.id)
|
||||
.then((data) => {
|
||||
console.log({ data });
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error('Permission could not be deleted. Please try again!');
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false);
|
||||
setShowConfirmDelete(false);
|
||||
handlePagePermissionModalClose();
|
||||
toast.success('Permission successfully deleted!');
|
||||
});
|
||||
};
|
||||
|
||||
const renderPermissionTypeOptions = ({ label, icon }) => {
|
||||
return (
|
||||
<div className="row permission-type-select">
|
||||
<div className="col-auto">
|
||||
<SolidIcon width="20" name={icon} />
|
||||
</div>
|
||||
<div className="col">
|
||||
<span>{label}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ModalBase
|
||||
title={
|
||||
<div className="my-3">
|
||||
<span className="tj-text-md font-weight-500">Page permission</span>
|
||||
</div>
|
||||
}
|
||||
handleConfirm={!pagePermission ? createPagePermission : updatePagePermission}
|
||||
show={showPagePermissionModal}
|
||||
isLoading={isLoading}
|
||||
handleClose={handlePagePermissionModalClose}
|
||||
confirmBtnProps={{
|
||||
title: pagePermission ? 'Update' : pagePermissionType === 'all' ? 'Default permission' : 'Create permission',
|
||||
disabled: pagePermissionType == 'all' ? true : false,
|
||||
tooltipMessage: '',
|
||||
}}
|
||||
darkMode={darkMode}
|
||||
className="page-permissions-modal"
|
||||
headerAction={() =>
|
||||
pagePermission && (
|
||||
<span
|
||||
onClick={(e) => {
|
||||
togglePagePermissionModal(false);
|
||||
setShowConfirmDelete(true);
|
||||
}}
|
||||
>
|
||||
<SolidIcon fill="var(--tomato10)" width="20" name="trash" />
|
||||
</span>
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className="page-permission">
|
||||
<div className="info-container">
|
||||
<div className="col-md-1 info-btn">
|
||||
<SolidIcon name="informationcircle" fill="#3E63DD" />
|
||||
</div>
|
||||
<div className="col-md-11">
|
||||
<div className="message">
|
||||
<p style={{ lineHeight: '18px' }}>
|
||||
Only selected users will be allowed to access this page. Read docs to know more.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<label className="form-label">Type</label>
|
||||
<Select
|
||||
options={permissionTypeOptions}
|
||||
value={pagePermissionType}
|
||||
width={'100%'}
|
||||
customOption={renderPermissionTypeOptions}
|
||||
useMenuPortal={false}
|
||||
onChange={handlePermissionTypeChange}
|
||||
/>
|
||||
{showUserGroupSelect && <UserGroupSelect />}
|
||||
{showUsersSelect && <UserSelect />}
|
||||
</div>
|
||||
</ModalBase>
|
||||
{showConfirmDelete && (
|
||||
<ConfirmDialog
|
||||
title={'Delete page permission'}
|
||||
show={showConfirmDelete}
|
||||
message={
|
||||
'Deleting the permission will allow all users with access to the app to view this page. Are you sure you want to continue?'
|
||||
}
|
||||
confirmButtonLoading={isLoading}
|
||||
onConfirm={() => deletePagePermission()}
|
||||
onCancel={() => setShowConfirmDelete(false)}
|
||||
confirmButtonText={'Delete'}
|
||||
darkMode={darkMode}
|
||||
confirmButtonIcon={'trash'}
|
||||
confirmButtonIconWidth="20"
|
||||
confirmButtonIconFill={'var(--slate3)'}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const UserGroupSelect = () => {
|
||||
console.log('rendering');
|
||||
const appId = useStore((state) => state.app.appId);
|
||||
const editingPage = useStore((state) => state.editingPage);
|
||||
const selectedUserGroups = useStore((state) => state.selectedUserGroups);
|
||||
const setSelectedUserGroups = useStore((state) => state.setSelectedUserGroups);
|
||||
const [userGroups, setUserGroups] = useState([]);
|
||||
useEffect(() => {
|
||||
const fetchUserGroups = () => {
|
||||
appPermissionService.getUsers(appId, 'user-groups').then((data) => {
|
||||
console.log({ data });
|
||||
if (data?.length) {
|
||||
const groups = [];
|
||||
data.map((group) => {
|
||||
groups.push({ value: group.id, label: group.name });
|
||||
});
|
||||
setUserGroups(groups);
|
||||
}
|
||||
});
|
||||
};
|
||||
fetchUserGroups();
|
||||
}, []);
|
||||
|
||||
console.log({ selectedUserGroups, userGroups });
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label className="form-label mt-3">User groups</label>
|
||||
<Select
|
||||
isMulti={true}
|
||||
options={userGroups}
|
||||
value={selectedUserGroups}
|
||||
width={'100%'}
|
||||
// customOption={renderPermissionTypeOptions}
|
||||
useMenuPortal={false}
|
||||
// menuIsOpen={true}
|
||||
onChange={(groups) => setSelectedUserGroups(groups)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const UserSelect = () => {
|
||||
const appId = useStore((state) => state.app.appId);
|
||||
const editingPage = useStore((state) => state.editingPage);
|
||||
const selectedUsers = useStore((state) => state.selectedUsers);
|
||||
const setSelectedUsers = useStore((state) => state.setSelectedUsers);
|
||||
const [users, setUsers] = useState([]);
|
||||
useEffect(() => {
|
||||
const fetchUsers = () => {
|
||||
appPermissionService.getUsers(appId, 'users').then((data) => {
|
||||
console.log({ data });
|
||||
if (data?.length) {
|
||||
const users = [];
|
||||
data.map((user) => {
|
||||
const firstName = user.firstName || '';
|
||||
const lastName = user.lastName || '';
|
||||
users.push({
|
||||
value: user.id,
|
||||
label: `${firstName} ${lastName}`.trim(),
|
||||
email: user.email,
|
||||
initials: `${firstName[0] || ''}${lastName[0] || ''}`.toUpperCase(),
|
||||
});
|
||||
});
|
||||
setUsers(users);
|
||||
}
|
||||
});
|
||||
};
|
||||
fetchUsers();
|
||||
}, []);
|
||||
|
||||
const CustomOption = (props) => {
|
||||
const { data, isFocused, isSelected } = props;
|
||||
return (
|
||||
<components.Option {...props}>
|
||||
<div className={`user-select-option ${isFocused ? 'focused' : ''}`}>
|
||||
<div className="avatar">{data.initials}</div>
|
||||
<div className="user-info">
|
||||
<div className="name">{data.label}</div>
|
||||
<div className="email">{data.email}</div>
|
||||
</div>
|
||||
</div>
|
||||
</components.Option>
|
||||
);
|
||||
};
|
||||
|
||||
console.log({ users });
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label className="form-label mt-3">Users</label>
|
||||
<Select
|
||||
isMulti={true}
|
||||
options={users}
|
||||
value={selectedUsers}
|
||||
width={'100%'}
|
||||
// customOption={renderUserSelectOptions}
|
||||
useMenuPortal={false}
|
||||
components={{ Option: CustomOption }}
|
||||
// menuIsOpen={true}
|
||||
onChange={(users) => {
|
||||
console.log({ userstemp: users });
|
||||
setSelectedUsers(users);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -266,4 +266,76 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.page-permission {
|
||||
.info-container {
|
||||
display: flex;
|
||||
width: auto;
|
||||
height: auto;
|
||||
padding: 10px 12px 8px 12px;
|
||||
border: 1px solid var(--slate5);
|
||||
background: var(--slate2);
|
||||
border-radius: 6px 6px 6px 6px;
|
||||
margin-bottom: 13px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.permission-type-select {
|
||||
align-items: center;
|
||||
|
||||
.col-auto {
|
||||
padding-right: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.page-permissions-modal {
|
||||
#header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.user-select-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
|
||||
&.focused {
|
||||
background-color: #f3f4f6; // Tailwind's gray-100 vibe
|
||||
}
|
||||
|
||||
.avatar {
|
||||
background-color: var(--slate5); // light gray
|
||||
color: var(--slate12); // dark text
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.name {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
color: var(--slate12);
|
||||
}
|
||||
|
||||
.email {
|
||||
font-size: 12px;
|
||||
color: var(--slate10); // gray-500
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -142,6 +142,7 @@ const RenderPageGroup = ({
|
|||
export const RenderPageAndPageGroup = ({ pages, labelStyle, computeStyles, darkMode, switchPageWrapper }) => {
|
||||
// Don't render empty folders if displaying only icons
|
||||
const tree = buildTree(pages, !!labelStyle?.label?.hidden);
|
||||
const filteredPages = tree.filter((page) => !page?.isPageGroup || page.children?.length > 0);
|
||||
|
||||
const currentPageId = useStore((state) => state.currentPageId);
|
||||
const currentPage = pages.find((page) => page.id === currentPageId);
|
||||
|
|
@ -149,14 +150,14 @@ export const RenderPageAndPageGroup = ({ pages, labelStyle, computeStyles, darkM
|
|||
return (
|
||||
<div className={cx('page-handler-wrapper viewer', { 'dark-theme': darkMode })}>
|
||||
{/* <Accordion alwaysOpen defaultActiveKey={tree.map((page) => page.id)}> */}
|
||||
{tree.map((page, index) => {
|
||||
{filteredPages.map((page, index) => {
|
||||
if (page.isPageGroup && page.children.length === 0 && labelStyle?.label?.hidden) {
|
||||
return null;
|
||||
}
|
||||
if (page.children && page.isPageGroup) {
|
||||
// if we are only displaying icons, we don't display the groups instead display separator to separate a page groups
|
||||
const renderSeparatorTop = index !== 0 && labelStyle?.label?.hidden;
|
||||
const renderSeparatorBottom = !tree[index + 1]?.isPageGroup && labelStyle?.label?.hidden;
|
||||
const renderSeparatorBottom = !filteredPages[index + 1]?.isPageGroup && labelStyle?.label?.hidden;
|
||||
return (
|
||||
<>
|
||||
{renderSeparatorTop && (
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ import { baseTheme, convertAllKeysToSnakeCase } from '../_stores/utils';
|
|||
import { getPreviewQueryParams } from '@/_helpers/routes';
|
||||
import { useLocation, useMatch, useParams } from 'react-router-dom';
|
||||
import useThemeAccess from './useThemeAccess';
|
||||
import { handleError } from '@/_helpers/handleAppAccess';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
/**
|
||||
* this is to normalize the query transformation options to match the expected schema. Takes care of corrupted data.
|
||||
|
|
@ -214,224 +216,248 @@ const useAppData = (appId, moduleId, darkMode, mode = 'edit', { environmentId, v
|
|||
}
|
||||
|
||||
// const appDataPromise = appService.fetchApp(appId);
|
||||
appDataPromise.then(async (result) => {
|
||||
let appData = { ...result };
|
||||
let editorEnvironment = result.editorEnvironment;
|
||||
if (isPreviewForVersion) {
|
||||
const rawDataQueries = appData?.data_queries;
|
||||
const rawEditingVersionDataQueries = appData?.editing_version?.data_queries;
|
||||
appData = convertAllKeysToSnakeCase(appData);
|
||||
appDataPromise
|
||||
.then(async (result) => {
|
||||
let appData = { ...result };
|
||||
let editorEnvironment = result.editorEnvironment;
|
||||
if (isPreviewForVersion) {
|
||||
const rawDataQueries = appData?.data_queries;
|
||||
const rawEditingVersionDataQueries = appData?.editing_version?.data_queries;
|
||||
appData = convertAllKeysToSnakeCase(appData);
|
||||
|
||||
appData.data_queries = rawDataQueries;
|
||||
if (appData.editing_version && rawEditingVersionDataQueries) {
|
||||
appData.editing_version.data_queries = rawEditingVersionDataQueries;
|
||||
}
|
||||
appData.data_queries = rawDataQueries;
|
||||
if (appData.editing_version && rawEditingVersionDataQueries) {
|
||||
appData.editing_version.data_queries = rawEditingVersionDataQueries;
|
||||
}
|
||||
|
||||
editorEnvironment = {
|
||||
id: environmentId,
|
||||
name: queryParams.env,
|
||||
};
|
||||
}
|
||||
|
||||
let constantsResp;
|
||||
if (mode !== 'edit') {
|
||||
try {
|
||||
const queryParams = { slug: slug };
|
||||
const viewerEnvironment = await appEnvironmentService.getEnvironment(environmentId, queryParams);
|
||||
editorEnvironment = {
|
||||
id: viewerEnvironment?.environment?.id,
|
||||
name: viewerEnvironment?.environment?.name,
|
||||
id: environmentId,
|
||||
name: queryParams.env,
|
||||
};
|
||||
constantsResp =
|
||||
isPublicAccess && appData.is_public
|
||||
? await orgEnvironmentConstantService.getConstantsFromPublicApp(slug, viewerEnvironment?.environment?.id)
|
||||
: await orgEnvironmentConstantService.getConstantsFromApp(slug, viewerEnvironment?.environment?.id);
|
||||
} catch (error) {
|
||||
console.error('Error fetching viewer environment:', error);
|
||||
}
|
||||
}
|
||||
|
||||
if (mode === 'edit') {
|
||||
constantsResp = await orgEnvironmentConstantService.getConstantsFromEnvironment(editorEnvironment?.id);
|
||||
}
|
||||
// get the constants for specific environment
|
||||
constantsResp.constants = extractEnvironmentConstantsFromConstantsList(
|
||||
constantsResp?.constants,
|
||||
editorEnvironment?.name
|
||||
);
|
||||
|
||||
setIsPublicAccess(isPublicAccess && mode !== 'edit' && appData.is_public);
|
||||
|
||||
fetchAndInjectCustomStyles(isPublicAccess && mode !== 'edit' && appData.is_public);
|
||||
|
||||
const pages = appData.pages.map((page) => {
|
||||
return page;
|
||||
});
|
||||
const conversation = appData.ai_conversation;
|
||||
const docsConversation = appData.ai_conversation_learn;
|
||||
if (setConversation && setDocsConversation) {
|
||||
setConversation(conversation);
|
||||
setDocsConversation(docsConversation);
|
||||
// important to control ai inputs
|
||||
getCreditBalance();
|
||||
}
|
||||
|
||||
let showWalkthrough = true;
|
||||
// if app was created from propmt, and no earlier messages are present in the conversation, send the prompt message
|
||||
|
||||
// handles the getappdataby slug api call. Gets the homePageId from the appData.
|
||||
const homePageId =
|
||||
appData.editing_version?.homePageId || appData.editing_version?.home_page_id || appData.home_page_id;
|
||||
|
||||
setApp({
|
||||
appName: appData.name,
|
||||
appId: appData.id,
|
||||
slug: appData.slug,
|
||||
currentAppEnvironmentId: editorEnvironment.id,
|
||||
isMaintenanceOn:
|
||||
'is_maintenance_on' in result
|
||||
? result.is_maintenance_on
|
||||
: 'isMaintenanceOn' in result
|
||||
? result.isMaintenanceOn
|
||||
: false,
|
||||
organizationId: appData.organizationId || appData.organization_id,
|
||||
homePageId: homePageId,
|
||||
isPublic: appData.is_public,
|
||||
creationMode: appData.creation_mode,
|
||||
});
|
||||
setIsEditorFreezed(appData.should_freeze_editor);
|
||||
const global_settings = mapKeys(
|
||||
appData.editing_version?.global_settings || appData.global_settings,
|
||||
(value, key) => camelCase(key)
|
||||
);
|
||||
if (!global_settings?.theme) {
|
||||
global_settings.theme = baseTheme;
|
||||
}
|
||||
setGlobalSettings(global_settings);
|
||||
setPages(pages, moduleId);
|
||||
setPageSettings(
|
||||
computePageSettings(deepCamelCase(appData?.editing_version?.page_settings ?? appData?.page_settings))
|
||||
);
|
||||
|
||||
// set starting page as homepage initially
|
||||
let startingPage = appData.pages.find((page) => page.id === homePageId);
|
||||
|
||||
if (initialLoadRef.current) {
|
||||
// if initial load, check if the path has a page handle and set that as the starting page
|
||||
const initialLoadPath = location.pathname.split('/').pop();
|
||||
const page = appData.pages.find((page) => page.handle === initialLoadPath && !page.isPageGroup);
|
||||
if (page) {
|
||||
// if page is disabled, and not editing redirect to home page
|
||||
if (mode !== 'edit' && page?.disabled) {
|
||||
const currentUrl = window.location.href;
|
||||
const replacedUrl = currentUrl.replace(initialLoadPath, startingPage.handle);
|
||||
window.history.replaceState(null, null, replacedUrl);
|
||||
} else {
|
||||
startingPage = page;
|
||||
let constantsResp;
|
||||
if (mode !== 'edit') {
|
||||
try {
|
||||
const queryParams = { slug: slug };
|
||||
const viewerEnvironment = await appEnvironmentService.getEnvironment(environmentId, queryParams);
|
||||
editorEnvironment = {
|
||||
id: viewerEnvironment?.environment?.id,
|
||||
name: viewerEnvironment?.environment?.name,
|
||||
};
|
||||
constantsResp =
|
||||
isPublicAccess && appData.is_public
|
||||
? await orgEnvironmentConstantService.getConstantsFromPublicApp(
|
||||
slug,
|
||||
viewerEnvironment?.environment?.id
|
||||
)
|
||||
: await orgEnvironmentConstantService.getConstantsFromApp(slug, viewerEnvironment?.environment?.id);
|
||||
} catch (error) {
|
||||
console.error('Error fetching viewer environment:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// navigate(`/${getWorkspaceId()}/apps/${slug ?? appId}/${startingPage.handle}`);
|
||||
}
|
||||
|
||||
// Add page id and handle to the state on initial load
|
||||
const currentState = window.history.state || {};
|
||||
const pageInfo = {
|
||||
id: startingPage.id,
|
||||
handle: startingPage.handle,
|
||||
};
|
||||
const newState = { ...currentState, ...pageInfo };
|
||||
window.history.replaceState(newState, '', window.location.href);
|
||||
|
||||
setCurrentPageHandle(startingPage.handle);
|
||||
updateFeatureAccess();
|
||||
setCurrentPageId(startingPage.id, moduleId);
|
||||
setResolvedPageConstants({
|
||||
id: startingPage?.id,
|
||||
handle: startingPage?.handle,
|
||||
name: startingPage?.name,
|
||||
});
|
||||
setComponentNameIdMapping(moduleId);
|
||||
updateEventsField('events', appData.events);
|
||||
setCurrentVersionId(appData.editing_version?.id || appData.current_version_id);
|
||||
setAppHomePageId(homePageId);
|
||||
|
||||
const queryData =
|
||||
isPublicAccess || (mode !== 'edit' && appData.is_public)
|
||||
? appData
|
||||
: await dataqueryService.getAll(appData.editing_version?.id || appData.current_version_id);
|
||||
const dataQueries = queryData.data_queries || queryData?.editing_version?.data_queries;
|
||||
dataQueries.forEach((query) => normalizeQueryTransformationOptions(query));
|
||||
setQueries(dataQueries);
|
||||
if (dataQueries?.length > 0) {
|
||||
setSelectedQuery(dataQueries[0]?.id);
|
||||
initialiseResolvedQuery(dataQueries.map((query) => query.id));
|
||||
}
|
||||
const constants = constantsResp?.constants;
|
||||
|
||||
if (constants) {
|
||||
const orgConstants = {};
|
||||
const orgSecrets = {};
|
||||
constants.map((constant) => {
|
||||
if (constant.type !== 'Secret') {
|
||||
orgConstants[constant.name] = constant.value;
|
||||
} else {
|
||||
orgSecrets[constant.name] = constant.value;
|
||||
}
|
||||
});
|
||||
setResolvedConstants(orgConstants);
|
||||
setSecrets(orgSecrets);
|
||||
}
|
||||
setQueryMapping(moduleId);
|
||||
|
||||
setResolvedGlobals('environment', editorEnvironment);
|
||||
setResolvedGlobals('mode', { value: mode });
|
||||
setResolvedGlobals('currentUser', {
|
||||
...user,
|
||||
groups: currentSession?.groups,
|
||||
role: currentSession?.role?.name,
|
||||
ssoUserInfo: currentSession?.ssoUserInfo,
|
||||
...(currentSession?.currentUser?.metadata && !isEmpty(currentSession?.currentUser?.metadata)
|
||||
? { metadata: currentSession?.currentUser?.metadata }
|
||||
: {}),
|
||||
});
|
||||
setResolvedGlobals('urlparams', JSON.parse(JSON.stringify(queryString.parse(location?.search))));
|
||||
initDependencyGraph(moduleId);
|
||||
setCurrentMode(mode); // TODO: set mode based on the slug/appDef
|
||||
if (
|
||||
state.ai &&
|
||||
state?.prompt &&
|
||||
initialLoadRef.current &&
|
||||
(conversation?.aiConversationMessages || []).length === 0
|
||||
) {
|
||||
setSelectedSidebarItem('tooljetai');
|
||||
toggleLeftSidebar('true');
|
||||
sendMessage(state.prompt);
|
||||
setConversationZeroState(true);
|
||||
showWalkthrough = false;
|
||||
}
|
||||
// fetchDataSources(appData.editing_version.id, editorEnvironment.id);
|
||||
if (!isPublicAccess) {
|
||||
const envFromQueryParams = mode === 'view' && new URLSearchParams(location?.search)?.get('env');
|
||||
useStore.getState().init(appData.editing_version?.id || appData.current_version_id, envFromQueryParams);
|
||||
fetchGlobalDataSources(
|
||||
appData.organization_id,
|
||||
appData.editing_version?.id || appData.current_version_id,
|
||||
editorEnvironment.id
|
||||
if (mode === 'edit') {
|
||||
constantsResp = await orgEnvironmentConstantService.getConstantsFromEnvironment(editorEnvironment?.id);
|
||||
}
|
||||
// get the constants for specific environment
|
||||
constantsResp.constants = extractEnvironmentConstantsFromConstantsList(
|
||||
constantsResp?.constants,
|
||||
editorEnvironment?.name
|
||||
);
|
||||
}
|
||||
useStore.getState().updateEditingVersion(appData.editing_version?.id || appData.current_version_id); //check if this is needed
|
||||
updateReleasedVersionId(appData.current_version_id);
|
||||
|
||||
setEditorLoading(false);
|
||||
initialLoadRef.current = false;
|
||||
// only show if app is not created from prompt
|
||||
if (showWalkthrough) initEditorWalkThrough();
|
||||
checkAndSetTrueBuildSuggestionsFlag();
|
||||
return () => {
|
||||
document.title = retrieveWhiteLabelText();
|
||||
};
|
||||
});
|
||||
setIsPublicAccess(isPublicAccess && mode !== 'edit' && appData.is_public);
|
||||
|
||||
fetchAndInjectCustomStyles(isPublicAccess && mode !== 'edit' && appData.is_public);
|
||||
|
||||
const pages = appData.pages.map((page) => {
|
||||
return page;
|
||||
});
|
||||
const conversation = appData.ai_conversation;
|
||||
const docsConversation = appData.ai_conversation_learn;
|
||||
if (setConversation && setDocsConversation) {
|
||||
setConversation(conversation);
|
||||
setDocsConversation(docsConversation);
|
||||
// important to control ai inputs
|
||||
getCreditBalance();
|
||||
}
|
||||
|
||||
let showWalkthrough = true;
|
||||
// if app was created from propmt, and no earlier messages are present in the conversation, send the prompt message
|
||||
|
||||
// handles the getappdataby slug api call. Gets the homePageId from the appData.
|
||||
const homePageId =
|
||||
appData.editing_version?.homePageId || appData.editing_version?.home_page_id || appData.home_page_id;
|
||||
|
||||
setApp({
|
||||
appName: appData.name,
|
||||
appId: appData.id,
|
||||
slug: appData.slug,
|
||||
currentAppEnvironmentId: editorEnvironment.id,
|
||||
isMaintenanceOn:
|
||||
'is_maintenance_on' in result
|
||||
? result.is_maintenance_on
|
||||
: 'isMaintenanceOn' in result
|
||||
? result.isMaintenanceOn
|
||||
: false,
|
||||
organizationId: appData.organizationId || appData.organization_id,
|
||||
homePageId: homePageId,
|
||||
isPublic: appData.is_public,
|
||||
creationMode: appData.creation_mode,
|
||||
});
|
||||
setIsEditorFreezed(appData.should_freeze_editor);
|
||||
const global_settings = mapKeys(
|
||||
appData.editing_version?.global_settings || appData.global_settings,
|
||||
(value, key) => camelCase(key)
|
||||
);
|
||||
if (!global_settings?.theme) {
|
||||
global_settings.theme = baseTheme;
|
||||
}
|
||||
setGlobalSettings(global_settings);
|
||||
setPages(pages, moduleId);
|
||||
setPageSettings(
|
||||
computePageSettings(deepCamelCase(appData?.editing_version?.page_settings ?? appData?.page_settings))
|
||||
);
|
||||
|
||||
// set starting page as homepage initially
|
||||
let startingPage = appData.pages.find((page) => page.id === homePageId);
|
||||
|
||||
//no access to homepage, set to the next available page
|
||||
if (!homePageId) {
|
||||
startingPage = appData.pages[0];
|
||||
}
|
||||
|
||||
if (initialLoadRef.current) {
|
||||
// if initial load, check if the path has a page handle and set that as the starting page
|
||||
const initialLoadPath = location.pathname.split('/')[3];
|
||||
|
||||
const page = appData.pages.find((page) => page.handle === initialLoadPath && !page.isPageGroup);
|
||||
if (page) {
|
||||
// if page is disabled, and not editing redirect to home page
|
||||
if (mode !== 'edit' && page?.disabled) {
|
||||
const currentUrl = window.location.href;
|
||||
const replacedUrl = currentUrl.replace(initialLoadPath, startingPage.handle);
|
||||
window.history.replaceState(null, null, replacedUrl);
|
||||
} else {
|
||||
startingPage = page;
|
||||
}
|
||||
} else {
|
||||
if (mode !== 'edit' && initialLoadPath) {
|
||||
const currentUrl = window.location.href;
|
||||
const replacedUrl = currentUrl.replace(initialLoadPath, startingPage.handle);
|
||||
window.history.replaceState(null, null, replacedUrl);
|
||||
toast.error('Access to this page is restricted. Contact admin to know more.');
|
||||
}
|
||||
}
|
||||
|
||||
// navigate(`/${getWorkspaceId()}/apps/${slug ?? appId}/${startingPage.handle}`);
|
||||
}
|
||||
|
||||
// Add page id and handle to the state on initial load
|
||||
const currentState = window.history.state || {};
|
||||
const pageInfo = {
|
||||
id: startingPage.id,
|
||||
handle: startingPage.handle,
|
||||
};
|
||||
const newState = { ...currentState, ...pageInfo };
|
||||
window.history.replaceState(newState, '', window.location.href);
|
||||
|
||||
setCurrentPageHandle(startingPage.handle);
|
||||
updateFeatureAccess();
|
||||
setCurrentPageId(startingPage.id, moduleId);
|
||||
setResolvedPageConstants({
|
||||
id: startingPage?.id,
|
||||
handle: startingPage?.handle,
|
||||
name: startingPage?.name,
|
||||
});
|
||||
setComponentNameIdMapping(moduleId);
|
||||
updateEventsField('events', appData.events);
|
||||
setCurrentVersionId(appData.editing_version?.id || appData.current_version_id);
|
||||
setAppHomePageId(homePageId);
|
||||
|
||||
const queryData =
|
||||
isPublicAccess || (mode !== 'edit' && appData.is_public)
|
||||
? appData
|
||||
: await dataqueryService.getAll(appData.editing_version?.id || appData.current_version_id);
|
||||
const dataQueries = queryData.data_queries || queryData?.editing_version?.data_queries;
|
||||
dataQueries.forEach((query) => normalizeQueryTransformationOptions(query));
|
||||
setQueries(dataQueries);
|
||||
if (dataQueries?.length > 0) {
|
||||
setSelectedQuery(dataQueries[0]?.id);
|
||||
initialiseResolvedQuery(dataQueries.map((query) => query.id));
|
||||
}
|
||||
const constants = constantsResp?.constants;
|
||||
|
||||
if (constants) {
|
||||
const orgConstants = {};
|
||||
const orgSecrets = {};
|
||||
constants.map((constant) => {
|
||||
if (constant.type !== 'Secret') {
|
||||
orgConstants[constant.name] = constant.value;
|
||||
} else {
|
||||
orgSecrets[constant.name] = constant.value;
|
||||
}
|
||||
});
|
||||
setResolvedConstants(orgConstants);
|
||||
setSecrets(orgSecrets);
|
||||
}
|
||||
setQueryMapping(moduleId);
|
||||
|
||||
setResolvedGlobals('environment', editorEnvironment);
|
||||
setResolvedGlobals('mode', { value: mode });
|
||||
setResolvedGlobals('currentUser', {
|
||||
...user,
|
||||
groups: currentSession?.groups,
|
||||
role: currentSession?.role?.name,
|
||||
ssoUserInfo: currentSession?.ssoUserInfo,
|
||||
...(currentSession?.currentUser?.metadata && !isEmpty(currentSession?.currentUser?.metadata)
|
||||
? { metadata: currentSession?.currentUser?.metadata }
|
||||
: {}),
|
||||
});
|
||||
setResolvedGlobals('urlparams', JSON.parse(JSON.stringify(queryString.parse(location?.search))));
|
||||
initDependencyGraph(moduleId);
|
||||
setCurrentMode(mode); // TODO: set mode based on the slug/appDef
|
||||
if (
|
||||
state.ai &&
|
||||
state?.prompt &&
|
||||
initialLoadRef.current &&
|
||||
(conversation?.aiConversationMessages || []).length === 0
|
||||
) {
|
||||
setSelectedSidebarItem('tooljetai');
|
||||
toggleLeftSidebar('true');
|
||||
sendMessage(state.prompt);
|
||||
setConversationZeroState(true);
|
||||
showWalkthrough = false;
|
||||
}
|
||||
// fetchDataSources(appData.editing_version.id, editorEnvironment.id);
|
||||
if (!isPublicAccess) {
|
||||
const envFromQueryParams = mode === 'view' && new URLSearchParams(location?.search)?.get('env');
|
||||
useStore.getState().init(appData.editing_version?.id || appData.current_version_id, envFromQueryParams);
|
||||
fetchGlobalDataSources(
|
||||
appData.organization_id,
|
||||
appData.editing_version?.id || appData.current_version_id,
|
||||
editorEnvironment.id
|
||||
);
|
||||
}
|
||||
useStore.getState().updateEditingVersion(appData.editing_version?.id || appData.current_version_id); //check if this is needed
|
||||
updateReleasedVersionId(appData.current_version_id);
|
||||
|
||||
setEditorLoading(false);
|
||||
initialLoadRef.current = false;
|
||||
// only show if app is not created from prompt
|
||||
if (showWalkthrough) initEditorWalkThrough();
|
||||
checkAndSetTrueBuildSuggestionsFlag();
|
||||
return () => {
|
||||
document.title = retrieveWhiteLabelText();
|
||||
};
|
||||
})
|
||||
.catch((error) => {
|
||||
if (isPublicAccess) {
|
||||
if (mode !== 'edit') {
|
||||
handleError('view', error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, [setApp, setEditorLoading, currentSession]);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -1118,6 +1118,10 @@ export const createEventsSlice = (set, get) => ({
|
|||
toast('Valid page handle is required', {
|
||||
icon: '⚠️',
|
||||
});
|
||||
mode === 'view' &&
|
||||
toast.error('Access to this page is restricted. Contact admin to know more.', {
|
||||
icon: '⚠️',
|
||||
});
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -96,6 +96,11 @@ export const createPageMenuSlice = (set, get) => {
|
|||
isPageGroup: false,
|
||||
pageSettingSelected: false,
|
||||
pageSettings: {},
|
||||
showPagePermissionModal: false,
|
||||
permissionPage: null,
|
||||
selectedUserGroups: [],
|
||||
selectedUsers: [],
|
||||
pagePermission: null,
|
||||
|
||||
toggleSearch: (show) =>
|
||||
set((state) => {
|
||||
|
|
@ -117,7 +122,6 @@ export const createPageMenuSlice = (set, get) => {
|
|||
|
||||
closePageEditPopover: () =>
|
||||
set((state) => {
|
||||
state.editingPage = null;
|
||||
state.showEditingPopover = false;
|
||||
state.showEditPageEventsModal = false;
|
||||
state.showRenamePageHandleModal = false;
|
||||
|
|
@ -419,5 +423,26 @@ export const createPageMenuSlice = (set, get) => {
|
|||
console.error('Error updating page:', error);
|
||||
}
|
||||
},
|
||||
|
||||
setPagePermission: (pagePermission) =>
|
||||
set((state) => {
|
||||
state.pagePermission = pagePermission;
|
||||
}),
|
||||
|
||||
togglePagePermissionModal: (show) => {
|
||||
set((state) => {
|
||||
state.showPagePermissionModal = show;
|
||||
});
|
||||
},
|
||||
|
||||
setSelectedUserGroups: (groups) =>
|
||||
set((state) => {
|
||||
state.selectedUserGroups = groups;
|
||||
}),
|
||||
|
||||
setSelectedUsers: (users) =>
|
||||
set((state) => {
|
||||
state.selectedUsers = users;
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ export const ON_BOARDING_ROLES = [
|
|||
export const ERROR_TYPES = {
|
||||
URL_UNAVAILABLE: 'url-unavailable',
|
||||
RESTRICTED: 'restricted',
|
||||
NO_ACCESSIBLE_PAGES: 'no-accessible-pages',
|
||||
INVALID: 'invalid-link',
|
||||
UNKNOWN: 'unknown',
|
||||
WORKSPACE_ARCHIVED: 'Organization is Archived',
|
||||
|
|
@ -53,6 +54,12 @@ export const ERROR_MESSAGES = {
|
|||
retry: false,
|
||||
queryParams: [],
|
||||
},
|
||||
'no-accessible-pages': {
|
||||
title: 'Restricted access',
|
||||
message: 'You don’t have access to any page in this app. Kindly contact admin to know more.',
|
||||
retry: false,
|
||||
queryParams: [],
|
||||
},
|
||||
'ws-login-restricted': {
|
||||
title: 'Restricted access',
|
||||
message:
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ const switchOrganization = (componentType, orgId, redirectPath) => {
|
|||
);
|
||||
};
|
||||
|
||||
const handleError = (componentType, error, redirectPath, editPermission, appSlug = null) => {
|
||||
export const handleError = (componentType, error, redirectPath, editPermission, appSlug = null) => {
|
||||
try {
|
||||
if (error?.data) {
|
||||
const statusCode = error.data?.statusCode;
|
||||
|
|
@ -63,6 +63,10 @@ const handleError = (componentType, error, redirectPath, editPermission, appSlug
|
|||
switchOrganization(componentType, errorObj?.organizationId, redirectPath);
|
||||
return;
|
||||
}
|
||||
if (errorObj?.type === ERROR_TYPES.NO_ACCESSIBLE_PAGES) {
|
||||
redirectToErrorPage(ERROR_TYPES.NO_ACCESSIBLE_PAGES);
|
||||
return;
|
||||
}
|
||||
redirectToErrorPage(ERROR_TYPES.RESTRICTED);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
49
frontend/src/_services/appPermission.service.js
Normal file
49
frontend/src/_services/appPermission.service.js
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import config from 'config';
|
||||
import { authHeader, handleResponse } from '@/_helpers';
|
||||
|
||||
export const appPermissionService = {
|
||||
getPagePermission,
|
||||
getUsers,
|
||||
createPagePermission,
|
||||
updatePagePermission,
|
||||
deletePagePermission,
|
||||
};
|
||||
|
||||
function getPagePermission(appId, pageId) {
|
||||
const requestOptions = { method: 'GET', headers: authHeader(), credentials: 'include' };
|
||||
return fetch(`${config.apiUrl}/app-permissions/${appId}/pages/${pageId}`, requestOptions).then(handleResponse);
|
||||
}
|
||||
|
||||
function getUsers(appId, type) {
|
||||
const requestOptions = { method: 'GET', headers: authHeader(), credentials: 'include' };
|
||||
return fetch(`${config.apiUrl}/app-permissions/${appId}/pages/${type}`, requestOptions).then(handleResponse);
|
||||
}
|
||||
|
||||
function createPagePermission(appId, pageId, body) {
|
||||
const requestOptions = {
|
||||
method: 'POST',
|
||||
headers: authHeader(),
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
return fetch(`${config.apiUrl}/app-permissions/${appId}/pages/${pageId}`, requestOptions).then(handleResponse);
|
||||
}
|
||||
|
||||
function updatePagePermission(appId, pageId, body) {
|
||||
const requestOptions = {
|
||||
method: 'PUT',
|
||||
headers: authHeader(),
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
return fetch(`${config.apiUrl}/app-permissions/${appId}/pages/${pageId}`, requestOptions).then(handleResponse);
|
||||
}
|
||||
|
||||
function deletePagePermission(appId, pageId) {
|
||||
const requestOptions = {
|
||||
method: 'DELETE',
|
||||
headers: authHeader(),
|
||||
credentials: 'include',
|
||||
};
|
||||
return fetch(`${config.apiUrl}/app-permissions/${appId}/pages/${pageId}`, requestOptions).then(handleResponse);
|
||||
}
|
||||
|
|
@ -33,3 +33,4 @@ export * from './workflow_schedules.service';
|
|||
export * from './session.service';
|
||||
export * from './login_configs.service';
|
||||
export * from './ai.service';
|
||||
export * from './appPermission.service';
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ export default function ModalBase({
|
|||
cancelDisabled,
|
||||
className = '',
|
||||
size = 'sm',
|
||||
headerAction,
|
||||
}) {
|
||||
return (
|
||||
<Modal
|
||||
|
|
@ -30,7 +31,8 @@ export default function ModalBase({
|
|||
<Modal.Title className="font-weight-500" data-cy="modal-title">
|
||||
{title}
|
||||
</Modal.Title>
|
||||
<div onClick={handleClose} className="cursor-pointer" data-cy="modal-close-button">
|
||||
<div onClick={handleClose} id="header-actions" className="cursor-pointer" data-cy="modal-close-button">
|
||||
{headerAction && headerAction()}
|
||||
<SolidIcon name="remove" width="20" />
|
||||
</div>
|
||||
</Modal.Header>
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 683647f83d3efeeadbe69c40b8e8dd5ba4e8ea06
|
||||
Subproject commit 6b633ee14e024674a47dffbd731243cd2f002026
|
||||
|
|
@ -29,7 +29,7 @@ export class AppPermissionsModule {
|
|||
PagePermissionsRepository,
|
||||
FeatureAbilityFactory,
|
||||
],
|
||||
exports: [AppPermissionsUtilService],
|
||||
exports: [AppPermissionsUtilService, AppPermissionsService],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export class PagePermissionsRepository extends Repository<PagePermission> {
|
|||
return dbTransactionWrap(async (manager: EntityManager) => {
|
||||
return manager.find(PagePermission, {
|
||||
where: { pageId },
|
||||
relations: ['users', 'users.user'],
|
||||
relations: ['users', 'users.user', 'users.permissionGroup'],
|
||||
});
|
||||
}, manager || this.manager);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { PageUser } from '@entities/page_users.entity';
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { DataSource, EntityManager, Repository } from 'typeorm';
|
||||
import { dbTransactionWrap } from '@helpers/database.helper';
|
||||
import { PagePermission } from '@entities/page_permissions.entity';
|
||||
|
||||
@Injectable()
|
||||
export class PageUsersRepository extends Repository<PageUser> {
|
||||
|
|
@ -42,4 +43,49 @@ export class PageUsersRepository extends Repository<PageUser> {
|
|||
return manager.save(pageUsers);
|
||||
}, manager || this.manager);
|
||||
}
|
||||
|
||||
async checkIfUserExistsInPermissionGroup(
|
||||
pagePermission: PagePermission,
|
||||
userId: string,
|
||||
manager?: EntityManager
|
||||
): Promise<PageUser> {
|
||||
return dbTransactionWrap(async (manager: EntityManager) => {
|
||||
const result = await manager
|
||||
.createQueryBuilder(PageUser, 'page_users')
|
||||
.innerJoin('page_users.permissionGroup', 'group')
|
||||
.innerJoin('group.groupUsers', 'groupUser')
|
||||
.where('page_users.pagePermission = :permissionId', {
|
||||
permissionId: pagePermission.id,
|
||||
})
|
||||
.andWhere('groupUser.userId = :userId', { userId })
|
||||
.getOne();
|
||||
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return pagePermission;
|
||||
}, manager || this.manager);
|
||||
}
|
||||
|
||||
async checkIfUserExistsInSingleConfig(
|
||||
pagePermission: PagePermission,
|
||||
userId: string,
|
||||
manager?: EntityManager
|
||||
): Promise<PageUser> {
|
||||
return dbTransactionWrap(async (manager: EntityManager) => {
|
||||
const pageUser = await manager.findOne(PageUser, {
|
||||
where: {
|
||||
pagePermission: { id: pagePermission.id },
|
||||
userId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!pageUser) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return pagePermission;
|
||||
}, manager || this.manager);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import { FeatureAbilityFactory } from './ability';
|
|||
import { DataSourcesModule } from '@modules/data-sources/module';
|
||||
import { AppsSubscriber } from './subscribers/apps.subscriber';
|
||||
import { AiModule } from '@modules/ai/module';
|
||||
import { AppPermissionsModule } from '@modules/app-permissions/module';
|
||||
@Module({})
|
||||
export class AppsModule {
|
||||
static async register(configs: { IS_GET_CONTEXT: boolean }): Promise<DynamicModule> {
|
||||
|
|
@ -44,6 +45,7 @@ export class AppsModule {
|
|||
await AppEnvironmentsModule.register(configs),
|
||||
await DataSourcesModule.register(configs),
|
||||
await AiModule.register(configs),
|
||||
await AppPermissionsModule.register(configs),
|
||||
],
|
||||
controllers: [AppsController],
|
||||
providers: [
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import { WorkflowSchedule } from '@entities/workflow_schedule.entity';
|
|||
import { App } from '@entities/app.entity';
|
||||
import { AiModule } from '@modules/ai/module';
|
||||
import { DataSourcesRepository } from '@modules/data-sources/repository';
|
||||
import { AppPermissionsModule } from '@modules/app-permissions/module';
|
||||
export class WorkflowsModule {
|
||||
static async register(configs?: { IS_GET_CONTEXT: boolean }): Promise<DynamicModule> {
|
||||
const importPath = await getImportPath(configs?.IS_GET_CONTEXT);
|
||||
|
|
@ -91,6 +92,7 @@ export class WorkflowsModule {
|
|||
await FolderAppsModule.register(configs),
|
||||
await ThemesModule.register(configs),
|
||||
await AiModule.register(configs),
|
||||
await AppPermissionsModule.register(configs),
|
||||
],
|
||||
providers: [
|
||||
AppsAbilityFactory,
|
||||
|
|
|
|||
Loading…
Reference in a new issue