mirror of
https://github.com/logto-io/logto.git
synced 2025-03-31 22:51:25 -05:00
refactor(console): add empty state to transfers (#2956)
This commit is contained in:
parent
448599e8d4
commit
5f2140eb0f
26 changed files with 195 additions and 97 deletions
19
packages/console/src/components/DataEmpty/index.module.scss
Normal file
19
packages/console/src/components/DataEmpty/index.module.scss
Normal file
|
@ -0,0 +1,19 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: _.unit(4) 0;
|
||||
|
||||
.title {
|
||||
font: var(--font-subhead-2);
|
||||
margin-bottom: _.unit(2);
|
||||
}
|
||||
|
||||
.description {
|
||||
font: var(--font-body-medium);
|
||||
color: var(--color-neutral-50);
|
||||
margin-bottom: _.unit(2);
|
||||
}
|
||||
}
|
39
packages/console/src/components/DataEmpty/index.tsx
Normal file
39
packages/console/src/components/DataEmpty/index.tsx
Normal file
|
@ -0,0 +1,39 @@
|
|||
import { AppearanceMode } from '@logto/schemas';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import EmptyDark from '@/assets/images/table-empty-dark.svg';
|
||||
import Empty from '@/assets/images/table-empty.svg';
|
||||
import { useTheme } from '@/hooks/use-theme';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
export type Props = {
|
||||
title?: string;
|
||||
description?: string;
|
||||
image?: ReactNode;
|
||||
children?: ReactNode;
|
||||
imageClassName?: string;
|
||||
};
|
||||
|
||||
const DataEmpty = ({ title, description, image, imageClassName, children }: Props) => {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<div className={styles.empty}>
|
||||
{image ??
|
||||
(theme === AppearanceMode.LightMode ? (
|
||||
<Empty className={imageClassName} />
|
||||
) : (
|
||||
<EmptyDark className={imageClassName} />
|
||||
))}
|
||||
<div className={styles.title}>{title ?? t('errors.empty')}</div>
|
||||
{description && <div className={styles.description}>{description}</div>}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DataEmpty;
|
|
@ -5,3 +5,8 @@
|
|||
.icon {
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.emptyImage {
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
}
|
||||
|
|
|
@ -6,8 +6,10 @@ import { useTranslation } from 'react-i18next';
|
|||
import useSWR from 'swr';
|
||||
|
||||
import Search from '@/assets/images/search.svg';
|
||||
import DataEmpty from '@/components/DataEmpty';
|
||||
import type { DetailedResourceResponse } from '@/components/RoleScopesTransfer/types';
|
||||
import TextInput from '@/components/TextInput';
|
||||
import type { RequestError } from '@/hooks/use-api';
|
||||
import * as transferLayout from '@/scss/transfer.module.scss';
|
||||
|
||||
import ResourceItem from '../ResourceItem';
|
||||
|
@ -21,8 +23,20 @@ type Props = {
|
|||
|
||||
const SourceScopesBox = ({ roleId, selectedScopes, onChange }: Props) => {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const { data = [] } = useSWR<ResourceResponse[]>('/api/resources?includeScopes=true');
|
||||
const { data: roleScopes = [] } = useSWR<Scope[]>(roleId && `/api/roles/${roleId}/scopes`);
|
||||
|
||||
const { data: allResources, error: fetchAllResourcesError } = useSWR<
|
||||
ResourceResponse[],
|
||||
RequestError
|
||||
>('/api/resources?includeScopes=true');
|
||||
|
||||
const { data: roleScopes, error: fetchRoleScopesError } = useSWR<Scope[], RequestError>(
|
||||
roleId && `/api/roles/${roleId}/scopes`
|
||||
);
|
||||
|
||||
const isLoading =
|
||||
(!allResources && !fetchAllResourcesError) || (!roleScopes && !fetchRoleScopesError);
|
||||
|
||||
const hasError = Boolean(fetchAllResourcesError) || Boolean(fetchRoleScopesError);
|
||||
|
||||
const [keyword, setKeyword] = useState('');
|
||||
|
||||
|
@ -58,9 +72,13 @@ const SourceScopesBox = ({ roleId, selectedScopes, onChange }: Props) => {
|
|||
scopes.filter((scope) => selectedScopes.findIndex(({ id }) => id === scope.id) >= 0);
|
||||
|
||||
const resources: DetailedResourceResponse[] = useMemo(() => {
|
||||
if (!allResources || !roleScopes) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const excludeScopeIds = new Set(roleScopes.map(({ id }) => id));
|
||||
|
||||
return data
|
||||
return allResources
|
||||
.filter(({ scopes }) => scopes.some(({ id }) => !excludeScopeIds.has(id)))
|
||||
.map(({ scopes, ...resource }) => ({
|
||||
...resource,
|
||||
|
@ -71,7 +89,7 @@ const SourceScopesBox = ({ roleId, selectedScopes, onChange }: Props) => {
|
|||
resource,
|
||||
})),
|
||||
}));
|
||||
}, [data, roleScopes]);
|
||||
}, [allResources, roleScopes]);
|
||||
|
||||
const dataSource = useMemo(() => {
|
||||
const lowerCasedKeyword = keyword.toLowerCase();
|
||||
|
@ -110,15 +128,22 @@ const SourceScopesBox = ({ roleId, selectedScopes, onChange }: Props) => {
|
|||
/>
|
||||
</div>
|
||||
<div className={transferLayout.boxContent}>
|
||||
{dataSource.map((resource) => (
|
||||
<ResourceItem
|
||||
key={resource.id}
|
||||
resource={resource}
|
||||
selectedScopes={getResourceSelectedScopes(resource)}
|
||||
onSelectResource={onSelectResource}
|
||||
onSelectScope={onSelectScope}
|
||||
{!isLoading && !hasError && dataSource.length === 0 && (
|
||||
<DataEmpty
|
||||
imageClassName={styles.emptyImage}
|
||||
title={t('role_details.permission.empty')}
|
||||
/>
|
||||
))}
|
||||
)}
|
||||
{dataSource.length > 0 &&
|
||||
dataSource.map((resource) => (
|
||||
<ResourceItem
|
||||
key={resource.id}
|
||||
resource={resource}
|
||||
selectedScopes={getResourceSelectedScopes(resource)}
|
||||
onSelectResource={onSelectResource}
|
||||
onSelectScope={onSelectScope}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -5,3 +5,8 @@
|
|||
.icon {
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.emptyImage {
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next';
|
|||
import useSWR from 'swr';
|
||||
|
||||
import Search from '@/assets/images/search.svg';
|
||||
import DataEmpty from '@/components/DataEmpty';
|
||||
import Pagination from '@/components/Pagination';
|
||||
import TextInput from '@/components/TextInput';
|
||||
import { defaultPageSize } from '@/consts';
|
||||
|
@ -40,7 +41,9 @@ const SourceUsersBox = ({ roleId, selectedUsers, onChange }: Props) => {
|
|||
...conditional(keyword && { search: formatKeyword(keyword) }),
|
||||
});
|
||||
|
||||
const { data } = useSWR<[User[], number], RequestError>(url);
|
||||
const { data, error } = useSWR<[User[], number], RequestError>(url);
|
||||
|
||||
const isLoading = !data && !error;
|
||||
|
||||
const [dataSource = [], totalCount] = data ?? [];
|
||||
|
||||
|
@ -64,24 +67,28 @@ const SourceUsersBox = ({ roleId, selectedUsers, onChange }: Props) => {
|
|||
/>
|
||||
</div>
|
||||
<div className={transferLayout.boxContent}>
|
||||
{dataSource.map((user) => {
|
||||
const isSelected = isUserAdded(user);
|
||||
{!isLoading && !error && dataSource.length === 0 && (
|
||||
<DataEmpty imageClassName={styles.emptyImage} title={t('role_details.users.empty')} />
|
||||
)}
|
||||
{dataSource.length > 0 &&
|
||||
dataSource.map((user) => {
|
||||
const isSelected = isUserAdded(user);
|
||||
|
||||
return (
|
||||
<SourceUserItem
|
||||
key={user.id}
|
||||
user={user}
|
||||
isSelected={isSelected}
|
||||
onSelect={() => {
|
||||
onChange(
|
||||
isSelected
|
||||
? selectedUsers.filter(({ id }) => user.id !== id)
|
||||
: [user, ...selectedUsers]
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
return (
|
||||
<SourceUserItem
|
||||
key={user.id}
|
||||
user={user}
|
||||
isSelected={isSelected}
|
||||
onSelect={() => {
|
||||
onChange(
|
||||
isSelected
|
||||
? selectedUsers.filter(({ id }) => user.id !== id)
|
||||
: [user, ...selectedUsers]
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<Pagination
|
||||
mode="pico"
|
||||
|
|
|
@ -1,23 +1,3 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.tableEmptyTableData {
|
||||
border-bottom: unset;
|
||||
}
|
||||
|
||||
.tableEmpty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: _.unit(4) 0;
|
||||
|
||||
.title {
|
||||
font: var(--font-subhead-2);
|
||||
margin-bottom: _.unit(2);
|
||||
}
|
||||
|
||||
.description {
|
||||
font: var(--font-body-medium);
|
||||
color: var(--color-neutral-50);
|
||||
margin-bottom: _.unit(2);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,37 +1,18 @@
|
|||
import { AppearanceMode } from '@logto/schemas';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import EmptyDark from '@/assets/images/table-empty-dark.svg';
|
||||
import Empty from '@/assets/images/table-empty.svg';
|
||||
import { useTheme } from '@/hooks/use-theme';
|
||||
import DataEmpty from '@/components/DataEmpty';
|
||||
import type { Props as DataEmptyProps } from '@/components/DataEmpty';
|
||||
|
||||
import * as styles from './TableEmpty.module.scss';
|
||||
|
||||
type Props = {
|
||||
title?: string;
|
||||
description?: string;
|
||||
image?: ReactNode;
|
||||
children?: ReactNode;
|
||||
type Props = DataEmptyProps & {
|
||||
columns: number;
|
||||
};
|
||||
|
||||
const TableEmpty = ({ title, description, image, children, columns }: Props) => {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<td colSpan={columns} className={styles.tableEmptyTableData}>
|
||||
<div className={styles.tableEmpty}>
|
||||
{image ?? (theme === AppearanceMode.LightMode ? <Empty /> : <EmptyDark />)}
|
||||
<div className={styles.title}>{title ?? t('errors.empty')}</div>
|
||||
{description && <div className={styles.description}>{description}</div>}
|
||||
{children}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
const TableEmpty = ({ columns, ...emptyProps }: Props) => (
|
||||
<tr>
|
||||
<td colSpan={columns} className={styles.tableEmptyTableData}>
|
||||
<DataEmpty {...emptyProps} />
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
export default TableEmpty;
|
||||
|
|
|
@ -5,3 +5,8 @@
|
|||
.icon {
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.emptyImage {
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
}
|
||||
|
|
|
@ -6,8 +6,10 @@ import { useTranslation } from 'react-i18next';
|
|||
import useSWR from 'swr';
|
||||
|
||||
import Search from '@/assets/images/search.svg';
|
||||
import DataEmpty from '@/components/DataEmpty';
|
||||
import Pagination from '@/components/Pagination';
|
||||
import TextInput from '@/components/TextInput';
|
||||
import type { RequestError } from '@/hooks/use-api';
|
||||
import useDebounce from '@/hooks/use-debounce';
|
||||
import * as transferLayout from '@/scss/transfer.module.scss';
|
||||
import { buildUrl } from '@/utilities/url';
|
||||
|
@ -39,7 +41,9 @@ const SourceRolesBox = ({ userId, selectedRoles, onChange }: Props) => {
|
|||
...conditional(keyword && { search: `%${keyword}%` }),
|
||||
});
|
||||
|
||||
const { data } = useSWR<[RoleResponse[], number]>(url);
|
||||
const { data, error } = useSWR<[RoleResponse[], number], RequestError>(url);
|
||||
|
||||
const isLoading = !data && !error;
|
||||
|
||||
const [dataSource = [], totalCount] = data ?? [];
|
||||
|
||||
|
@ -64,24 +68,28 @@ const SourceRolesBox = ({ userId, selectedRoles, onChange }: Props) => {
|
|||
/>
|
||||
</div>
|
||||
<div className={transferLayout.boxContent}>
|
||||
{dataSource.map((role) => {
|
||||
const isSelected = isRoleSelected(role);
|
||||
{!isLoading && !error && dataSource.length === 0 && (
|
||||
<DataEmpty imageClassName={styles.emptyImage} title={t('user_details.roles.empty')} />
|
||||
)}
|
||||
{dataSource.length > 0 &&
|
||||
dataSource.map((role) => {
|
||||
const isSelected = isRoleSelected(role);
|
||||
|
||||
return (
|
||||
<SourceRoleItem
|
||||
key={role.id}
|
||||
role={role}
|
||||
isSelected={isSelected}
|
||||
onSelect={() => {
|
||||
onChange(
|
||||
isSelected
|
||||
? selectedRoles.filter(({ id }) => id !== role.id)
|
||||
: [role, ...selectedRoles]
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
return (
|
||||
<SourceRoleItem
|
||||
key={role.id}
|
||||
role={role}
|
||||
isSelected={isSelected}
|
||||
onSelect={() => {
|
||||
onChange(
|
||||
isSelected
|
||||
? selectedRoles.filter(({ id }) => id !== role.id)
|
||||
: [role, ...selectedRoles]
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<Pagination
|
||||
mode="pico"
|
||||
|
|
|
@ -25,6 +25,7 @@ const role_details = {
|
|||
deletion_description:
|
||||
'If this permission is deleted, the affected user with this role will lose the access granted by this permission.', // UNTRANSLATED
|
||||
permission_deleted: 'The permission "{{name}}" was successfully removed from this role!', // UNTRANSLATED
|
||||
empty: 'No permission available', // UNTRANSLATED
|
||||
},
|
||||
users: {
|
||||
assign_button: 'Assign Users', // UNTRANSLATED
|
||||
|
@ -40,6 +41,7 @@ const role_details = {
|
|||
assign_users_field: 'Assign users', // UNTRANSLATED
|
||||
confirm_assign: 'Assign users', // UNTRANSLATED
|
||||
users_assigned: 'The selected users were successfully assigned to this role!', // UNTRANSLATED
|
||||
empty: 'No user available', // UNTRANSLATED
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ const user_details = {
|
|||
confirm_assign: 'Assign roles', // UNTRANSLATED
|
||||
role_assigned: 'Successfully assigned role(s)', // UNTRANSLATED
|
||||
search: 'Search by role name, description or ID', // UNTRANSLATED
|
||||
empty: 'No role available', // UNTRANSLATED
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ const role_details = {
|
|||
deletion_description:
|
||||
'If this permission is deleted, the affected user with this role will lose the access granted by this permission.',
|
||||
permission_deleted: 'The permission "{{name}}" was successfully removed from this role!',
|
||||
empty: 'No permission available',
|
||||
},
|
||||
users: {
|
||||
assign_button: 'Assign Users',
|
||||
|
@ -40,6 +41,7 @@ const role_details = {
|
|||
assign_users_field: 'Assign users',
|
||||
confirm_assign: 'Assign users',
|
||||
users_assigned: 'The selected users were successfully assigned to this role!',
|
||||
empty: 'No user available',
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ const user_details = {
|
|||
confirm_assign: 'Assign roles',
|
||||
role_assigned: 'Successfully assigned role(s)',
|
||||
search: 'Search by role name, description or ID',
|
||||
empty: 'No role available',
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ const role_details = {
|
|||
deletion_description:
|
||||
'If this permission is deleted, the affected user with this role will lose the access granted by this permission.', // UNTRANSLATED
|
||||
permission_deleted: 'The permission "{{name}}" was successfully removed from this role!', // UNTRANSLATED
|
||||
empty: 'No permission available', // UNTRANSLATED
|
||||
},
|
||||
users: {
|
||||
assign_button: 'Assign Users', // UNTRANSLATED
|
||||
|
@ -40,6 +41,7 @@ const role_details = {
|
|||
assign_users_field: 'Assign users', // UNTRANSLATED
|
||||
confirm_assign: 'Assign users', // UNTRANSLATED
|
||||
users_assigned: 'The selected users were successfully assigned to this role!', // UNTRANSLATED
|
||||
empty: 'No user available', // UNTRANSLATED
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ const user_details = {
|
|||
confirm_assign: 'Assign roles', // UNTRANSLATED
|
||||
role_assigned: 'Successfully assigned role(s)', // UNTRANSLATED
|
||||
search: 'Search by role name, description or ID', // UNTRANSLATED
|
||||
empty: 'No role available', // UNTRANSLATED
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ const role_details = {
|
|||
deletion_description:
|
||||
'If this permission is deleted, the affected user with this role will lose the access granted by this permission.', // UNTRANSLATED
|
||||
permission_deleted: 'The permission "{{name}}" was successfully removed from this role!', // UNTRANSLATED
|
||||
empty: 'No permission available', // UNTRANSLATED
|
||||
},
|
||||
users: {
|
||||
assign_button: 'Assign Users', // UNTRANSLATED
|
||||
|
@ -40,6 +41,7 @@ const role_details = {
|
|||
assign_users_field: 'Assign users', // UNTRANSLATED
|
||||
confirm_assign: 'Assign users', // UNTRANSLATED
|
||||
users_assigned: 'The selected users were successfully assigned to this role!', // UNTRANSLATED
|
||||
empty: 'No user available', // UNTRANSLATED
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ const user_details = {
|
|||
confirm_assign: 'Assign roles', // UNTRANSLATED
|
||||
role_assigned: 'Successfully assigned role(s)', // UNTRANSLATED
|
||||
search: 'Search by role name, description or ID', // UNTRANSLATED
|
||||
empty: 'No role available', // UNTRANSLATED
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ const role_details = {
|
|||
deletion_description:
|
||||
'If this permission is deleted, the affected user with this role will lose the access granted by this permission.', // UNTRANSLATED
|
||||
permission_deleted: 'The permission "{{name}}" was successfully removed from this role!', // UNTRANSLATED
|
||||
empty: 'No permission available', // UNTRANSLATED
|
||||
},
|
||||
users: {
|
||||
assign_button: 'Assign Users', // UNTRANSLATED
|
||||
|
@ -40,6 +41,7 @@ const role_details = {
|
|||
assign_users_field: 'Assign users', // UNTRANSLATED
|
||||
confirm_assign: 'Assign users', // UNTRANSLATED
|
||||
users_assigned: 'The selected users were successfully assigned to this role!', // UNTRANSLATED
|
||||
empty: 'No user available', // UNTRANSLATED
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ const user_details = {
|
|||
confirm_assign: 'Assign roles', // UNTRANSLATED
|
||||
role_assigned: 'Successfully assigned role(s)', // UNTRANSLATED
|
||||
search: 'Search by role name, description or ID', // UNTRANSLATED
|
||||
empty: 'No role available', // UNTRANSLATED
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ const role_details = {
|
|||
deletion_description:
|
||||
'If this permission is deleted, the affected user with this role will lose the access granted by this permission.', // UNTRANSLATED
|
||||
permission_deleted: 'The permission "{{name}}" was successfully removed from this role!', // UNTRANSLATED
|
||||
empty: 'No permission available', // UNTRANSLATED
|
||||
},
|
||||
users: {
|
||||
assign_button: 'Assign Users', // UNTRANSLATED
|
||||
|
@ -40,6 +41,7 @@ const role_details = {
|
|||
assign_users_field: 'Assign users', // UNTRANSLATED
|
||||
confirm_assign: 'Assign users', // UNTRANSLATED
|
||||
users_assigned: 'The selected users were successfully assigned to this role!', // UNTRANSLATED
|
||||
empty: 'No user available', // UNTRANSLATED
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ const user_details = {
|
|||
confirm_assign: 'Assign roles', // UNTRANSLATED
|
||||
role_assigned: 'Successfully assigned role(s)', // UNTRANSLATED
|
||||
search: 'Search by role name, description or ID', // UNTRANSLATED
|
||||
empty: 'No role available', // UNTRANSLATED
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ const role_details = {
|
|||
deletion_description:
|
||||
'If this permission is deleted, the affected user with this role will lose the access granted by this permission.', // UNTRANSLATED
|
||||
permission_deleted: 'The permission "{{name}}" was successfully removed from this role!', // UNTRANSLATED
|
||||
empty: 'No permission available', // UNTRANSLATED
|
||||
},
|
||||
users: {
|
||||
assign_button: 'Assign Users', // UNTRANSLATED
|
||||
|
@ -40,6 +41,7 @@ const role_details = {
|
|||
assign_users_field: 'Assign users', // UNTRANSLATED
|
||||
confirm_assign: 'Assign users', // UNTRANSLATED
|
||||
users_assigned: 'The selected users were successfully assigned to this role!', // UNTRANSLATED
|
||||
empty: 'No user available', // UNTRANSLATED
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ const user_details = {
|
|||
confirm_assign: 'Assign roles', // UNTRANSLATED
|
||||
role_assigned: 'Successfully assigned role(s)', // UNTRANSLATED
|
||||
search: 'Search by role name, description or ID', // UNTRANSLATED
|
||||
empty: 'No role available', // UNTRANSLATED
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ const role_details = {
|
|||
deletion_description:
|
||||
'If this permission is deleted, the affected user with this role will lose the access granted by this permission.', // UNTRANSLATED
|
||||
permission_deleted: 'The permission "{{name}}" was successfully removed from this role!', // UNTRANSLATED
|
||||
empty: 'No permission available', // UNTRANSLATED
|
||||
},
|
||||
users: {
|
||||
assign_button: 'Assign Users', // UNTRANSLATED
|
||||
|
@ -40,6 +41,7 @@ const role_details = {
|
|||
assign_users_field: 'Assign users', // UNTRANSLATED
|
||||
confirm_assign: 'Assign users', // UNTRANSLATED
|
||||
users_assigned: 'The selected users were successfully assigned to this role!', // UNTRANSLATED
|
||||
empty: 'No user available', // UNTRANSLATED
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -54,6 +54,7 @@ const user_details = {
|
|||
confirm_assign: 'Assign roles', // UNTRANSLATED
|
||||
role_assigned: 'Successfully assigned role(s)', // UNTRANSLATED
|
||||
search: 'Search by role name, description or ID', // UNTRANSLATED
|
||||
empty: 'No role available', // UNTRANSLATED
|
||||
},
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue