diff --git a/frontend/src/AppBuilder/LeftSidebar/PageMenu/PageHandlerMenu.jsx b/frontend/src/AppBuilder/LeftSidebar/PageMenu/PageHandlerMenu.jsx index 1c5c3124f0..497b697b25 100644 --- a/frontend/src/AppBuilder/LeftSidebar/PageMenu/PageHandlerMenu.jsx +++ b/frontend/src/AppBuilder/LeftSidebar/PageMenu/PageHandlerMenu.jsx @@ -164,7 +164,7 @@ export const PageHandlerMenu = ({ darkMode }) => { >
Page permission
- {!licenseValid && } + {!licenseValid && }
); diff --git a/frontend/src/AppBuilder/LeftSidebar/PageMenu/PageMenuItem.jsx b/frontend/src/AppBuilder/LeftSidebar/PageMenu/PageMenuItem.jsx index a113251b61..6c74e7b6af 100644 --- a/frontend/src/AppBuilder/LeftSidebar/PageMenu/PageMenuItem.jsx +++ b/frontend/src/AppBuilder/LeftSidebar/PageMenu/PageMenuItem.jsx @@ -17,6 +17,7 @@ import IconSelector from './IconSelector'; import { withRouter } from '@/_hoc/withRouter'; import OverflowTooltip from '@/_components/OverflowTooltip'; import { shallow } from 'zustand/shallow'; +import { ToolTip } from '@/_components/ToolTip'; export const PageMenuItem = withRouter( memo(({ darkMode, page, navigate }) => { @@ -151,6 +152,36 @@ export const PageMenuItem = withRouter( [popoverRef.current, page] ); + function getTooltip() { + const permission = page?.permissions?.length ? page?.permissions[0] : null; + if (!permission) return ''; + const users = permission.users || []; + const isSingle = permission.type === 'SINGLE'; + const isGroup = permission.type === 'GROUP'; + + if (users.length === 0) return null; + + if (isSingle) { + if (users.length === 1) { + const email = users[0].user.email; + return `Access restricted to ${email}`; + } else { + return `Access restricted to ${users.length} users`; + } + } + + if (isGroup) { + if (users.length === 1) { + const groupName = users[0].permissionGroup?.name ?? 'Group'; + return `Access restricted to ${groupName} group`; + } else { + return `Access restricted to ${users.length} groups`; + } + } + + return ''; + } + return (
setIsHovered(true)} @@ -200,7 +231,13 @@ export const PageMenuItem = withRouter(
- {licenseValid && restricted && } + {licenseValid && restricted && ( + +
+ +
+
+ )}
{!shouldFreeze && ( diff --git a/frontend/src/AppBuilder/LeftSidebar/PageMenu/PagePermission.jsx b/frontend/src/AppBuilder/LeftSidebar/PageMenu/PagePermission.jsx index 6a4a1c516a..e82f251665 100644 --- a/frontend/src/AppBuilder/LeftSidebar/PageMenu/PagePermission.jsx +++ b/frontend/src/AppBuilder/LeftSidebar/PageMenu/PagePermission.jsx @@ -34,7 +34,6 @@ export default function PagePermission({ darkMode }) { const [showConfirmDelete, setShowConfirmDelete] = useState(false); const [isLoading, setIsLoading] = useState(false); const [isPermissionsLoading, setPermissionsLoading] = useState(true); - const [pageToDelete, setPageToDelete] = useState(null); const [initialSelectedGroups, setInitialSelectedGroups] = useState([]); const [initialSelectedUsers, setInitialSelectedUsers] = useState([]); const [initalPagePermissionType, setInitialPagePermissionType] = useState('all'); @@ -42,7 +41,7 @@ export default function PagePermission({ darkMode }) { useEffect(() => { if (!showPagePermissionModal) return; const fetchPagePermission = () => { - appPermissionService.getPagePermission(appId, editingPage?.id || pageToDelete).then((data) => { + appPermissionService.getPagePermission(appId, editingPage?.id).then((data) => { if (data) { if (data[0] && data[0]?.type === PERMISSION_TYPES.group) { const groups = @@ -55,7 +54,6 @@ export default function PagePermission({ darkMode }) { setInitialPagePermissionType(data[0]?.type?.toLowerCase()); setPagePermission(data); toggleUserGroupSelect(true); - setPageToDelete(null); setInitialSelectedGroups(groups); data?.length && setSelectedUserGroups(groups); } else if (data[0] && data[0]?.type === PERMISSION_TYPES.single) { @@ -74,7 +72,6 @@ export default function PagePermission({ darkMode }) { setInitialPagePermissionType(data[0]?.type?.toLowerCase()); setPagePermission(data); toggleUsersSelect(true); - setPageToDelete(null); setInitialSelectedUsers(users); data?.length && setSelectedUsers(users); } @@ -83,7 +80,7 @@ export default function PagePermission({ darkMode }) { }); }; fetchPagePermission(); - }, [showPagePermissionModal, pageToDelete]); + }, [showPagePermissionModal]); const isSelectionUnchanged = useMemo(() => { if (pagePermissionType === 'group') { @@ -237,13 +234,12 @@ export default function PagePermission({ darkMode }) { const deletePagePermission = () => { setIsLoading(true); appPermissionService - .deletePagePermission(appId, pageToDelete) + .deletePagePermission(appId, editingPage?.id) .then((data) => { toast.success('Permission successfully deleted!', { className: 'text-nowrap w-auto mw-100', }); - updatePageWithPermissions(pageToDelete, []); - setPageToDelete(null); + updatePageWithPermissions(editingPage?.id, []); }) .catch(() => { toast.error('Permission could not be deleted. Please try again!', { @@ -284,25 +280,18 @@ export default function PagePermission({ darkMode }) { isLoading={isLoading} handleClose={handlePagePermissionModalClose} confirmBtnProps={{ - title: pagePermission ? 'Update' : pagePermissionType === 'all' ? 'Default permission' : 'Create permission', + 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" - headerAction={() => - pagePermission && ( - { - setPageToDelete(editingPage?.id); - togglePagePermissionModal(false); - setShowConfirmDelete(true); - }} - > - - - ) - } >
{isPermissionsLoading ? ( diff --git a/frontend/src/AppBuilder/LeftSidebar/PageMenu/style.scss b/frontend/src/AppBuilder/LeftSidebar/PageMenu/style.scss index 968218b106..a3adc9ec20 100644 --- a/frontend/src/AppBuilder/LeftSidebar/PageMenu/style.scss +++ b/frontend/src/AppBuilder/LeftSidebar/PageMenu/style.scss @@ -374,4 +374,8 @@ .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/Viewer/PageGroup.jsx b/frontend/src/AppBuilder/Viewer/PageGroup.jsx index 0311115b09..1739263fff 100644 --- a/frontend/src/AppBuilder/Viewer/PageGroup.jsx +++ b/frontend/src/AppBuilder/Viewer/PageGroup.jsx @@ -11,8 +11,6 @@ import cx from 'classnames'; const RenderPage = ({ page, currentPageId, switchPageWrapper, labelStyle, computeStyles, darkMode, homePageId }) => { const isHomePage = page.id === homePageId; - console.log({ page, homePageId }); - console.log({ isHomePage }); const iconName = isHomePage && !page.icon ? 'IconHome2' : page.icon; const IconElement = Icons?.[iconName] ?? Icons?.['IconFileDescription']; return (page.hidden || page.disabled) && page?.restricted ? null : ( diff --git a/frontend/src/_styles/components.scss b/frontend/src/_styles/components.scss index 074338602e..059841e6c7 100644 --- a/frontend/src/_styles/components.scss +++ b/frontend/src/_styles/components.scss @@ -242,10 +242,16 @@ $btn-dark-color: #FFFFFF; display: flex; align-items: baseline; gap: 5px; + cursor: pointer !important; + pointer-events: unset !important; &.disabled { opacity: 1 !important; } + + svg { + margin-left: 5px; + } } } diff --git a/frontend/src/_ui/Icon/solidIcons/EnterrpiseCrown.jsx b/frontend/src/_ui/Icon/solidIcons/EnterrpiseCrown.jsx new file mode 100644 index 0000000000..eed8dd5e8c --- /dev/null +++ b/frontend/src/_ui/Icon/solidIcons/EnterrpiseCrown.jsx @@ -0,0 +1,19 @@ +import React from 'react'; + +const EnterpriseCrown = ({ fill = '#FCA23F', width = '12', className = '', viewBox = '0 0 16 16' }) => ( + + + +); + +export default EnterpriseCrown; diff --git a/frontend/src/_ui/Icon/solidIcons/index.js b/frontend/src/_ui/Icon/solidIcons/index.js index 5daa4b7c91..a190dd4c52 100644 --- a/frontend/src/_ui/Icon/solidIcons/index.js +++ b/frontend/src/_ui/Icon/solidIcons/index.js @@ -234,6 +234,7 @@ import NewTabSmall from './NewTabSmall.jsx'; import Code from './Code.jsx'; import WorkflowV3 from './WorkflowV3.jsx'; import WorkspaceV3 from './WorkspaceV3.jsx'; +import EnterpriseCrown from './EnterrpiseCrown.jsx'; const Icon = (props) => { switch (props.name) { @@ -355,6 +356,8 @@ const Icon = (props) => { return ; case 'enterprisev3': return ; + case 'enterprisecrown': + return ; case 'lockGradient': return ; case 'datasourceGradient': diff --git a/server/ee b/server/ee index 84ec48d0f6..bd6745ee0a 160000 --- a/server/ee +++ b/server/ee @@ -1 +1 @@ -Subproject commit 84ec48d0f64fd6dc5f7677f71a5119219cc4ada4 +Subproject commit bd6745ee0a9344d5fcf42a79a50b8185ec43643a diff --git a/server/src/modules/app-permissions/repositories/page-permissions.repository.ts b/server/src/modules/app-permissions/repositories/page-permissions.repository.ts index 7caa5f4318..a36be08bc7 100644 --- a/server/src/modules/app-permissions/repositories/page-permissions.repository.ts +++ b/server/src/modules/app-permissions/repositories/page-permissions.repository.ts @@ -19,14 +19,10 @@ export class PagePermissionsRepository extends Repository { }); return pagePermissions.map((permission) => { - if (permission.type === PAGE_PERMISSION_TYPE.GROUP) { - return { - ...permission, - groups: permission.users, - users: undefined, - }; - } - return permission; + return { + ...permission, + users: permission.users, + }; }); }, manager || this.manager); } diff --git a/server/src/modules/versions/module.ts b/server/src/modules/versions/module.ts index 1f08dc76bb..80929f4a3a 100644 --- a/server/src/modules/versions/module.ts +++ b/server/src/modules/versions/module.ts @@ -9,6 +9,7 @@ import { DataSourcesModule } from '@modules/data-sources/module'; import { AppsRepository } from '@modules/apps/repository'; import { FeatureAbilityFactory } from './ability'; import { getImportPath } from '@modules/app/constants'; +import { AppPermissionsModule } from '@modules/app-permissions/module'; export class VersionModule { static async register(configs?: { IS_GET_CONTEXT: boolean }): Promise { @@ -33,6 +34,7 @@ export class VersionModule { await DataSourcesModule.register(configs), await AppEnvironmentsModule.register(configs), await ThemesModule.register(configs), + await AppPermissionsModule.register(configs), ], controllers: [ComponentsController, EventsController, PagesController, VersionController, VersionControllerV2], providers: [