mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-23 08:58:26 +00:00
Extracted page permission modal as a reusable component
This commit is contained in:
parent
dc0d46cd7b
commit
3bd6bc5beb
10 changed files with 55 additions and 641 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit 777446d71e78e5941d34353606a12d982820438f
|
||||
Subproject commit 5c2787b498202f4356b4598fb961ddbab04acbbf
|
||||
|
|
@ -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 }) => {
|
|||
>
|
||||
<div>
|
||||
<PageHandlerMenu darkMode={darkMode} />
|
||||
{isLicensed ? <PagePermission darkMode={darkMode} /> : <></>}
|
||||
{isLicensed && (
|
||||
<AppPermissionsModal
|
||||
modalType="page"
|
||||
resourceId={editingPageId}
|
||||
showModal={showPagePermissionModal}
|
||||
toggleModal={togglePagePermissionModal}
|
||||
darkMode={darkMode}
|
||||
fetchPermission={(id, appId) => 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)}
|
||||
/>
|
||||
)}
|
||||
<EditModal darkMode={darkMode} />
|
||||
<SettingsModal darkMode={darkMode} />
|
||||
<DeletePageConfirmationModal darkMode={darkMode} />
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div className="row permission-type-select" style={{ padding: '0px 8px' }}>
|
||||
<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
|
||||
? '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"
|
||||
>
|
||||
<div className="page-permission">
|
||||
{isPermissionsLoading ? (
|
||||
<div className="spinner-center">
|
||||
<Spinner />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<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 = () => {
|
||||
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 (
|
||||
<components.Option {...props}>
|
||||
<div className={`user-select-option ${isFocused ? 'focused' : ''}`}>
|
||||
<input
|
||||
style={{ width: '1.2rem', height: '1.2rem', borderRadius: '6px !important' }}
|
||||
type={'checkbox'}
|
||||
className="form-check-input"
|
||||
checked={isSelected}
|
||||
/>
|
||||
<div className="group-info">
|
||||
<div className="name">{data.label}</div>
|
||||
<div className="count">{data.count} users</div>
|
||||
</div>
|
||||
</div>
|
||||
</components.Option>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label className="form-label mt-3">User groups</label>
|
||||
<Select
|
||||
isMulti={true}
|
||||
options={userGroups}
|
||||
value={selectedUserGroups}
|
||||
width={'100%'}
|
||||
closeMenuOnSelect={false}
|
||||
components={{ Option: CustomOption, MenuList: CustomMenuList }}
|
||||
useMenuPortal={false}
|
||||
hideSelectedOptions={false}
|
||||
onChange={(groups) => setSelectedUserGroups(groups)}
|
||||
info="Only user groups with access to this application can be selected"
|
||||
/>
|
||||
</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) => {
|
||||
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' : ''}`}>
|
||||
<input
|
||||
style={{ width: '1.2rem', height: '1.2rem', borderRadius: '6px !important' }}
|
||||
type={'checkbox'}
|
||||
className="form-check-input"
|
||||
checked={isSelected}
|
||||
/>
|
||||
<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>
|
||||
);
|
||||
};
|
||||
|
||||
const selectStyles = {
|
||||
option: (base) => ({
|
||||
...base,
|
||||
padding: '8px 0px',
|
||||
}),
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<label className="form-label mt-3">Users</label>
|
||||
<Select
|
||||
isMulti={true}
|
||||
options={users}
|
||||
value={selectedUsers}
|
||||
width={'100%'}
|
||||
useMenuPortal={false}
|
||||
closeMenuOnSelect={false}
|
||||
components={{ Option: CustomOption, MenuList: CustomMenuList }}
|
||||
styles={selectStyles}
|
||||
hideSelectedOptions={false}
|
||||
info="Only user with access to this application can be selected"
|
||||
onChange={(users) => {
|
||||
setSelectedUsers(users);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const CustomMenuList = (props) => {
|
||||
const { info } = props.selectProps;
|
||||
return (
|
||||
<components.MenuList {...props}>
|
||||
<div className="info-container" style={{ marginLeft: '12px', marginRight: '12px', marginTop: '8px' }}>
|
||||
<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' }}>{info}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{props.children}
|
||||
</components.MenuList>
|
||||
);
|
||||
};
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
import React from 'react';
|
||||
import { withEditionSpecificComponent } from '@/modules/common/helpers/withEditionSpecificComponent';
|
||||
|
||||
const AppPermissionsModal = () => {
|
||||
return <></>;
|
||||
};
|
||||
|
||||
export default withEditionSpecificComponent(AppPermissionsModal, 'Appbuilder');
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './AppPermissionsModal';
|
||||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue