add object settings permission tab (#10159)
## Context Introducing the "Permissions" tab in the role page Next: Need to address some css improvements, some components might be reusable and it still does not fully match the figma (icon missing for permission types for example). We decided to merge like this for now so we have something functional and I will update the code in an upcoming PR <img width="633" alt="Screenshot 2025-02-12 at 13 54 16" src="https://github.com/user-attachments/assets/762db5d7-e0a6-4ee1-b299-24de6645bad1" />
This commit is contained in:
@ -64,7 +64,7 @@ const StyledAvatarGroup = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledTableHeaderRow = styled(Table)`
|
const StyledTableHeaderRow = styled(Table)`
|
||||||
margin-bottom: ${({ theme }) => theme.spacing(1.5)};
|
margin-bottom: ${({ theme }) => theme.spacing(2)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledBottomSection = styled(Section)`
|
const StyledBottomSection = styled(Section)`
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { currentWorkspaceMembersState } from '@/auth/states/currentWorkspaceMembersStates';
|
||||||
import { SettingsPath } from '@/types/SettingsPath';
|
import { SettingsPath } from '@/types/SettingsPath';
|
||||||
import { TextInput } from '@/ui/input/components/TextInput';
|
import { TextInput } from '@/ui/input/components/TextInput';
|
||||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||||
@ -6,7 +7,16 @@ import { Table } from '@/ui/layout/table/components/Table';
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { t } from '@lingui/core/macro';
|
import { t } from '@lingui/core/macro';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Button, H2Title, IconPlus, IconSearch, Section } from 'twenty-ui';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
import {
|
||||||
|
AppTooltip,
|
||||||
|
Button,
|
||||||
|
H2Title,
|
||||||
|
IconPlus,
|
||||||
|
IconSearch,
|
||||||
|
Section,
|
||||||
|
TooltipDelay,
|
||||||
|
} from 'twenty-ui';
|
||||||
import { Role, WorkspaceMember } from '~/generated-metadata/graphql';
|
import { Role, WorkspaceMember } from '~/generated-metadata/graphql';
|
||||||
import {
|
import {
|
||||||
GetRolesDocument,
|
GetRolesDocument,
|
||||||
@ -36,14 +46,6 @@ const StyledBottomSection = styled(Section)<{ hasRows: boolean }>`
|
|||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledEmptyText = styled.div`
|
|
||||||
align-items: center;
|
|
||||||
color: ${({ theme }) => theme.font.color.light};
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
margin-top: ${({ theme }) => theme.spacing(4)};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledSearchContainer = styled.div`
|
const StyledSearchContainer = styled.div`
|
||||||
margin: ${({ theme }) => theme.spacing(2)} 0;
|
margin: ${({ theme }) => theme.spacing(2)} 0;
|
||||||
`;
|
`;
|
||||||
@ -80,6 +82,7 @@ export const RoleAssignment = ({ role }: RoleAssignmentProps) => {
|
|||||||
const { data: rolesData } = useGetRolesQuery();
|
const { data: rolesData } = useGetRolesQuery();
|
||||||
const { closeDropdown } = useDropdown('role-member-select');
|
const { closeDropdown } = useDropdown('role-member-select');
|
||||||
const [searchFilter, setSearchFilter] = useState('');
|
const [searchFilter, setSearchFilter] = useState('');
|
||||||
|
const currentWorkspaceMembers = useRecoilValue(currentWorkspaceMembersState);
|
||||||
|
|
||||||
const workspaceMemberRoleMap = new Map<
|
const workspaceMemberRoleMap = new Map<
|
||||||
string,
|
string,
|
||||||
@ -154,6 +157,9 @@ export const RoleAssignment = ({ role }: RoleAssignmentProps) => {
|
|||||||
setSearchFilter(text);
|
setSearchFilter(text);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const allWorkspaceMembersHaveThisRole =
|
||||||
|
role.workspaceMembers.length === currentWorkspaceMembers.length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Section>
|
<Section>
|
||||||
@ -180,13 +186,6 @@ export const RoleAssignment = ({ role }: RoleAssignmentProps) => {
|
|||||||
onRemove={() => handleRemoveClick(workspaceMember)}
|
onRemove={() => handleRemoveClick(workspaceMember)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{filteredWorkspaceMembers.length === 0 && (
|
|
||||||
<StyledEmptyText>
|
|
||||||
{searchFilter
|
|
||||||
? t`No members matching your search`
|
|
||||||
: t`No members assigned to this role yet`}
|
|
||||||
</StyledEmptyText>
|
|
||||||
)}
|
|
||||||
</Table>
|
</Table>
|
||||||
</Section>
|
</Section>
|
||||||
<StyledBottomSection hasRows={filteredWorkspaceMembers.length > 0}>
|
<StyledBottomSection hasRows={filteredWorkspaceMembers.length > 0}>
|
||||||
@ -194,12 +193,23 @@ export const RoleAssignment = ({ role }: RoleAssignmentProps) => {
|
|||||||
dropdownId="role-member-select"
|
dropdownId="role-member-select"
|
||||||
dropdownHotkeyScope={{ scope: 'roleAssignment' }}
|
dropdownHotkeyScope={{ scope: 'roleAssignment' }}
|
||||||
clickableComponent={
|
clickableComponent={
|
||||||
<Button
|
<>
|
||||||
Icon={IconPlus}
|
<div id="assign-member">
|
||||||
title={t`Assign to member`}
|
<Button
|
||||||
variant="secondary"
|
Icon={IconPlus}
|
||||||
size="small"
|
title={t`Assign to member`}
|
||||||
/>
|
variant="secondary"
|
||||||
|
size="small"
|
||||||
|
disabled={allWorkspaceMembersHaveThisRole}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<AppTooltip
|
||||||
|
anchorSelect="#assign-member"
|
||||||
|
content={t`No more members to assign`}
|
||||||
|
delay={TooltipDelay.noDelay}
|
||||||
|
hidden={!allWorkspaceMembersHaveThisRole}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
}
|
}
|
||||||
dropdownComponents={
|
dropdownComponents={
|
||||||
<RoleWorkspaceMemberPickerDropdown
|
<RoleWorkspaceMemberPickerDropdown
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import styled from '@emotion/styled';
|
|||||||
import { t } from '@lingui/core/macro';
|
import { t } from '@lingui/core/macro';
|
||||||
|
|
||||||
const StyledTableHeaderRow = styled(Table)`
|
const StyledTableHeaderRow = styled(Table)`
|
||||||
margin-bottom: ${({ theme }) => theme.spacing(1.5)};
|
margin-bottom: ${({ theme }) => theme.spacing(2)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type RoleAssignmentTableHeaderProps = {
|
type RoleAssignmentTableHeaderProps = {
|
||||||
|
|||||||
@ -1,19 +1,126 @@
|
|||||||
|
import styled from '@emotion/styled';
|
||||||
import { t } from '@lingui/core/macro';
|
import { t } from '@lingui/core/macro';
|
||||||
import { H2Title, Section } from 'twenty-ui';
|
import {
|
||||||
import { Role } from '~/generated-metadata/graphql';
|
H2Title,
|
||||||
|
IconEye,
|
||||||
|
IconPencil,
|
||||||
|
IconTrash,
|
||||||
|
IconTrashX,
|
||||||
|
Section,
|
||||||
|
} from 'twenty-ui';
|
||||||
|
import { Role, SettingsFeatures } from '~/generated-metadata/graphql';
|
||||||
|
import { RolePermissionsObjectsTableHeader } from '~/pages/settings/roles/components/RolePermissionsObjectsTableHeader';
|
||||||
|
import { RolePermissionsSettingsTableHeader } from '~/pages/settings/roles/components/RolePermissionsSettingsTableHeader';
|
||||||
|
import { RolePermissionsSettingsTableRow } from '~/pages/settings/roles/components/RolePermissionsSettingsTableRow';
|
||||||
|
import { RolePermissionsObjectPermission } from '~/pages/settings/roles/types/RolePermissionsObjectPermission';
|
||||||
|
import { RolePermissionsObjectsTableRow } from './RolePermissionsObjectsTableRow';
|
||||||
|
|
||||||
type RolePermissionsProps = {
|
const StyledRolePermissionsContainer = styled.div`
|
||||||
role: Pick<Role, 'id' | 'label' | 'canUpdateAllSettings'>;
|
display: flex;
|
||||||
};
|
flex-direction: column;
|
||||||
|
gap: ${({ theme }) => theme.spacing(8)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const RolePermissions = ({ role }: { role: Role }) => {
|
||||||
|
const objectPermissionsConfig: RolePermissionsObjectPermission[] = [
|
||||||
|
{
|
||||||
|
key: 'seeRecords',
|
||||||
|
label: 'See Records on All Objects',
|
||||||
|
icon: <IconEye size={14} />,
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'editRecords',
|
||||||
|
label: 'Edit Records on All Objects',
|
||||||
|
icon: <IconPencil size={14} />,
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'deleteRecords',
|
||||||
|
label: 'Delete Records on All Objects',
|
||||||
|
icon: <IconTrash size={14} />,
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'destroyRecords',
|
||||||
|
label: 'Destroy Records on All Objects',
|
||||||
|
icon: <IconTrashX size={14} />,
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const settingsPermissionsConfig = [
|
||||||
|
{
|
||||||
|
key: SettingsFeatures.API_KEYS_AND_WEBHOOKS,
|
||||||
|
label: 'API Keys and Webhooks',
|
||||||
|
type: 'Developer',
|
||||||
|
value: role.canUpdateAllSettings,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingsFeatures.ROLES,
|
||||||
|
label: 'Roles',
|
||||||
|
type: 'Members',
|
||||||
|
value: role.canUpdateAllSettings,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingsFeatures.WORKSPACE_SETTINGS,
|
||||||
|
label: 'Workspace Settings',
|
||||||
|
type: 'General',
|
||||||
|
value: role.canUpdateAllSettings,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingsFeatures.WORKSPACE_USERS,
|
||||||
|
label: 'Workspace Users',
|
||||||
|
type: 'Members',
|
||||||
|
value: role.canUpdateAllSettings,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingsFeatures.DATA_MODEL,
|
||||||
|
label: 'Data Model',
|
||||||
|
type: 'Data Model',
|
||||||
|
value: role.canUpdateAllSettings,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingsFeatures.ADMIN_PANEL,
|
||||||
|
label: 'Admin Panel',
|
||||||
|
type: 'Admin Panel',
|
||||||
|
value: role.canUpdateAllSettings,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: SettingsFeatures.SECURITY_SETTINGS,
|
||||||
|
label: 'Security Settings',
|
||||||
|
type: 'Security',
|
||||||
|
value: role.canUpdateAllSettings,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
||||||
export const RolePermissions = ({ role }: RolePermissionsProps) => {
|
|
||||||
return (
|
return (
|
||||||
<Section>
|
<StyledRolePermissionsContainer>
|
||||||
<H2Title
|
<Section>
|
||||||
title={t`Permissions`}
|
<H2Title
|
||||||
description={t`This Role has the following permissions.`}
|
title={t`Objects`}
|
||||||
/>
|
description={t`Ability to interact with each object`}
|
||||||
</Section>
|
/>
|
||||||
|
<RolePermissionsObjectsTableHeader allPermissions={true} />
|
||||||
|
{objectPermissionsConfig.map((permission) => (
|
||||||
|
<RolePermissionsObjectsTableRow
|
||||||
|
key={permission.key}
|
||||||
|
permission={permission}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Section>
|
||||||
|
<Section>
|
||||||
|
<H2Title title={t`Settings`} description={t`Settings permissions`} />
|
||||||
|
<RolePermissionsSettingsTableHeader
|
||||||
|
allPermissions={role.canUpdateAllSettings}
|
||||||
|
/>
|
||||||
|
{settingsPermissionsConfig.map((permission) => (
|
||||||
|
<RolePermissionsSettingsTableRow
|
||||||
|
key={permission.key}
|
||||||
|
permission={permission}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Section>
|
||||||
|
</StyledRolePermissionsContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,44 @@
|
|||||||
|
import { Table } from '@/ui/layout/table/components/Table';
|
||||||
|
import { TableHeader } from '@/ui/layout/table/components/TableHeader';
|
||||||
|
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
import { t } from '@lingui/core/macro';
|
||||||
|
import { Checkbox } from 'twenty-ui';
|
||||||
|
|
||||||
|
const StyledTableHeaderRow = styled(TableRow)`
|
||||||
|
display: flex;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledNameHeader = styled(TableHeader)`
|
||||||
|
flex: 1;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledActionsHeader = styled(TableHeader)`
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding-right: ${({ theme }) => theme.spacing(4)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledTable = styled(Table)`
|
||||||
|
margin-bottom: ${({ theme }) => theme.spacing(2)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
type RolePermissionsObjectsTableHeaderProps = {
|
||||||
|
className?: string;
|
||||||
|
allPermissions: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RolePermissionsObjectsTableHeader = ({
|
||||||
|
className,
|
||||||
|
allPermissions,
|
||||||
|
}: RolePermissionsObjectsTableHeaderProps) => (
|
||||||
|
<StyledTable className={className}>
|
||||||
|
<StyledTableHeaderRow>
|
||||||
|
<StyledNameHeader>{t`Name`}</StyledNameHeader>
|
||||||
|
<StyledActionsHeader aria-label={t`Actions`}>
|
||||||
|
<Checkbox checked={allPermissions} disabled />
|
||||||
|
</StyledActionsHeader>
|
||||||
|
</StyledTableHeaderRow>
|
||||||
|
</StyledTable>
|
||||||
|
);
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||||
|
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
import { Checkbox } from 'twenty-ui';
|
||||||
|
import { RolePermissionsObjectPermission } from '~/pages/settings/roles/types/RolePermissionsObjectPermission';
|
||||||
|
|
||||||
|
const StyledIconWrapper = styled.div`
|
||||||
|
align-items: center;
|
||||||
|
background: ${({ theme }) => theme.color.blue10};
|
||||||
|
border: 1px solid ${({ theme }) => theme.color.blue30};
|
||||||
|
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||||
|
display: flex;
|
||||||
|
height: ${({ theme }) => theme.spacing(4)};
|
||||||
|
justify-content: center;
|
||||||
|
width: ${({ theme }) => theme.spacing(4)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledIcon = styled.div`
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
color: ${({ theme }) => theme.color.blue};
|
||||||
|
justify-content: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledLabel = styled.span`
|
||||||
|
color: ${({ theme }) => theme.font.color.primary};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledPermissionCell = styled(TableCell)`
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
gap: ${({ theme }) => theme.spacing(2)};
|
||||||
|
padding-left: ${({ theme }) => theme.spacing(2)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledCheckboxCell = styled(TableCell)`
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding-right: ${({ theme }) => theme.spacing(4)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledTableRow = styled(TableRow)`
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
`;
|
||||||
|
|
||||||
|
type RolePermissionsObjectsTableRowProps = {
|
||||||
|
permission: RolePermissionsObjectPermission;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RolePermissionsObjectsTableRow = ({
|
||||||
|
permission,
|
||||||
|
}: RolePermissionsObjectsTableRowProps) => {
|
||||||
|
return (
|
||||||
|
<StyledTableRow key={permission.key}>
|
||||||
|
<StyledPermissionCell>
|
||||||
|
<StyledIconWrapper>
|
||||||
|
<StyledIcon>{permission.icon}</StyledIcon>
|
||||||
|
</StyledIconWrapper>
|
||||||
|
<StyledLabel>{permission.label}</StyledLabel>
|
||||||
|
</StyledPermissionCell>
|
||||||
|
<StyledCheckboxCell>
|
||||||
|
<Checkbox checked={permission.value} disabled />
|
||||||
|
</StyledCheckboxCell>
|
||||||
|
</StyledTableRow>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
import { Table } from '@/ui/layout/table/components/Table';
|
||||||
|
import { TableHeader } from '@/ui/layout/table/components/TableHeader';
|
||||||
|
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
import { t } from '@lingui/core/macro';
|
||||||
|
import { Checkbox } from 'twenty-ui';
|
||||||
|
|
||||||
|
const StyledTableHeaderRow = styled(TableRow)`
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
height: ${({ theme }) => theme.spacing(8)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledNameHeader = styled(TableHeader)`
|
||||||
|
flex: 1;
|
||||||
|
padding-left: ${({ theme }) => theme.spacing(2)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledActionsHeader = styled(TableHeader)`
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding-right: ${({ theme }) => theme.spacing(4)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledTable = styled(Table)`
|
||||||
|
margin-bottom: ${({ theme }) => theme.spacing(2)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledTypeHeader = styled(TableHeader)`
|
||||||
|
flex: 1;
|
||||||
|
`;
|
||||||
|
|
||||||
|
type RolePermissionsSettingsTableHeaderProps = {
|
||||||
|
className?: string;
|
||||||
|
allPermissions: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RolePermissionsSettingsTableHeader = ({
|
||||||
|
className,
|
||||||
|
allPermissions,
|
||||||
|
}: RolePermissionsSettingsTableHeaderProps) => (
|
||||||
|
<StyledTable className={className}>
|
||||||
|
<StyledTableHeaderRow>
|
||||||
|
<StyledNameHeader>{t`Name`}</StyledNameHeader>
|
||||||
|
<StyledTypeHeader>{t`Type`}</StyledTypeHeader>
|
||||||
|
<StyledActionsHeader aria-label={t`Actions`}>
|
||||||
|
<Checkbox checked={allPermissions} disabled />
|
||||||
|
</StyledActionsHeader>
|
||||||
|
</StyledTableHeaderRow>
|
||||||
|
</StyledTable>
|
||||||
|
);
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||||
|
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
import { Checkbox } from 'twenty-ui';
|
||||||
|
import { RolePermissionsSettingPermission } from '~/pages/settings/roles/types/RolePermissionsSettingPermission';
|
||||||
|
|
||||||
|
const StyledLabel = styled.span`
|
||||||
|
color: ${({ theme }) => theme.font.color.primary};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledType = styled(StyledLabel)`
|
||||||
|
color: ${({ theme }) => theme.font.color.secondary};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledPermissionCell = styled(TableCell)`
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
gap: ${({ theme }) => theme.spacing(2)};
|
||||||
|
padding-left: ${({ theme }) => theme.spacing(2)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledCheckboxCell = styled(TableCell)`
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding-right: ${({ theme }) => theme.spacing(4)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledTableRow = styled(TableRow)`
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
`;
|
||||||
|
|
||||||
|
type RolePermissionsSettingsTableRowProps = {
|
||||||
|
permission: RolePermissionsSettingPermission;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RolePermissionsSettingsTableRow = ({
|
||||||
|
permission,
|
||||||
|
}: RolePermissionsSettingsTableRowProps) => {
|
||||||
|
return (
|
||||||
|
<StyledTableRow key={permission.key}>
|
||||||
|
<StyledPermissionCell>
|
||||||
|
<StyledLabel>{permission.label}</StyledLabel>
|
||||||
|
</StyledPermissionCell>
|
||||||
|
<StyledPermissionCell>
|
||||||
|
<StyledType>{permission.type}</StyledType>
|
||||||
|
</StyledPermissionCell>
|
||||||
|
<StyledCheckboxCell>
|
||||||
|
<Checkbox checked={permission.value} disabled />
|
||||||
|
</StyledCheckboxCell>
|
||||||
|
</StyledTableRow>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,17 +1,13 @@
|
|||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
import { useSearchRecords } from '@/object-record/hooks/useSearchRecords';
|
||||||
|
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
||||||
import styled from '@emotion/styled';
|
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||||
import { ChangeEvent, useState } from 'react';
|
import { ChangeEvent, useState } from 'react';
|
||||||
import { WorkspaceMember } from '~/generated-metadata/graphql';
|
import { WorkspaceMember } from '~/generated-metadata/graphql';
|
||||||
import { RoleWorkspaceMemberPickerDropdownContent } from './RoleWorkspaceMemberPickerDropdownContent';
|
import { RoleWorkspaceMemberPickerDropdownContent } from './RoleWorkspaceMemberPickerDropdownContent';
|
||||||
|
|
||||||
const StyledWorkspaceMemberSelectContainer = styled.div`
|
|
||||||
max-height: ${({ theme }) => theme.spacing(50)};
|
|
||||||
overflow-y: auto;
|
|
||||||
`;
|
|
||||||
|
|
||||||
type RoleWorkspaceMemberPickerDropdownProps = {
|
type RoleWorkspaceMemberPickerDropdownProps = {
|
||||||
excludedWorkspaceMemberIds: string[];
|
excludedWorkspaceMemberIds: string[];
|
||||||
onSelect: (workspaceMember: WorkspaceMember) => void;
|
onSelect: (workspaceMember: WorkspaceMember) => void;
|
||||||
@ -23,23 +19,9 @@ export const RoleWorkspaceMemberPickerDropdown = ({
|
|||||||
}: RoleWorkspaceMemberPickerDropdownProps) => {
|
}: RoleWorkspaceMemberPickerDropdownProps) => {
|
||||||
const [searchFilter, setSearchFilter] = useState('');
|
const [searchFilter, setSearchFilter] = useState('');
|
||||||
|
|
||||||
const { records: workspaceMembers, loading } = useFindManyRecords({
|
const { loading, records: workspaceMembers } = useSearchRecords({
|
||||||
objectNameSingular: CoreObjectNameSingular.WorkspaceMember,
|
objectNameSingular: CoreObjectNameSingular.WorkspaceMember,
|
||||||
filter: searchFilter
|
searchInput: searchFilter,
|
||||||
? {
|
|
||||||
or: [
|
|
||||||
{
|
|
||||||
name: { firstName: { ilike: `%${searchFilter}%` } },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: { lastName: { ilike: `%${searchFilter}%` } },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
userEmail: { ilike: `%${searchFilter}%` },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const filteredWorkspaceMembers = (workspaceMembers?.filter(
|
const filteredWorkspaceMembers = (workspaceMembers?.filter(
|
||||||
@ -52,20 +34,21 @@ export const RoleWorkspaceMemberPickerDropdown = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenuItemsContainer>
|
<DropdownMenu>
|
||||||
<DropdownMenuSearchInput
|
<DropdownMenuSearchInput
|
||||||
value={searchFilter}
|
value={searchFilter}
|
||||||
onChange={handleSearchFilterChange}
|
onChange={handleSearchFilterChange}
|
||||||
placeholder="Search"
|
placeholder="Search"
|
||||||
/>
|
/>
|
||||||
<StyledWorkspaceMemberSelectContainer>
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItemsContainer>
|
||||||
<RoleWorkspaceMemberPickerDropdownContent
|
<RoleWorkspaceMemberPickerDropdownContent
|
||||||
loading={loading}
|
loading={loading}
|
||||||
searchFilter={searchFilter}
|
searchFilter={searchFilter}
|
||||||
filteredWorkspaceMembers={filteredWorkspaceMembers}
|
filteredWorkspaceMembers={filteredWorkspaceMembers}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
/>
|
/>
|
||||||
</StyledWorkspaceMemberSelectContainer>
|
</DropdownMenuItemsContainer>
|
||||||
</DropdownMenuItemsContainer>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,37 +1,7 @@
|
|||||||
import styled from '@emotion/styled';
|
|
||||||
import { t } from '@lingui/core/macro';
|
import { t } from '@lingui/core/macro';
|
||||||
import { Avatar } from 'twenty-ui';
|
import { MenuItem, MenuItemAvatar } from 'twenty-ui';
|
||||||
import { WorkspaceMember } from '~/generated-metadata/graphql';
|
import { WorkspaceMember } from '~/generated-metadata/graphql';
|
||||||
|
|
||||||
const StyledEmptyState = styled.div`
|
|
||||||
align-items: center;
|
|
||||||
color: ${({ theme }) => theme.font.color.light};
|
|
||||||
display: flex;
|
|
||||||
font-size: ${({ theme }) => theme.font.size.md};
|
|
||||||
font-weight: ${({ theme }) => theme.font.weight.regular};
|
|
||||||
height: ${({ theme }) => theme.spacing(8)};
|
|
||||||
justify-content: flex-start;
|
|
||||||
padding: ${({ theme }) => theme.spacing(2)};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledWorkspaceMemberItem = styled.div`
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
font-size: ${({ theme }) => theme.font.size.md};
|
|
||||||
gap: ${({ theme }) => theme.spacing(2)};
|
|
||||||
min-width: ${({ theme }) => theme.spacing(45)};
|
|
||||||
padding: ${({ theme }) => theme.spacing(2)};
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: ${({ theme }) => theme.background.tertiary};
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledWorkspaceMemberName = styled.div`
|
|
||||||
color: ${({ theme }) => theme.font.color.secondary};
|
|
||||||
`;
|
|
||||||
|
|
||||||
type RoleWorkspaceMemberPickerDropdownContentProps = {
|
type RoleWorkspaceMemberPickerDropdownContentProps = {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
searchFilter: string;
|
searchFilter: string;
|
||||||
@ -49,34 +19,24 @@ export const RoleWorkspaceMemberPickerDropdownContent = ({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!filteredWorkspaceMembers?.length) {
|
if (!filteredWorkspaceMembers?.length && searchFilter?.length > 0) {
|
||||||
return (
|
return <MenuItem disabled text={t`No Result`} />;
|
||||||
<StyledEmptyState>
|
|
||||||
{searchFilter
|
|
||||||
? t`No members matching this search`
|
|
||||||
: t`No more members to add`}
|
|
||||||
</StyledEmptyState>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{filteredWorkspaceMembers.map((workspaceMember) => (
|
{filteredWorkspaceMembers.map((workspaceMember) => (
|
||||||
<StyledWorkspaceMemberItem
|
<MenuItemAvatar
|
||||||
key={workspaceMember.id}
|
key={workspaceMember.id}
|
||||||
onClick={() => onSelect(workspaceMember)}
|
onClick={() => onSelect(workspaceMember)}
|
||||||
aria-label={`${workspaceMember.name.firstName} ${workspaceMember.name.lastName}`}
|
avatar={{
|
||||||
>
|
type: 'rounded',
|
||||||
<Avatar
|
size: 'md',
|
||||||
type="rounded"
|
placeholder: workspaceMember.name.firstName ?? '',
|
||||||
size="md"
|
placeholderColorSeed: workspaceMember.id,
|
||||||
placeholderColorSeed={workspaceMember.id}
|
}}
|
||||||
placeholder={workspaceMember.name.firstName ?? ''}
|
text={`${workspaceMember.name.firstName} ${workspaceMember.name.lastName}`}
|
||||||
/>
|
/>
|
||||||
<StyledWorkspaceMemberName>
|
|
||||||
{workspaceMember.name.firstName} {workspaceMember.name.lastName}
|
|
||||||
</StyledWorkspaceMemberName>
|
|
||||||
</StyledWorkspaceMemberItem>
|
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -0,0 +1,6 @@
|
|||||||
|
export type RolePermissionsObjectPermission = {
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
icon: React.ReactNode;
|
||||||
|
value: boolean;
|
||||||
|
};
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
export type RolePermissionsSettingPermission = {
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
type: string;
|
||||||
|
value: boolean;
|
||||||
|
};
|
||||||
@ -32,7 +32,6 @@ import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
|||||||
import { InjectCacheStorage } from 'src/engine/core-modules/cache-storage/decorators/cache-storage.decorator';
|
import { InjectCacheStorage } from 'src/engine/core-modules/cache-storage/decorators/cache-storage.decorator';
|
||||||
import { CacheStorageService } from 'src/engine/core-modules/cache-storage/services/cache-storage.service';
|
import { CacheStorageService } from 'src/engine/core-modules/cache-storage/services/cache-storage.service';
|
||||||
import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum';
|
import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum';
|
||||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
|
||||||
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
||||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||||
import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/field-metadata.service';
|
import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/field-metadata.service';
|
||||||
@ -43,12 +42,11 @@ import { PETS_METADATA_SEEDS } from 'src/engine/seeder/metadata-seeds/pets-metad
|
|||||||
import { SURVEY_RESULTS_METADATA_SEEDS } from 'src/engine/seeder/metadata-seeds/survey-results-metadata-seeds';
|
import { SURVEY_RESULTS_METADATA_SEEDS } from 'src/engine/seeder/metadata-seeds/survey-results-metadata-seeds';
|
||||||
import { SeederService } from 'src/engine/seeder/seeder.service';
|
import { SeederService } from 'src/engine/seeder/seeder.service';
|
||||||
import { shouldSeedWorkspaceFavorite } from 'src/engine/utils/should-seed-workspace-favorite';
|
import { shouldSeedWorkspaceFavorite } from 'src/engine/utils/should-seed-workspace-favorite';
|
||||||
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
|
|
||||||
import { createWorkspaceViews } from 'src/engine/workspace-manager/standard-objects-prefill-data/create-workspace-views';
|
import { createWorkspaceViews } from 'src/engine/workspace-manager/standard-objects-prefill-data/create-workspace-views';
|
||||||
import { seedViewWithDemoData } from 'src/engine/workspace-manager/standard-objects-prefill-data/seed-view-with-demo-data';
|
import { seedViewWithDemoData } from 'src/engine/workspace-manager/standard-objects-prefill-data/seed-view-with-demo-data';
|
||||||
import { opportunitiesTableByStageView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/opportunity-table-by-stage.view';
|
import { opportunitiesTableByStageView } from 'src/engine/workspace-manager/standard-objects-prefill-data/views/opportunity-table-by-stage.view';
|
||||||
|
import { WorkspaceManagerService } from 'src/engine/workspace-manager/workspace-manager.service';
|
||||||
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids';
|
||||||
import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.service';
|
|
||||||
|
|
||||||
// TODO: implement dry-run
|
// TODO: implement dry-run
|
||||||
@Command({
|
@Command({
|
||||||
@ -63,14 +61,12 @@ export class DataSeedWorkspaceCommand extends CommandRunner {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly dataSourceService: DataSourceService,
|
private readonly dataSourceService: DataSourceService,
|
||||||
private readonly typeORMService: TypeORMService,
|
private readonly typeORMService: TypeORMService,
|
||||||
private readonly workspaceSyncMetadataService: WorkspaceSyncMetadataService,
|
|
||||||
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
|
|
||||||
private readonly fieldMetadataService: FieldMetadataService,
|
private readonly fieldMetadataService: FieldMetadataService,
|
||||||
private readonly objectMetadataService: ObjectMetadataService,
|
private readonly objectMetadataService: ObjectMetadataService,
|
||||||
@InjectCacheStorage(CacheStorageNamespace.EngineWorkspace)
|
@InjectCacheStorage(CacheStorageNamespace.EngineWorkspace)
|
||||||
private readonly workspaceSchemaCache: CacheStorageService,
|
private readonly workspaceSchemaCache: CacheStorageService,
|
||||||
private readonly featureFlagService: FeatureFlagService,
|
|
||||||
private readonly seederService: SeederService,
|
private readonly seederService: SeederService,
|
||||||
|
private readonly workspaceManagerService: WorkspaceManagerService,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@ -100,21 +96,7 @@ export class DataSeedWorkspaceCommand extends CommandRunner {
|
|||||||
|
|
||||||
await rawDataSource.destroy();
|
await rawDataSource.destroy();
|
||||||
|
|
||||||
const schemaName =
|
await this.workspaceManagerService.initDev(workspaceId);
|
||||||
await this.workspaceDataSourceService.createWorkspaceDBSchema(
|
|
||||||
workspaceId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const dataSourceMetadata =
|
|
||||||
await this.dataSourceService.createDataSourceMetadata(
|
|
||||||
workspaceId,
|
|
||||||
schemaName,
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.workspaceSyncMetadataService.synchronize({
|
|
||||||
workspaceId: workspaceId,
|
|
||||||
dataSourceId: dataSourceMetadata.id,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async seedWorkspace(workspaceId: string) {
|
async seedWorkspace(workspaceId: string) {
|
||||||
|
|||||||
@ -4,6 +4,12 @@ import { DEMO_SEED_USER_IDS } from 'src/database/typeorm-seeds/core/demo/users';
|
|||||||
|
|
||||||
const tableName = 'userWorkspace';
|
const tableName = 'userWorkspace';
|
||||||
|
|
||||||
|
export const DEV_SEED_USER_WORKSPACE_IDS = {
|
||||||
|
NOAH: '20202020-9e3b-46d4-a556-88b9ddc2b534',
|
||||||
|
HUGO: '20202020-3957-4908-9c36-2929a23f8457',
|
||||||
|
TIM: '20202020-9e3b-46d4-a556-88b9ddc2b015',
|
||||||
|
};
|
||||||
|
|
||||||
export const seedUserWorkspaces = async (
|
export const seedUserWorkspaces = async (
|
||||||
workspaceDataSource: DataSource,
|
workspaceDataSource: DataSource,
|
||||||
schemaName: string,
|
schemaName: string,
|
||||||
@ -12,18 +18,21 @@ export const seedUserWorkspaces = async (
|
|||||||
await workspaceDataSource
|
await workspaceDataSource
|
||||||
.createQueryBuilder()
|
.createQueryBuilder()
|
||||||
.insert()
|
.insert()
|
||||||
.into(`${schemaName}.${tableName}`, ['userId', 'workspaceId'])
|
.into(`${schemaName}.${tableName}`, ['id', 'userId', 'workspaceId'])
|
||||||
.orIgnore()
|
.orIgnore()
|
||||||
.values([
|
.values([
|
||||||
{
|
{
|
||||||
|
id: DEV_SEED_USER_WORKSPACE_IDS.NOAH,
|
||||||
userId: DEMO_SEED_USER_IDS.NOAH,
|
userId: DEMO_SEED_USER_IDS.NOAH,
|
||||||
workspaceId: workspaceId,
|
workspaceId: workspaceId,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: DEV_SEED_USER_WORKSPACE_IDS.HUGO,
|
||||||
userId: DEMO_SEED_USER_IDS.HUGO,
|
userId: DEMO_SEED_USER_IDS.HUGO,
|
||||||
workspaceId: workspaceId,
|
workspaceId: workspaceId,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: DEV_SEED_USER_WORKSPACE_IDS.TIM,
|
||||||
userId: DEMO_SEED_USER_IDS.TIM,
|
userId: DEMO_SEED_USER_IDS.TIM,
|
||||||
workspaceId: workspaceId,
|
workspaceId: workspaceId,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { DataSource } from 'typeorm';
|
import { DataSource } from 'typeorm';
|
||||||
|
|
||||||
|
import { seedFeatureFlags } from 'src/database/typeorm-seeds/core/feature-flags';
|
||||||
|
import { seedUserWorkspaces } from 'src/database/typeorm-seeds/core/user-workspaces';
|
||||||
import { seedUsers } from 'src/database/typeorm-seeds/core/users';
|
import { seedUsers } from 'src/database/typeorm-seeds/core/users';
|
||||||
import { seedWorkspaces } from 'src/database/typeorm-seeds/core/workspaces';
|
import { seedWorkspaces } from 'src/database/typeorm-seeds/core/workspaces';
|
||||||
import { seedFeatureFlags } from 'src/database/typeorm-seeds/core/feature-flags';
|
|
||||||
import { seedUserWorkspaces } from 'src/database/typeorm-seeds/core/userWorkspaces';
|
|
||||||
|
|
||||||
export const seedCoreSchema = async (
|
export const seedCoreSchema = async (
|
||||||
workspaceDataSource: DataSource,
|
workspaceDataSource: DataSource,
|
||||||
|
|||||||
@ -1,32 +1,43 @@
|
|||||||
import { DataSource } from 'typeorm';
|
import { DataSource } from 'typeorm';
|
||||||
|
|
||||||
|
import { DEV_SEED_USER_IDS } from 'src/database/typeorm-seeds/core/users';
|
||||||
import {
|
import {
|
||||||
SEED_APPLE_WORKSPACE_ID,
|
|
||||||
SEED_ACME_WORKSPACE_ID,
|
SEED_ACME_WORKSPACE_ID,
|
||||||
|
SEED_APPLE_WORKSPACE_ID,
|
||||||
} from 'src/database/typeorm-seeds/core/workspaces';
|
} from 'src/database/typeorm-seeds/core/workspaces';
|
||||||
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||||
import { DEV_SEED_USER_IDS } from 'src/database/typeorm-seeds/core/users';
|
|
||||||
|
|
||||||
const tableName = 'userWorkspace';
|
const tableName = 'userWorkspace';
|
||||||
|
|
||||||
|
export const DEV_SEED_USER_WORKSPACE_IDS = {
|
||||||
|
TIM: '20202020-9e3b-46d4-a556-88b9ddc2b035',
|
||||||
|
JONY: '20202020-3957-4908-9c36-2929a23f8353',
|
||||||
|
PHIL: '20202020-7169-42cf-bc47-1cfef15264b1',
|
||||||
|
TIM_ACME: '20202020-9e3b-46d4-a556-88b9ddc2b436',
|
||||||
|
};
|
||||||
|
|
||||||
export const seedUserWorkspaces = async (
|
export const seedUserWorkspaces = async (
|
||||||
workspaceDataSource: DataSource,
|
workspaceDataSource: DataSource,
|
||||||
schemaName: string,
|
schemaName: string,
|
||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
) => {
|
) => {
|
||||||
let userWorkspaces: Pick<UserWorkspace, 'userId' | 'workspaceId'>[] = [];
|
let userWorkspaces: Pick<UserWorkspace, 'id' | 'userId' | 'workspaceId'>[] =
|
||||||
|
[];
|
||||||
|
|
||||||
if (workspaceId === SEED_APPLE_WORKSPACE_ID) {
|
if (workspaceId === SEED_APPLE_WORKSPACE_ID) {
|
||||||
userWorkspaces = [
|
userWorkspaces = [
|
||||||
{
|
{
|
||||||
|
id: DEV_SEED_USER_WORKSPACE_IDS.TIM,
|
||||||
userId: DEV_SEED_USER_IDS.TIM,
|
userId: DEV_SEED_USER_IDS.TIM,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: DEV_SEED_USER_WORKSPACE_IDS.JONY,
|
||||||
userId: DEV_SEED_USER_IDS.JONY,
|
userId: DEV_SEED_USER_IDS.JONY,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: DEV_SEED_USER_WORKSPACE_IDS.PHIL,
|
||||||
userId: DEV_SEED_USER_IDS.PHIL,
|
userId: DEV_SEED_USER_IDS.PHIL,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
},
|
},
|
||||||
@ -36,6 +47,7 @@ export const seedUserWorkspaces = async (
|
|||||||
if (workspaceId === SEED_ACME_WORKSPACE_ID) {
|
if (workspaceId === SEED_ACME_WORKSPACE_ID) {
|
||||||
userWorkspaces = [
|
userWorkspaces = [
|
||||||
{
|
{
|
||||||
|
id: DEV_SEED_USER_WORKSPACE_IDS.TIM_ACME,
|
||||||
userId: DEV_SEED_USER_IDS.TIM,
|
userId: DEV_SEED_USER_IDS.TIM,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
},
|
},
|
||||||
@ -44,7 +56,7 @@ export const seedUserWorkspaces = async (
|
|||||||
await workspaceDataSource
|
await workspaceDataSource
|
||||||
.createQueryBuilder()
|
.createQueryBuilder()
|
||||||
.insert()
|
.insert()
|
||||||
.into(`${schemaName}.${tableName}`, ['userId', 'workspaceId'])
|
.into(`${schemaName}.${tableName}`, ['id', 'userId', 'workspaceId'])
|
||||||
.orIgnore()
|
.orIgnore()
|
||||||
.values(userWorkspaces)
|
.values(userWorkspaces)
|
||||||
.execute();
|
.execute();
|
||||||
@ -108,6 +108,31 @@ export class WorkspaceManagerService {
|
|||||||
await this.prefillWorkspaceWithDemoObjects(dataSourceMetadata, workspaceId);
|
await this.prefillWorkspaceWithDemoObjects(dataSourceMetadata, workspaceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async initDev(workspaceId: string): Promise<void> {
|
||||||
|
const schemaName =
|
||||||
|
await this.workspaceDataSourceService.createWorkspaceDBSchema(
|
||||||
|
workspaceId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const dataSourceMetadata =
|
||||||
|
await this.dataSourceService.createDataSourceMetadata(
|
||||||
|
workspaceId,
|
||||||
|
schemaName,
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.workspaceSyncMetadataService.synchronize({
|
||||||
|
workspaceId: workspaceId,
|
||||||
|
dataSourceId: dataSourceMetadata.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const permissionsEnabled =
|
||||||
|
await this.permissionsService.isPermissionsEnabled();
|
||||||
|
|
||||||
|
if (permissionsEnabled === true) {
|
||||||
|
await this.initPermissions(workspaceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* We are prefilling a few standard objects with data to make it easier for the user to get started.
|
* We are prefilling a few standard objects with data to make it easier for the user to get started.
|
||||||
@ -209,30 +234,25 @@ export class WorkspaceManagerService {
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const userWorkspace = await this.userWorkspaceRepository.find({
|
const userWorkspaces = await this.userWorkspaceRepository.find({
|
||||||
where: {
|
where: {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isEmpty(userWorkspace)) {
|
if (isEmpty(userWorkspaces)) {
|
||||||
throw new PermissionsException(
|
throw new PermissionsException(
|
||||||
'User workspace not found',
|
'User workspace not found',
|
||||||
PermissionsExceptionCode.USER_WORKSPACE_NOT_FOUND,
|
PermissionsExceptionCode.USER_WORKSPACE_NOT_FOUND,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userWorkspace.length > 1) {
|
for (const userWorkspace of userWorkspaces) {
|
||||||
throw new PermissionsException(
|
await this.userRoleService.assignRoleToUserWorkspace({
|
||||||
'Multiple user workspaces found, cannot tell which one should be admin',
|
workspaceId,
|
||||||
PermissionsExceptionCode.TOO_MANY_ADMIN_CANDIDATES,
|
userWorkspaceId: userWorkspace.id,
|
||||||
);
|
roleId: adminRole.id,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.userRoleService.assignRoleToUserWorkspace({
|
|
||||||
workspaceId,
|
|
||||||
userWorkspaceId: userWorkspace[0].id,
|
|
||||||
roleId: adminRole.id,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -100,8 +100,12 @@ const StyledInput = styled.input<InputProps>`
|
|||||||
& + label:before {
|
& + label:before {
|
||||||
--size: ${({ checkboxSize }) =>
|
--size: ${({ checkboxSize }) =>
|
||||||
checkboxSize === CheckboxSize.Large ? '18px' : '12px'};
|
checkboxSize === CheckboxSize.Large ? '18px' : '12px'};
|
||||||
background: ${({ theme, indeterminate, isChecked }) =>
|
background: ${({ theme, indeterminate, isChecked, disabled }) =>
|
||||||
indeterminate || isChecked ? theme.color.blue : 'transparent'};
|
disabled && isChecked
|
||||||
|
? theme.color.blue30
|
||||||
|
: indeterminate || isChecked
|
||||||
|
? theme.color.blue
|
||||||
|
: 'transparent'};
|
||||||
border-color: ${({
|
border-color: ${({
|
||||||
theme,
|
theme,
|
||||||
indeterminate,
|
indeterminate,
|
||||||
@ -111,7 +115,7 @@ const StyledInput = styled.input<InputProps>`
|
|||||||
}) => {
|
}) => {
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case disabled:
|
case disabled:
|
||||||
return theme.background.transparent.medium;
|
return isChecked ? theme.color.blue30 : theme.font.color.extraLight;
|
||||||
case indeterminate || isChecked:
|
case indeterminate || isChecked:
|
||||||
return theme.color.blue;
|
return theme.color.blue;
|
||||||
case variant === CheckboxVariant.Primary:
|
case variant === CheckboxVariant.Primary:
|
||||||
|
|||||||
Reference in New Issue
Block a user