From be1b877868bb11bd231a3a57339678f867eb7052 Mon Sep 17 00:00:00 2001 From: Weiko Date: Tue, 18 Mar 2025 14:18:33 +0100 Subject: [PATCH] Add empty states to settings tables (#10978) ## Context Fixes https://github.com/twentyhq/twenty/issues/10964 ## Test Screenshot 2025-03-18 at 12 18 30 Screenshot 2025-03-18 at 12 18 25 Screenshot 2025-03-18 at 12 18 19 Screenshot 2025-03-18 at 12 18 07 Screenshot 2025-03-18 at 12 18 01 --- .../components/RoleAssignment.tsx | 38 +-- .../components/RoleAssignmentTableHeader.tsx | 11 +- .../components/RolePermissions.tsx | 32 ++- .../RolePermissionsObjectsTableHeader.tsx | 21 +- .../RolePermissionsSettingsTableHeader.tsx | 23 +- .../settings/SettingsWorkspaceMembers.tsx | 245 +++++++++++------- 6 files changed, 210 insertions(+), 160 deletions(-) diff --git a/packages/twenty-front/src/modules/settings/roles/role-assignment/components/RoleAssignment.tsx b/packages/twenty-front/src/modules/settings/roles/role-assignment/components/RoleAssignment.tsx index 578e8e61d..287d8e58d 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-assignment/components/RoleAssignment.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-assignment/components/RoleAssignment.tsx @@ -7,6 +7,7 @@ import { SettingsPath } from '@/types/SettingsPath'; import { TextInput } from '@/ui/input/components/TextInput'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; +import { TableCell } from '@/ui/layout/table/components/TableCell'; import styled from '@emotion/styled'; import { t } from '@lingui/core/macro'; import { useState } from 'react'; @@ -41,19 +42,14 @@ const StyledSearchContainer = styled.div` padding-bottom: ${({ theme }) => theme.spacing(2)}; `; -const StyledTable = styled.div<{ hasRows: boolean }>` - border-bottom: ${({ hasRows, theme }) => - hasRows ? `1px solid ${theme.border.color.light}` : 'none'}; +const StyledTable = styled.div` + border-bottom: 1px solid ${({ theme }) => theme.border.color.light}; `; const StyledSearchInput = styled(TextInput)` input { background: ${({ theme }) => theme.background.transparent.lighter}; border: 1px solid ${({ theme }) => theme.border.color.medium}; - - &:hover { - border: 1px solid ${({ theme }) => theme.border.color.medium}; - } } `; @@ -63,6 +59,10 @@ const StyledTableRows = styled.div` padding-top: ${({ theme }) => theme.spacing(2)}; `; +const StyledNoMembers = styled(TableCell)` + color: ${({ theme }) => theme.font.color.tertiary}; +`; + type RoleAssignmentProps = { role: Pick & { workspaceMembers: Array; @@ -175,21 +175,29 @@ export const RoleAssignment = ({ role }: RoleAssignmentProps) => { - 0}> + - {filteredWorkspaceMembers.map((workspaceMember) => ( - - ))} + {filteredWorkspaceMembers.length > 0 ? ( + filteredWorkspaceMembers.map((workspaceMember) => ( + + )) + ) : ( + + {!searchFilter + ? t`No members assigned` + : t`No members match your search`} + + )} diff --git a/packages/twenty-front/src/modules/settings/roles/role-assignment/components/RoleAssignmentTableHeader.tsx b/packages/twenty-front/src/modules/settings/roles/role-assignment/components/RoleAssignmentTableHeader.tsx index 018241083..21c2b7129 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-assignment/components/RoleAssignmentTableHeader.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-assignment/components/RoleAssignmentTableHeader.tsx @@ -1,13 +1,10 @@ -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 { t } from '@lingui/core/macro'; export const RoleAssignmentTableHeader = () => ( - - - {t`Name`} - {t`Email`} - -
+ + {t`Name`} + {t`Email`} + ); diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/components/RolePermissions.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/components/RolePermissions.tsx index d0f01d4bc..10c63a14e 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/components/RolePermissions.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/components/RolePermissions.tsx @@ -32,7 +32,11 @@ const StyledRolePermissionsContainer = styled.div` const StyledTable = styled.div` border-bottom: 1px solid ${({ theme }) => theme.border.color.light}; +`; + +const StyledTableRows = styled.div` padding-bottom: ${({ theme }) => theme.spacing(2)}; + padding-top: ${({ theme }) => theme.spacing(2)}; `; type RolePermissionsProps = { @@ -136,12 +140,14 @@ export const RolePermissions = ({ role }: RolePermissionsProps) => { /> - {objectPermissionsConfig.map((permission) => ( - - ))} + + {objectPermissionsConfig.map((permission) => ( + + ))} +
@@ -150,12 +156,14 @@ export const RolePermissions = ({ role }: RolePermissionsProps) => { - {settingsPermissionsConfig.map((permission) => ( - - ))} + + {settingsPermissionsConfig.map((permission) => ( + + ))} +
diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/components/RolePermissionsObjectsTableHeader.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/components/RolePermissionsObjectsTableHeader.tsx index 254bdb26f..018a528f2 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/components/RolePermissionsObjectsTableHeader.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/components/RolePermissionsObjectsTableHeader.tsx @@ -1,4 +1,3 @@ -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'; @@ -20,25 +19,17 @@ const StyledActionsHeader = styled(TableHeader)` 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) => ( - - - {t`Name`} - - - - - + + {t`Name`} + + + + ); diff --git a/packages/twenty-front/src/modules/settings/roles/role-permissions/components/RolePermissionsSettingsTableHeader.tsx b/packages/twenty-front/src/modules/settings/roles/role-permissions/components/RolePermissionsSettingsTableHeader.tsx index 6e950d8bd..a72f378cb 100644 --- a/packages/twenty-front/src/modules/settings/roles/role-permissions/components/RolePermissionsSettingsTableHeader.tsx +++ b/packages/twenty-front/src/modules/settings/roles/role-permissions/components/RolePermissionsSettingsTableHeader.tsx @@ -1,4 +1,3 @@ -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'; @@ -23,30 +22,22 @@ const StyledActionsHeader = styled(TableHeader)` 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) => ( - - - {t`Name`} - {t`Type`} - - - - - + + {t`Name`} + {t`Type`} + + + + ); diff --git a/packages/twenty-front/src/pages/settings/SettingsWorkspaceMembers.tsx b/packages/twenty-front/src/pages/settings/SettingsWorkspaceMembers.tsx index 9a6641b1b..68639addd 100644 --- a/packages/twenty-front/src/pages/settings/SettingsWorkspaceMembers.tsx +++ b/packages/twenty-front/src/pages/settings/SettingsWorkspaceMembers.tsx @@ -11,6 +11,7 @@ import { IconButton, IconMail, IconReload, + IconSearch, IconTrash, Section, Status, @@ -26,6 +27,7 @@ import { SettingsPageContainer } from '@/settings/components/SettingsPageContain import { SettingsPath } from '@/types/SettingsPath'; import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; +import { TextInput } from '@/ui/input/components/TextInput'; import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal'; import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; import { Table } from '@/ui/layout/table/components/Table'; @@ -51,11 +53,7 @@ const StyledButtonContainer = styled.div` `; const StyledTable = styled(Table)` - margin-top: ${({ theme }) => theme.spacing(0.5)}; -`; - -const StyledTableHeaderRow = styled(Table)` - margin-bottom: ${({ theme }) => theme.spacing(1.5)}; + border-bottom: 1px solid ${({ theme }) => theme.border.color.light}; `; const StyledIconWrapper = styled.div` @@ -70,6 +68,26 @@ const StyledTextContainerWithEllipsis = styled.div` white-space: nowrap; `; +const StyledSearchContainer = styled.div` + padding-bottom: ${({ theme }) => theme.spacing(2)}; +`; + +const StyledSearchInput = styled(TextInput)` + input { + background: ${({ theme }) => theme.background.transparent.lighter}; + border: 1px solid ${({ theme }) => theme.border.color.medium}; + } +`; + +const StyledTableRows = styled.div` + padding-bottom: ${({ theme }) => theme.spacing(2)}; + padding-top: ${({ theme }) => theme.spacing(2)}; +`; + +const StyledNoMembers = styled(TableCell)` + color: ${({ theme }) => theme.font.color.tertiary}; +`; + export const SettingsWorkspaceMembers = () => { const { t } = useLingui(); const { enqueueSnackBar } = useSnackBar(); @@ -100,6 +118,12 @@ export const SettingsWorkspaceMembers = () => { const workspaceInvitations = useRecoilValue(workspaceInvitationsState); const setWorkspaceInvitations = useSetRecoilState(workspaceInvitationsState); + const [searchFilter, setSearchFilter] = useState(''); + + const handleSearchChange = (text: string) => { + setSearchFilter(text); + }; + useGetWorkspaceInvitationsQuery({ onError: (error: Error) => { enqueueSnackBar(error.message, { @@ -138,6 +162,21 @@ export const SettingsWorkspaceMembers = () => { : formatDistanceToNow(new Date(expiresAt)); }; + const filteredWorkspaceMembers = !searchFilter + ? workspaceMembers + : workspaceMembers.filter((member) => { + const searchTerm = searchFilter.toLowerCase(); + const firstName = member.name.firstName?.toLowerCase() || ''; + const lastName = member.name.lastName?.toLowerCase() || ''; + const email = member.userEmail?.toLowerCase() || ''; + + return ( + firstName.includes(searchTerm) || + lastName.includes(searchTerm) || + email.includes(searchTerm) + ); + }); + return ( { title={t`Manage Members`} description={t`Manage the members of your space here`} /> - - - - - Name - - - Email - - - - - {workspaceMembers?.map((workspaceMember) => ( - - - - - - - - {workspaceMember.name.firstName + - ' ' + - workspaceMember.name.lastName} - - - - - - {workspaceMember.userEmail} - - - - {currentWorkspaceMember?.id !== workspaceMember.id && ( - - { - setIsConfirmationModalOpen(true); - setWorkspaceMemberToDelete(workspaceMember.id); - }} - variant="tertiary" - size="medium" - Icon={IconTrash} + + + + + + + Name + + + Email + + + + + {filteredWorkspaceMembers.length > 0 ? ( + filteredWorkspaceMembers.map((workspaceMember) => ( + + + + - - )} - - - - ))} -
+ + + {workspaceMember.name.firstName + + ' ' + + workspaceMember.name.lastName} + + + + + + {workspaceMember.userEmail} + + + + {currentWorkspaceMember?.id !== workspaceMember.id && ( + + { + setIsConfirmationModalOpen(true); + setWorkspaceMemberToDelete(workspaceMember.id); + }} + variant="tertiary" + size="medium" + Icon={IconTrash} + /> + + )} + + + )) + ) : ( + + {!searchFilter + ? t`No members` + : t`No members match your search`} + + )} + +
{ /> {isNonEmptyArray(workspaceInvitations) && ( - - - - - Email - - - Expires in - - - - - {workspaceInvitations?.map((workspaceInvitation) => ( - + + + + Email + + + Expires in + + + + + {workspaceInvitations?.map((workspaceInvitation) => ( @@ -309,9 +364,9 @@ export const SettingsWorkspaceMembers = () => { - - ))} -
+ ))} + + )}