diff --git a/frontend/ee b/frontend/ee
index 777446d71e..5c2787b498 160000
--- a/frontend/ee
+++ b/frontend/ee
@@ -1 +1 @@
-Subproject commit 777446d71e78e5941d34353606a12d982820438f
+Subproject commit 5c2787b498202f4356b4598fb961ddbab04acbbf
diff --git a/frontend/src/AppBuilder/LeftSidebar/PageMenu/PageMenu.jsx b/frontend/src/AppBuilder/LeftSidebar/PageMenu/PageMenu.jsx
index d4b8573c7d..d55864e91c 100644
--- a/frontend/src/AppBuilder/LeftSidebar/PageMenu/PageMenu.jsx
+++ b/frontend/src/AppBuilder/LeftSidebar/PageMenu/PageMenu.jsx
@@ -12,11 +12,12 @@ import './style.scss';
import { SortableTree } from './Tree/SortableTree';
import { PageGroupMenu } from './AddPageButton';
import { PageHandlerMenu } from './PageHandlerMenu.jsx';
+import AppPermissionsModal from '@/modules/Appbuilder/components/AppPermissionsModal';
import { EditModal } from './EditModal';
import { SettingsModal } from './SettingsModal';
import { DeletePageConfirmationModal } from './DeletePageConfirmationModal';
import SolidIcon from '@/_ui/Icon/SolidIcons';
-import PagePermission from './PagePermission';
+import { appPermissionService } from '@/_services';
export const PageMenu = ({ darkMode, switchPage, pinned, setPinned }) => {
const showAddNewPageInput = useStore((state) => state.showAddNewPageInput);
@@ -27,6 +28,11 @@ export const PageMenu = ({ darkMode, switchPage, pinned, setPinned }) => {
const shouldFreeze = useStore((state) => state.getShouldFreeze());
const enableReleasedVersionPopupState = useStore((state) => state.enableReleasedVersionPopupState);
const closePageEditPopover = useStore((state) => state.closePageEditPopover);
+ const editingPageId = useStore((state) => state.editingPage?.id);
+ const showPagePermissionModal = useStore((state) => state.showPagePermissionModal);
+ const togglePagePermissionModal = useStore((state) => state.togglePagePermissionModal);
+ const updatePageWithPermissions = useStore((state) => state.updatePageWithPermissions);
+
useEffect(() => {
return () => {
closePageEditPopover();
@@ -95,7 +101,20 @@ export const PageMenu = ({ darkMode, switchPage, pinned, setPinned }) => {
>
- {isLicensed ?
: <>>}
+ {isLicensed && (
+
appPermissionService.getPagePermission(appId, id)}
+ createPermission={(id, appId, body) => appPermissionService.createPagePermission(appId, id, body)}
+ updatePermission={(id, appId, body) => appPermissionService.updatePagePermission(appId, id, body)}
+ deletePermission={(id, appId) => appPermissionService.deletePagePermission(appId, id)}
+ onSuccess={(data) => updatePageWithPermissions(editingPageId, data)}
+ />
+ )}
diff --git a/frontend/src/AppBuilder/LeftSidebar/PageMenu/PagePermission.jsx b/frontend/src/AppBuilder/LeftSidebar/PageMenu/PagePermission.jsx
deleted file mode 100644
index bd7a865a54..0000000000
--- a/frontend/src/AppBuilder/LeftSidebar/PageMenu/PagePermission.jsx
+++ /dev/null
@@ -1,505 +0,0 @@
-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';
-import Spinner from '@/_ui/Spinner';
-
-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 updatePageWithPermissions = useStore((state) => state.updatePageWithPermissions);
-
- 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);
- const [isPermissionsLoading, setPermissionsLoading] = useState(true);
- const [initialSelectedGroups, setInitialSelectedGroups] = useState([]);
- const [initialSelectedUsers, setInitialSelectedUsers] = useState([]);
- const [initalPagePermissionType, setInitialPagePermissionType] = useState('all');
-
- useEffect(() => {
- if (!showPagePermissionModal) return;
- const fetchPagePermission = () => {
- appPermissionService.getPagePermission(appId, editingPage?.id).then((data) => {
- if (data) {
- if (data[0] && data[0]?.type === PERMISSION_TYPES.group) {
- const groups =
- data[0]?.groups?.map((user) => ({
- label: user?.permissionGroup?.name,
- value: user?.permissionGroup?.id,
- count: user?.permissionGroup?.count,
- })) ?? [];
- setPagePermissionType(data[0]?.type?.toLowerCase());
- setInitialPagePermissionType(data[0]?.type?.toLowerCase());
- setPagePermission(data);
- toggleUserGroupSelect(true);
- setInitialSelectedGroups(groups);
- data?.length && setSelectedUserGroups(groups);
- } else if (data[0] && data[0]?.type === PERMISSION_TYPES.single) {
- const users =
- data[0]?.users?.map(({ user }) => {
- const firstName = user.firstName || '';
- const lastName = user.lastName || '';
- return {
- value: user.id,
- label: `${firstName} ${lastName}`.trim(),
- email: user.email,
- initials: `${firstName[0] || ''}${lastName[0] || ''}`.toUpperCase(),
- };
- }) ?? [];
- setPagePermissionType(data[0]?.type?.toLowerCase());
- setInitialPagePermissionType(data[0]?.type?.toLowerCase());
- setPagePermission(data);
- toggleUsersSelect(true);
- setInitialSelectedUsers(users);
- data?.length && setSelectedUsers(users);
- }
- }
- setPermissionsLoading(false);
- });
- };
- fetchPagePermission();
- }, [showPagePermissionModal]);
-
- const isSelectionUnchanged = useMemo(() => {
- if (pagePermissionType === 'group') {
- if (!selectedUserGroups.length) return true;
- const current = selectedUserGroups
- .map((g) => g.value)
- .sort()
- .join(',');
- const initial = initialSelectedGroups
- .map((g) => g.value)
- .sort()
- .join(',');
- return current === initial;
- } else if (pagePermissionType === 'single') {
- if (!selectedUsers.length) return true;
- const current = selectedUsers
- .map((u) => u.value)
- .sort()
- .join(',');
- const initial = initialSelectedUsers
- .map((u) => u.value)
- .sort()
- .join(',');
- return current === initial;
- } else {
- if (!pagePermission?.length) {
- return true;
- } else {
- return initalPagePermissionType == pagePermissionType;
- }
- }
- }, [
- pagePermissionType,
- selectedUserGroups,
- initialSelectedGroups,
- selectedUsers,
- initialSelectedUsers,
- initalPagePermissionType,
- ]);
-
- 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',
- },
- ],
- []
- );
- const handlePermissionTypeChange = (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);
- setSelectedUsers([]);
- setSelectedUserGroups([]);
- setInitialSelectedGroups([]);
- setInitialSelectedUsers([]);
- };
-
- const createPagePermission = () => {
- const body = {
- id: 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) => {
- toast.success('Permission successfully created!', {
- className: 'text-nowrap w-auto mw-100',
- });
- updatePageWithPermissions(editingPage?.id, data);
- })
- .catch(() => {
- toast.error('Permission could not be created. Please try again!', {
- className: 'text-nowrap w-auto mw-100',
- });
- })
- .finally(() => {
- setIsLoading(false);
- handlePagePermissionModalClose();
- });
- };
-
- const updatePagePermission = () => {
- const body = {
- id: 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) => {
- toast.success('Permission successfully updated!', {
- className: 'text-nowrap w-auto mw-100',
- });
- updatePageWithPermissions(editingPage?.id, data);
- })
- .catch(() => {
- toast.error('Permission could not be updated. Please try again!', {
- className: 'text-nowrap w-auto mw-100',
- });
- })
- .finally(() => {
- setIsLoading(false);
- handlePagePermissionModalClose();
- });
- };
-
- const deletePagePermission = () => {
- setIsLoading(true);
- appPermissionService
- .deletePagePermission(appId, editingPage?.id)
- .then((data) => {
- toast.success('Permission successfully deleted!', {
- className: 'text-nowrap w-auto mw-100',
- });
- updatePageWithPermissions(editingPage?.id, []);
- })
- .catch(() => {
- toast.error('Permission could not be deleted. Please try again!', {
- className: 'text-nowrap w-auto mw-100',
- });
- setShowConfirmDelete(false);
- togglePagePermissionModal(true);
- })
- .finally(() => {
- setIsLoading(false);
- setShowConfirmDelete(false);
- });
- };
-
- const renderPermissionTypeOptions = ({ label, icon }) => {
- return (
-
- );
- };
-
- return (
- <>
-
- Page permission
-
- }
- handleConfirm={!pagePermission ? createPagePermission : updatePagePermission}
- show={showPagePermissionModal}
- isLoading={isLoading}
- handleClose={handlePagePermissionModalClose}
- confirmBtnProps={{
- title: pagePermission
- ? 'Save changes'
- : pagePermissionType === 'all'
- ? 'Default permission'
- : 'Create permission',
- disabled: isPermissionsLoading || isSelectionUnchanged,
- tooltipMessage: '',
- leftIcon: pagePermission && 'save',
- className: 'action-btn-page-permission',
- }}
- darkMode={darkMode}
- className="page-permissions-modal"
- >
-
- {isPermissionsLoading ? (
-
-
-
- ) : (
- <>
-
-
-
-
-
-
-
- Only selected users will be allowed to access this page. Read docs to know more.
-
-
-
-
-
-
- {showUserGroupSelect &&
}
- {showUsersSelect &&
}
- >
- )}
-
-
- {showConfirmDelete && (
- deletePagePermission()}
- onCancel={() => setShowConfirmDelete(false)}
- confirmButtonText={'Delete'}
- darkMode={darkMode}
- confirmButtonIcon={'trash'}
- confirmButtonIconWidth="20"
- confirmButtonIconFill={'var(--slate3)'}
- />
- )}
- >
- );
-}
-
-const UserGroupSelect = () => {
- const appId = useStore((state) => state.app.appId);
- 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) => {
- if (data?.length) {
- const groups = [];
- data.map((group) => {
- groups.push({ value: group.id, label: group.name, count: group.count });
- });
- setUserGroups(groups);
- }
- });
- };
- fetchUserGroups();
- }, []);
-
- const CustomOption = (props) => {
- const { data, isFocused, isSelected } = props;
-
- return (
-
-
-
-
-
{data.label}
-
{data.count} users
-
-
-
- );
- };
-
- return (
-
-
-
- );
-};
-
-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) => {
- 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 (
-
-
-
-
{data.initials}
-
-
{data.label}
-
{data.email}
-
-
-
- );
- };
-
- const selectStyles = {
- option: (base) => ({
- ...base,
- padding: '8px 0px',
- }),
- };
- return (
-
-
-
- );
-};
-
-const CustomMenuList = (props) => {
- const { info } = props.selectProps;
- return (
-
-
- {props.children}
-
- );
-};
diff --git a/frontend/src/AppBuilder/LeftSidebar/PageMenu/style.scss b/frontend/src/AppBuilder/LeftSidebar/PageMenu/style.scss
index a3adc9ec20..001a38a4e6 100644
--- a/frontend/src/AppBuilder/LeftSidebar/PageMenu/style.scss
+++ b/frontend/src/AppBuilder/LeftSidebar/PageMenu/style.scss
@@ -267,115 +267,3 @@
}
}
}
-
-.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;
- }
-
- .react-select__option {
- padding: 8px 0px;
-
- input {
- margin-right: 10px;
- }
- }
-
- .react-select__menu-list {
- overflow-y: unset !important;
- }
-
- .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: 500;
- font-size: 14px;
- color: var(--slate12);
- }
-
- .email {
- font-size: 12px;
- color: var(--slate10);
- }
- }
-
- .group-info {
- display: flex;
- flex-direction: row;
- gap: 8px;
- align-items: center;
-
- .name {
- font-weight: 400;
- font-size: 14px;
- color: var(--slate12);
- }
-
- .count {
- font-size: 12px;
- color: var(--slate9);
- }
- }
- }
-}
-
-.page-permission {
- .spinner-center {
- min-height: 250px;
- }
-}
-
-.modal-base .modal-footer .action-btn-page-permission svg path {
- fill: var(--indigo1) !important;
-}
\ No newline at end of file
diff --git a/frontend/src/AppBuilder/_stores/slices/appSlice.js b/frontend/src/AppBuilder/_stores/slices/appSlice.js
index 4b0ded7023..ac426cb25f 100644
--- a/frontend/src/AppBuilder/_stores/slices/appSlice.js
+++ b/frontend/src/AppBuilder/_stores/slices/appSlice.js
@@ -20,6 +20,10 @@ const initialState = {
isTJDarkMode: localStorage.getItem('darkMode') === 'true',
isViewer: false,
isComponentLayoutReady: false,
+ appPermission: {
+ selectedUsers: [],
+ selectedUserGroups: [],
+ },
};
export const createAppSlice = (set, get) => ({
@@ -206,4 +210,12 @@ export const createAppSlice = (set, get) => ({
);
},
updateIsTJDarkMode: (newMode) => set({ isTJDarkMode: newMode }, false, 'updateIsTJDarkMode'),
+ setSelectedUserGroups: (groups) =>
+ set((state) => {
+ state.appPermission.selectedUserGroups = groups;
+ }),
+ setSelectedUsers: (users) =>
+ set((state) => {
+ state.appPermission.selectedUsers = users;
+ }),
});
diff --git a/frontend/src/AppBuilder/_stores/slices/pageMenuSlice.js b/frontend/src/AppBuilder/_stores/slices/pageMenuSlice.js
index 45a5b86428..1ff607d873 100644
--- a/frontend/src/AppBuilder/_stores/slices/pageMenuSlice.js
+++ b/frontend/src/AppBuilder/_stores/slices/pageMenuSlice.js
@@ -99,10 +99,6 @@ export const createPageMenuSlice = (set, get) => {
pageSettingSelected: false,
pageSettings: {},
showPagePermissionModal: false,
- permissionPage: null,
- selectedUserGroups: [],
- selectedUsers: [],
- pagePermission: null,
toggleSearch: (show) =>
set((state) => {
@@ -427,26 +423,12 @@ export const createPageMenuSlice = (set, get) => {
}
},
- 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;
- }),
setEditingPage: (page) =>
set((state) => {
state.editingPage = page;
diff --git a/frontend/src/HomePage/HomePage.jsx b/frontend/src/HomePage/HomePage.jsx
index 3af41903aa..bf36da2f39 100644
--- a/frontend/src/HomePage/HomePage.jsx
+++ b/frontend/src/HomePage/HomePage.jsx
@@ -360,7 +360,7 @@ class HomePageComponent extends React.Component {
}
};
- importFile = async (importJSON, appName, skipPagePermissionsGroupCheck = false) => {
+ importFile = async (importJSON, appName, skipPermissionsGroupCheck = false) => {
this.setState({ isImportingApp: true });
// For backward compatibility with legacy app import
const organization_id = this.state.currentUser?.organization_id;
@@ -376,7 +376,7 @@ class HomePageComponent extends React.Component {
const requestBody = {
organization_id,
...importJSON,
- skip_page_permissions_group_check: skipPagePermissionsGroupCheck,
+ skip_permissions_group_check: skipPermissionsGroupCheck,
};
let installedPluginsInfo = [];
try {
diff --git a/frontend/src/modules/Appbuilder/components/AppPermissionsModal/AppPermissionsModal.jsx b/frontend/src/modules/Appbuilder/components/AppPermissionsModal/AppPermissionsModal.jsx
new file mode 100644
index 0000000000..75be7509b2
--- /dev/null
+++ b/frontend/src/modules/Appbuilder/components/AppPermissionsModal/AppPermissionsModal.jsx
@@ -0,0 +1,8 @@
+import React from 'react';
+import { withEditionSpecificComponent } from '@/modules/common/helpers/withEditionSpecificComponent';
+
+const AppPermissionsModal = () => {
+ return <>>;
+};
+
+export default withEditionSpecificComponent(AppPermissionsModal, 'Appbuilder');
diff --git a/frontend/src/modules/Appbuilder/components/AppPermissionsModal/index.js b/frontend/src/modules/Appbuilder/components/AppPermissionsModal/index.js
new file mode 100644
index 0000000000..cae7417742
--- /dev/null
+++ b/frontend/src/modules/Appbuilder/components/AppPermissionsModal/index.js
@@ -0,0 +1 @@
+export { default } from './AppPermissionsModal';
diff --git a/frontend/src/modules/Appbuilder/components/index.js b/frontend/src/modules/Appbuilder/components/index.js
index b1778f177f..94c5c9fff0 100644
--- a/frontend/src/modules/Appbuilder/components/index.js
+++ b/frontend/src/modules/Appbuilder/components/index.js
@@ -4,5 +4,14 @@ import LogoNavDropdown from './LogoNavDropdown';
import AppEnvironments from './AppEnvironments';
import ThemeSelect from './ThemeSelect';
import ColorSwatches from './ColorSwatches';
+import AppPermissionsModal from './AppPermissionsModal';
-export { CreateVersionModal, PromoteReleaseButton, LogoNavDropdown, AppEnvironments, ThemeSelect, ColorSwatches };
+export {
+ CreateVersionModal,
+ PromoteReleaseButton,
+ LogoNavDropdown,
+ AppEnvironments,
+ ThemeSelect,
+ ColorSwatches,
+ AppPermissionsModal,
+};