From 8c53ac4bab5a14bf26e3f3894e2e44d81b7c2eee Mon Sep 17 00:00:00 2001 From: Vijaykant Yadav Date: Tue, 22 Apr 2025 09:27:02 +0530 Subject: [PATCH] feat: add crud operatios for page permissions --- .../editor/left-sidebar/authorization.svg | 3 + .../LeftSidebar/PageMenu/PageHandlerMenu.jsx | 12 + .../LeftSidebar/PageMenu/PageMenu.jsx | 2 + .../LeftSidebar/PageMenu/PagePermission.jsx | 377 +++++++++++++++ .../LeftSidebar/PageMenu/style.scss | 72 +++ frontend/src/AppBuilder/Viewer/PageGroup.jsx | 5 +- frontend/src/AppBuilder/_hooks/useAppData.js | 440 ++++++++++-------- .../AppBuilder/_stores/slices/eventsSlice.js | 4 + .../_stores/slices/pageMenuSlice.js | 27 +- frontend/src/_helpers/constants.js | 7 + frontend/src/_helpers/handleAppAccess.js | 6 +- .../src/_services/appPermission.service.js | 49 ++ frontend/src/_services/index.js | 1 + frontend/src/_ui/Modal/index.jsx | 4 +- server/ee | 2 +- server/src/modules/app-permissions/module.ts | 2 +- .../page-permissions.repository.ts | 2 +- .../repositories/page-users.repository.ts | 46 ++ server/src/modules/apps/module.ts | 2 + server/src/modules/workflows/module.ts | 2 + 20 files changed, 850 insertions(+), 215 deletions(-) create mode 100644 frontend/assets/images/icons/editor/left-sidebar/authorization.svg create mode 100644 frontend/src/AppBuilder/LeftSidebar/PageMenu/PagePermission.jsx create mode 100644 frontend/src/_services/appPermission.service.js diff --git a/frontend/assets/images/icons/editor/left-sidebar/authorization.svg b/frontend/assets/images/icons/editor/left-sidebar/authorization.svg new file mode 100644 index 0000000000..609f7a5910 --- /dev/null +++ b/frontend/assets/images/icons/editor/left-sidebar/authorization.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/AppBuilder/LeftSidebar/PageMenu/PageHandlerMenu.jsx b/frontend/src/AppBuilder/LeftSidebar/PageMenu/PageHandlerMenu.jsx index e2a395ecab..ec84149418 100644 --- a/frontend/src/AppBuilder/LeftSidebar/PageMenu/PageHandlerMenu.jsx +++ b/frontend/src/AppBuilder/LeftSidebar/PageMenu/PageHandlerMenu.jsx @@ -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} /> + { + togglePagePermissionModal(true); + }} + /> { const showAddNewPageInput = useStore((state) => state.showAddNewPageInput); @@ -94,6 +95,7 @@ export const PageMenu = ({ darkMode, switchPage, pinned, setPinned }) => { >
+ diff --git a/frontend/src/AppBuilder/LeftSidebar/PageMenu/PagePermission.jsx b/frontend/src/AppBuilder/LeftSidebar/PageMenu/PagePermission.jsx new file mode 100644 index 0000000000..ce7c78e6f3 --- /dev/null +++ b/frontend/src/AppBuilder/LeftSidebar/PageMenu/PagePermission.jsx @@ -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 ( +
+
+ +
+
+ {label} +
+
+ ); + }; + + return ( + <> + + Page permission +
+ } + 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 && ( + { + togglePagePermissionModal(false); + setShowConfirmDelete(true); + }} + > + + + ) + } + > +
+
+
+ +
+
+
+

+ Only selected users will be allowed to access this page. Read docs to know more. +

+
+
+
+ + setSelectedUserGroups(groups)} + /> +
+ ); +}; + +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 ( + +
+
{data.initials}
+
+
{data.label}
+
{data.email}
+
+
+
+ ); + }; + + console.log({ users }); + + return ( +
+ +