mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
feat(core-blocks): CB-1318 add "select all" logic to table
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { observable, computed } from 'mobx';
|
||||
import { observable, computed, action } from 'mobx';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useState, useCallback, useMemo } from 'react';
|
||||
import styled, { use } from 'reshadow';
|
||||
@@ -14,23 +14,59 @@ import styled, { use } from 'reshadow';
|
||||
import { useStyles } from '@cloudbeaver/core-theming';
|
||||
|
||||
import { useObjectRef } from '../useObjectRef';
|
||||
import { TableContext, ITableContext } from './TableContext';
|
||||
import { useObservableRef } from '../useObservableRef';
|
||||
import { TableContext, ITableContext, ITableState } from './TableContext';
|
||||
|
||||
interface Props {
|
||||
keys?: any[];
|
||||
selectedItems?: Map<any, boolean>;
|
||||
expandedItems?: Map<any, boolean>;
|
||||
size?: 'big';
|
||||
className?: string;
|
||||
onSelect?: (item: any, state: boolean) => void;
|
||||
isItemSelectable?: (item: any) => boolean;
|
||||
}
|
||||
|
||||
export const Table = observer<Props>(function Table({
|
||||
selectedItems, expandedItems, className, size, children, onSelect,
|
||||
keys, isItemSelectable, selectedItems, expandedItems, className, size, children, onSelect,
|
||||
}) {
|
||||
const props = useObjectRef({ onSelect });
|
||||
|
||||
const [selected] = useState<Map<any, boolean>>(() => selectedItems || observable(new Map()));
|
||||
const [expanded] = useState<Map<any, boolean>>(() => expandedItems || observable(new Map()));
|
||||
|
||||
const state: ITableState = useObservableRef(() => ({
|
||||
get selectableItems() {
|
||||
if (!this.keys) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!this.isItemSelectable) {
|
||||
return this.keys;
|
||||
}
|
||||
|
||||
return this.keys.filter(this.isItemSelectable);
|
||||
},
|
||||
get tableSelected() {
|
||||
return this.selectableItems.length > 0 && this.selectableItems.every(item => selected.get(item));
|
||||
},
|
||||
selectTable() {
|
||||
const tableSelected = this.tableSelected;
|
||||
for (const item of this.selectableItems) {
|
||||
selected.set(item, !tableSelected);
|
||||
}
|
||||
},
|
||||
|
||||
}), {
|
||||
keys: observable.ref,
|
||||
isItemSelectable: observable.ref,
|
||||
selectableItems: computed,
|
||||
tableSelected: computed,
|
||||
selectTable: action.bound,
|
||||
}, { keys, isItemSelectable });
|
||||
|
||||
const isExpanded = useMemo(() => computed(() => Array.from(expanded.values()).some(Boolean)), [expanded]);
|
||||
|
||||
const setItemSelect = useCallback((item: any, state: boolean) => {
|
||||
selected.set(item, state);
|
||||
if (props.onSelect) {
|
||||
@@ -40,11 +76,12 @@ export const Table = observer<Props>(function Table({
|
||||
const setItemExpand = useCallback((item: any, state: boolean) => expanded.set(item, state), []);
|
||||
const clearSelection = useCallback(() => selected.clear(), []);
|
||||
const collapse = useCallback(() => expanded.clear(), []);
|
||||
const isExpanded = useMemo(() => computed(() => Array.from(expanded.values()).some(Boolean)), [expanded]);
|
||||
|
||||
const [context] = useState<ITableContext>(() => ({
|
||||
state,
|
||||
selectedItems: selected,
|
||||
expandedItems: expanded,
|
||||
isItemSelectable,
|
||||
setItemExpand,
|
||||
setItemSelect,
|
||||
clearSelection,
|
||||
|
||||
@@ -8,7 +8,16 @@
|
||||
|
||||
import { createContext } from 'react';
|
||||
|
||||
export interface ITableState {
|
||||
keys?: any[];
|
||||
readonly selectableItems: any[];
|
||||
readonly tableSelected: boolean;
|
||||
selectTable: () => void;
|
||||
isItemSelectable?: (item: any) => boolean;
|
||||
}
|
||||
|
||||
export interface ITableContext {
|
||||
state: ITableState;
|
||||
selectedItems: Map<any, boolean>;
|
||||
expandedItems: Map<any, boolean>;
|
||||
setItemSelect: (item: any, state: boolean) => void;
|
||||
|
||||
@@ -30,6 +30,7 @@ interface Props {
|
||||
selectOnItem?: boolean;
|
||||
selectDisabled?: boolean;
|
||||
disabled?: boolean;
|
||||
title?: string;
|
||||
className?: string;
|
||||
onClick?: () => void;
|
||||
onDoubleClick?: () => void;
|
||||
@@ -42,6 +43,7 @@ export const TableItem = observer<Props>(function TableItem({
|
||||
selectDisabled = false,
|
||||
disabled,
|
||||
children,
|
||||
title,
|
||||
className,
|
||||
onClick,
|
||||
onDoubleClick,
|
||||
@@ -91,6 +93,7 @@ export const TableItem = observer<Props>(function TableItem({
|
||||
return styled(styles)(
|
||||
<TableItemContext.Provider value={itemContext}>
|
||||
<tr
|
||||
title={title}
|
||||
className={className}
|
||||
onClick={handleClick}
|
||||
onDoubleClick={handleDoubleClick}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { useTranslate } from '@cloudbeaver/core-localization';
|
||||
|
||||
import { Checkbox } from '../FormControls/Checkboxes/Checkbox';
|
||||
import { TableContext } from './TableContext';
|
||||
|
||||
interface Props {
|
||||
id: string;
|
||||
disabled?: boolean;
|
||||
tooltip?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const TableSelect = observer<Props>(function TableSelect({ id, disabled, tooltip, className }) {
|
||||
const tableContext = useContext(TableContext);
|
||||
const translate = useTranslate();
|
||||
|
||||
if (!tableContext) {
|
||||
throw new Error('Context must be provided');
|
||||
}
|
||||
|
||||
return (
|
||||
<Checkbox
|
||||
id={id}
|
||||
className={className}
|
||||
title={tooltip || translate('ui_select_all')}
|
||||
disabled={disabled || !tableContext.state.selectableItems.length}
|
||||
checked={tableContext.state.tableSelected}
|
||||
onClick={tableContext.state.selectTable}
|
||||
/>
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
export function getSelectedItems(items: Map<any, boolean>): any[] {
|
||||
const result: any[] = [];
|
||||
|
||||
for (const [key, value] of items) {
|
||||
if (value) {
|
||||
result.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -53,6 +53,8 @@ export * from './Table/TableItemSelect';
|
||||
export * from './Table/TableItemSeparator';
|
||||
export * from './Table/useTable';
|
||||
export * from './Table/TableState';
|
||||
export * from './Table/TableSelect';
|
||||
export * from './Table/getSelectedItems';
|
||||
|
||||
export * from './Tabs/ITab';
|
||||
export * from './Tabs/TabContext';
|
||||
|
||||
+19
-20
@@ -6,9 +6,9 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { computed, observable } from 'mobx';
|
||||
import { observable } from 'mobx';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import type { RoleInfo } from '@cloudbeaver/core-authentication';
|
||||
@@ -20,7 +20,9 @@ import {
|
||||
BASE_CONTAINERS_STYLES,
|
||||
Group,
|
||||
Button,
|
||||
useObjectRef
|
||||
useObjectRef,
|
||||
getComputed,
|
||||
getSelectedItems
|
||||
} from '@cloudbeaver/core-blocks';
|
||||
import { TLocalizationToken, useTranslate } from '@cloudbeaver/core-localization';
|
||||
import type { AdminUserInfoFragment } from '@cloudbeaver/core-sdk';
|
||||
@@ -78,25 +80,20 @@ export const ConnectionAccessGrantedList = observer<Props>(function ConnectionAc
|
||||
const translate = useTranslate();
|
||||
const [selectedSubjects] = useState<Map<any, boolean>>(() => observable(new Map()));
|
||||
const [filterState] = useState<IFilterState>(() => observable({ filterValue: '' }));
|
||||
const selectedList = useMemo(() => computed(
|
||||
() => Array.from(selectedSubjects.entries()).filter(([key, value]) => value).map(([key]) => key)
|
||||
), [selectedSubjects]);
|
||||
|
||||
const selected = getComputed(() => Array.from(selectedSubjects.values()).some(v => v));
|
||||
|
||||
const revoke = useCallback(() => {
|
||||
props.onRevoke(selectedList.get());
|
||||
props.onRevoke(getSelectedItems(selectedSubjects));
|
||||
selectedSubjects.clear();
|
||||
}, []);
|
||||
|
||||
const roles = useMemo(() => computed(() => getFilteredRoles(
|
||||
grantedRoles, filterState.filterValue
|
||||
)), [filterState, grantedRoles]);
|
||||
|
||||
const users = useMemo(() => computed(() => getFilteredUsers(
|
||||
grantedUsers, filterState.filterValue
|
||||
)), [filterState, grantedUsers]);
|
||||
const roles = getFilteredRoles(grantedRoles, filterState.filterValue);
|
||||
const users = getFilteredUsers(grantedUsers, filterState.filterValue);
|
||||
const keys = roles.map(role => role.roleId).concat(users.map(user => user.userId));
|
||||
|
||||
let tableInfoText: TLocalizationToken = 'connections_connection_access_admin_info';
|
||||
if (!roles.get().length && !users.get().length) {
|
||||
if (!keys.length) {
|
||||
if (filterState.filterValue) {
|
||||
tableInfoText = 'ui_search_no_result_placeholder';
|
||||
} else {
|
||||
@@ -108,34 +105,36 @@ export const ConnectionAccessGrantedList = observer<Props>(function ConnectionAc
|
||||
<Group box medium overflow>
|
||||
<container>
|
||||
<ConnectionAccessTableHeader filterState={filterState} disabled={disabled}>
|
||||
<Button disabled={disabled || !selectedList.get().length} mod={['outlined']} onClick={revoke}>{translate('ui_delete')}</Button>
|
||||
<Button disabled={disabled || !selected} mod={['outlined']} onClick={revoke}>{translate('ui_delete')}</Button>
|
||||
<Button disabled={disabled} mod={['unelevated']} onClick={props.onEdit}>{translate('ui_edit')}</Button>
|
||||
</ConnectionAccessTableHeader>
|
||||
<table-container>
|
||||
<Table selectedItems={selectedSubjects}>
|
||||
<ConnectionAccessTableInnerHeader />
|
||||
<Table keys={keys} selectedItems={selectedSubjects}>
|
||||
<ConnectionAccessTableInnerHeader disabled={disabled} />
|
||||
<TableBody>
|
||||
<TableItem item='tableInfo' selectDisabled>
|
||||
<TableColumnValue colSpan={5}>
|
||||
{translate(tableInfoText)}
|
||||
</TableColumnValue>
|
||||
</TableItem>
|
||||
{roles.get().map(role => (
|
||||
{roles.map(role => (
|
||||
<ConnectionAccessTableItem
|
||||
key={role.roleId}
|
||||
id={role.roleId}
|
||||
name={role.roleName || ''}
|
||||
tooltip={role.roleName}
|
||||
description={role.description}
|
||||
icon='/icons/role.svg'
|
||||
iconTooltip={translate('authentication_role_icon_tooltip')}
|
||||
disabled={disabled}
|
||||
/>
|
||||
))}
|
||||
{users.get().map(user => (
|
||||
{users.map(user => (
|
||||
<ConnectionAccessTableItem
|
||||
key={user.userId}
|
||||
id={user.userId}
|
||||
name={user.userId}
|
||||
tooltip={user.userId}
|
||||
icon='/icons/user.svg'
|
||||
iconTooltip={translate('authentication_user_icon_tooltip')}
|
||||
disabled={disabled}
|
||||
|
||||
+26
-23
@@ -6,9 +6,9 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { computed, observable } from 'mobx';
|
||||
import { observable } from 'mobx';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import type { RoleInfo } from '@cloudbeaver/core-authentication';
|
||||
@@ -21,6 +21,8 @@ import {
|
||||
Group,
|
||||
Button,
|
||||
useObjectRef,
|
||||
getComputed,
|
||||
getSelectedItems,
|
||||
} from '@cloudbeaver/core-blocks';
|
||||
import { useTranslate } from '@cloudbeaver/core-localization';
|
||||
import type { AdminUserInfoFragment } from '@cloudbeaver/core-sdk';
|
||||
@@ -78,59 +80,60 @@ export const ConnectionAccessList = observer<Props>(function ConnectionAccessLis
|
||||
const translate = useTranslate();
|
||||
const [selectedSubjects] = useState<Map<any, boolean>>(() => observable(new Map()));
|
||||
const [filterState] = useState<IFilterState>(() => observable({ filterValue: '' }));
|
||||
const selectedList = useMemo(() => computed(
|
||||
() => Array.from(selectedSubjects.entries()).filter(([key, value]) => value).map(([key]) => key)
|
||||
), [selectedSubjects]);
|
||||
|
||||
const roles = getFilteredRoles(roleList, filterState.filterValue);
|
||||
const users = getFilteredUsers(userList, filterState.filterValue);
|
||||
const keys = roles.map(role => role.roleId).concat(users.map(user => user.userId));
|
||||
|
||||
const selected = getComputed(() => Array.from(selectedSubjects.values()).some(v => v));
|
||||
|
||||
const grant = useCallback(() => {
|
||||
props.onGrant(selectedList.get());
|
||||
props.onGrant(getSelectedItems(selectedSubjects));
|
||||
selectedSubjects.clear();
|
||||
}, []);
|
||||
|
||||
const roles = useMemo(() => computed(() => getFilteredRoles(
|
||||
roleList, filterState.filterValue
|
||||
)), [filterState, roleList]);
|
||||
|
||||
const users = useMemo(() => computed(() => getFilteredUsers(
|
||||
userList, filterState.filterValue
|
||||
)), [filterState, userList]);
|
||||
|
||||
return styled(style)(
|
||||
<Group box medium overflow>
|
||||
<container>
|
||||
<ConnectionAccessTableHeader filterState={filterState} disabled={disabled}>
|
||||
<Button disabled={disabled || !selectedList.get().length} mod={['unelevated']} onClick={grant}>{translate('ui_add')}</Button>
|
||||
<Button disabled={disabled || !selected} mod={['unelevated']} onClick={grant}>{translate('ui_add')}</Button>
|
||||
</ConnectionAccessTableHeader>
|
||||
<table-container>
|
||||
<Table selectedItems={selectedSubjects}>
|
||||
<ConnectionAccessTableInnerHeader />
|
||||
<Table
|
||||
keys={keys}
|
||||
selectedItems={selectedSubjects}
|
||||
isItemSelectable={item => !grantedSubjects.includes(item)}
|
||||
>
|
||||
<ConnectionAccessTableInnerHeader disabled={disabled} />
|
||||
<TableBody>
|
||||
{!roles.get().length && !users.get().length && filterState.filterValue && (
|
||||
{!keys.length && filterState.filterValue && (
|
||||
<TableItem item='tableInfo' selectDisabled>
|
||||
<TableColumnValue colSpan={5}>
|
||||
{translate('ui_search_no_result_placeholder')}
|
||||
</TableColumnValue>
|
||||
</TableItem>
|
||||
)}
|
||||
{roles.get().map(role => (
|
||||
{roles.map(role => (
|
||||
<ConnectionAccessTableItem
|
||||
key={role.roleId}
|
||||
id={role.roleId}
|
||||
name={role.roleName || ''}
|
||||
tooltip={role.roleName}
|
||||
description={role.description}
|
||||
icon='/icons/role.svg'
|
||||
iconTooltip='authentication_role_icon_tooltip'
|
||||
disabled={disabled || grantedSubjects.includes(role.roleId)}
|
||||
iconTooltip={translate('authentication_role_icon_tooltip')}
|
||||
disabled={disabled}
|
||||
/>
|
||||
))}
|
||||
{users.get().map(user => (
|
||||
{users.map(user => (
|
||||
<ConnectionAccessTableItem
|
||||
key={user.userId}
|
||||
id={user.userId}
|
||||
name={user.userId}
|
||||
tooltip={user.userId}
|
||||
icon='/icons/user.svg'
|
||||
iconTooltip={translate('authentication_user_icon_tooltip')}
|
||||
disabled={disabled || grantedSubjects.includes(user.userId)}
|
||||
disabled={disabled}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
|
||||
+6
-3
@@ -9,11 +9,12 @@
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { TableColumnHeader, TableHeader } from '@cloudbeaver/core-blocks';
|
||||
import { TableColumnHeader, TableHeader, TableSelect } from '@cloudbeaver/core-blocks';
|
||||
import { useTranslate } from '@cloudbeaver/core-localization';
|
||||
import { composes, useStyles } from '@cloudbeaver/core-theming';
|
||||
|
||||
interface Props {
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@@ -32,11 +33,13 @@ const styles = composes(
|
||||
`
|
||||
);
|
||||
|
||||
export const ConnectionAccessTableInnerHeader = observer<Props>(function ConnectionAccessTableInnerHeader({ className }) {
|
||||
export const ConnectionAccessTableInnerHeader = observer<Props>(function ConnectionAccessTableInnerHeader({ disabled, className }) {
|
||||
const translate = useTranslate();
|
||||
return styled(useStyles(styles))(
|
||||
<TableHeader className={className}>
|
||||
<TableColumnHeader min />
|
||||
<TableColumnHeader min>
|
||||
<TableSelect id='selectUsersAndRoles' disabled={disabled} />
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader min />
|
||||
<TableColumnHeader>{translate('connections_connection_access_user_or_role_name')}</TableColumnHeader>
|
||||
<TableColumnHeader>{translate('connections_connection_description')}</TableColumnHeader>
|
||||
|
||||
+15
-6
@@ -7,17 +7,18 @@
|
||||
*/
|
||||
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useContext } from 'react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { StaticImage, TableColumnValue, TableItem, TableItemSelect } from '@cloudbeaver/core-blocks';
|
||||
import { TLocalizationToken, useTranslate } from '@cloudbeaver/core-localization';
|
||||
import { StaticImage, TableColumnValue, TableContext, TableItem, TableItemSelect } from '@cloudbeaver/core-blocks';
|
||||
|
||||
interface Props {
|
||||
id: any;
|
||||
name: string;
|
||||
icon: string;
|
||||
disabled: boolean;
|
||||
iconTooltip?: TLocalizationToken;
|
||||
iconTooltip?: string;
|
||||
tooltip?: string;
|
||||
description?: string;
|
||||
className?: string;
|
||||
}
|
||||
@@ -30,12 +31,20 @@ const style = css`
|
||||
`;
|
||||
|
||||
export const ConnectionAccessTableItem = observer<Props>(function ConnectionAccessTableItem({
|
||||
id, name, description, icon, iconTooltip, disabled, className,
|
||||
id, name, description, icon, iconTooltip, tooltip, disabled: tableDisabled, className,
|
||||
}) {
|
||||
const translate = useTranslate();
|
||||
const tableContext = useContext(TableContext);
|
||||
|
||||
if (!tableContext) {
|
||||
throw new Error('Context must be provided');
|
||||
}
|
||||
|
||||
const disabled = tableDisabled || tableContext.state.isItemSelectable?.(id) === false;
|
||||
|
||||
return styled(style)(
|
||||
<TableItem
|
||||
item={id}
|
||||
title={tooltip}
|
||||
disabled={disabled}
|
||||
selectDisabled={disabled}
|
||||
className={className}
|
||||
@@ -43,7 +52,7 @@ export const ConnectionAccessTableItem = observer<Props>(function ConnectionAcce
|
||||
<TableColumnValue centerContent flex>
|
||||
<TableItemSelect disabled={disabled} />
|
||||
</TableColumnValue>
|
||||
<TableColumnValue><StaticImage icon={icon} title={translate(iconTooltip)} /></TableColumnValue>
|
||||
<TableColumnValue><StaticImage icon={icon} title={iconTooltip} /></TableColumnValue>
|
||||
<TableColumnValue>{name}</TableColumnValue>
|
||||
<TableColumnValue>{description}</TableColumnValue>
|
||||
</TableItem>
|
||||
|
||||
@@ -75,7 +75,7 @@ export const RenameDialog: DialogComponent<RenameDialogPayload, string> = functi
|
||||
mod={['unelevated']}
|
||||
onClick={() => resolveDialog(name)}
|
||||
>
|
||||
<Translate token={confirmActionText || 'ui_processing_ok'} />
|
||||
<Translate token={confirmActionText || 'ui_rename'} />
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -45,6 +45,8 @@ export const defaultENLocale = [
|
||||
['ui_search_no_result_placeholder', 'No results have been found.'],
|
||||
['ui_save_reminder', 'You have unsaved changes.'],
|
||||
['ui_yes', 'Yes'],
|
||||
['ui_select_all', 'Select all'],
|
||||
['ui_reconnect', 'Reconnect'],
|
||||
|
||||
['root_permission_denied', 'You have no permissions'],
|
||||
['app_root_session_expire_warning_title', 'Session is about to expire'],
|
||||
|
||||
@@ -43,6 +43,8 @@ export const defaultRULocale = [
|
||||
['ui_search_no_result_placeholder', 'По вашему запросу ничего не найдено.'],
|
||||
['ui_save_reminder', 'У вас есть несохраненные изменения.'],
|
||||
['ui_yes', 'Да'],
|
||||
['ui_select_all', 'Выбрать все'],
|
||||
['ui_reconnect', 'Переподключиться'],
|
||||
|
||||
['root_permission_denied', 'Отказано в доступе'],
|
||||
['app_root_session_expire_warning_title', 'Ваша сессия скоро закончится'],
|
||||
|
||||
+11
-5
@@ -21,6 +21,7 @@ import {
|
||||
Button,
|
||||
useObjectRef,
|
||||
getComputed,
|
||||
getSelectedItems,
|
||||
} from '@cloudbeaver/core-blocks';
|
||||
import { DBDriverResource } from '@cloudbeaver/core-connections';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
@@ -85,12 +86,12 @@ export const ConnectionList = observer<Props>(function ConnectionList({
|
||||
const selected = getComputed(() => Array.from(selectedSubjects.values()).some(v => v));
|
||||
|
||||
const grant = useCallback(() => {
|
||||
const selectedList = Array.from(selectedSubjects.entries()).filter(([key, value]) => value).map(([key]) => key);
|
||||
props.onGrant(selectedList);
|
||||
props.onGrant(getSelectedItems(selectedSubjects));
|
||||
selectedSubjects.clear();
|
||||
}, []);
|
||||
|
||||
const connections = getFilteredConnections(connectionList, filterState.filterValue);
|
||||
const keys = connections.map(connection => connection.id);
|
||||
|
||||
return styled(style)(
|
||||
<Group box medium overflow>
|
||||
@@ -99,8 +100,12 @@ export const ConnectionList = observer<Props>(function ConnectionList({
|
||||
<Button disabled={disabled || !selected} mod={['unelevated']} onClick={grant}>{translate('ui_add')}</Button>
|
||||
</GrantedConnectionsTableHeader>
|
||||
<table-container>
|
||||
<Table selectedItems={selectedSubjects}>
|
||||
<GrantedConnectionsTableInnerHeader />
|
||||
<Table
|
||||
keys={keys}
|
||||
selectedItems={selectedSubjects}
|
||||
isItemSelectable={item => !grantedSubjects.includes(item)}
|
||||
>
|
||||
<GrantedConnectionsTableInnerHeader disabled={disabled} />
|
||||
<TableBody>
|
||||
{!connections.length && filterState.filterValue && (
|
||||
<TableItem item='tableInfo' selectDisabled>
|
||||
@@ -116,9 +121,10 @@ export const ConnectionList = observer<Props>(function ConnectionList({
|
||||
key={connection.id}
|
||||
id={connection.id}
|
||||
name={connection.name}
|
||||
tooltip={connection.name}
|
||||
host={`${connection.host || ''}${connection.host && connection.port ? ':' + connection.port : ''}`}
|
||||
icon={driver?.icon}
|
||||
disabled={disabled || grantedSubjects.includes(connection.id)}
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
+9
-7
@@ -19,7 +19,8 @@ import {
|
||||
BASE_CONTAINERS_STYLES,
|
||||
Group,
|
||||
Button,
|
||||
useObjectRef
|
||||
useObjectRef,
|
||||
getSelectedItems
|
||||
} from '@cloudbeaver/core-blocks';
|
||||
import { getComputed } from '@cloudbeaver/core-blocks';
|
||||
import { DBDriverResource } from '@cloudbeaver/core-connections';
|
||||
@@ -82,16 +83,16 @@ export const GrantedConnectionList = observer<Props>(function GrantedConnectionL
|
||||
const [selectedSubjects] = useState<Map<any, boolean>>(() => observable(new Map()));
|
||||
const [filterState] = useState<IFilterState>(() => observable({ filterValue: '' }));
|
||||
|
||||
const connections = getFilteredConnections(grantedConnections, filterState.filterValue);
|
||||
const keys = connections.map(connection => connection.id);
|
||||
|
||||
const selected = getComputed(() => Array.from(selectedSubjects.values()).some(v => v));
|
||||
|
||||
const revoke = useCallback(() => {
|
||||
const selectedList = Array.from(selectedSubjects.entries()).filter(([key, value]) => value).map(([key]) => key);
|
||||
props.onRevoke(selectedList);
|
||||
props.onRevoke(getSelectedItems(selectedSubjects));
|
||||
selectedSubjects.clear();
|
||||
}, []);
|
||||
|
||||
const connections = getFilteredConnections(grantedConnections, filterState.filterValue);
|
||||
|
||||
let tableInfoText: TLocalizationToken | null = null;
|
||||
if (!connections.length) {
|
||||
if (filterState.filterValue) {
|
||||
@@ -109,8 +110,8 @@ export const GrantedConnectionList = observer<Props>(function GrantedConnectionL
|
||||
<Button disabled={disabled} mod={['unelevated']} onClick={props.onEdit}>{translate('ui_edit')}</Button>
|
||||
</GrantedConnectionsTableHeader>
|
||||
<table-container>
|
||||
<Table selectedItems={selectedSubjects}>
|
||||
<GrantedConnectionsTableInnerHeader />
|
||||
<Table keys={keys} selectedItems={selectedSubjects}>
|
||||
<GrantedConnectionsTableInnerHeader disabled={disabled} />
|
||||
<TableBody>
|
||||
<TableItem item='tableInfo' selectDisabled>
|
||||
{tableInfoText && (
|
||||
@@ -126,6 +127,7 @@ export const GrantedConnectionList = observer<Props>(function GrantedConnectionL
|
||||
key={connection.id}
|
||||
id={connection.id}
|
||||
name={connection.name}
|
||||
tooltip={connection.name}
|
||||
host={`${connection.host || ''}${connection.port ? ':' + connection.port : ''}`}
|
||||
icon={driver?.icon}
|
||||
disabled={disabled}
|
||||
|
||||
+6
-3
@@ -9,11 +9,12 @@
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { TableColumnHeader, TableHeader } from '@cloudbeaver/core-blocks';
|
||||
import { TableColumnHeader, TableHeader, TableSelect } from '@cloudbeaver/core-blocks';
|
||||
import { useTranslate } from '@cloudbeaver/core-localization';
|
||||
import { composes, useStyles } from '@cloudbeaver/core-theming';
|
||||
|
||||
interface Props {
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@@ -32,11 +33,13 @@ const styles = composes(
|
||||
`
|
||||
);
|
||||
|
||||
export const GrantedConnectionsTableInnerHeader = observer<Props>(function GrantedConnectionsTableInnerHeader({ className }) {
|
||||
export const GrantedConnectionsTableInnerHeader = observer<Props>(function GrantedConnectionsTableInnerHeader({ disabled, className }) {
|
||||
const translate = useTranslate();
|
||||
return styled(useStyles(styles))(
|
||||
<TableHeader className={className}>
|
||||
<TableColumnHeader min />
|
||||
<TableColumnHeader min>
|
||||
<TableSelect id='selectConnections' disabled={disabled} />
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader min />
|
||||
<TableColumnHeader>{translate('connections_connection_name')}</TableColumnHeader>
|
||||
<TableColumnHeader>{translate('connections_connection_address')}</TableColumnHeader>
|
||||
|
||||
+14
-6
@@ -7,10 +7,10 @@
|
||||
*/
|
||||
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useContext } from 'react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { StaticImage, TableColumnValue, TableItem, TableItemSelect } from '@cloudbeaver/core-blocks';
|
||||
import { TLocalizationToken, useTranslate } from '@cloudbeaver/core-localization';
|
||||
import { StaticImage, TableColumnValue, TableContext, TableItem, TableItemSelect } from '@cloudbeaver/core-blocks';
|
||||
|
||||
interface Props {
|
||||
id: any;
|
||||
@@ -18,7 +18,8 @@ interface Props {
|
||||
disabled: boolean;
|
||||
host?: string;
|
||||
icon?: string;
|
||||
iconTooltip?: TLocalizationToken;
|
||||
iconTooltip?: string;
|
||||
tooltip?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@@ -30,13 +31,20 @@ const style = css`
|
||||
`;
|
||||
|
||||
export const GrantedConnectionsTableItem = observer<Props>(function GrantedConnectionsTableItem({
|
||||
id, name, host, icon, iconTooltip, disabled, className,
|
||||
id, name, host, icon, iconTooltip, tooltip, disabled: tableDisabled, className,
|
||||
}) {
|
||||
const translate = useTranslate();
|
||||
const tableContext = useContext(TableContext);
|
||||
|
||||
if (!tableContext) {
|
||||
throw new Error('Context must be provided');
|
||||
}
|
||||
|
||||
const disabled = tableDisabled || tableContext.state.isItemSelectable?.(id) === false;
|
||||
|
||||
return styled(style)(
|
||||
<TableItem
|
||||
item={id}
|
||||
title={tooltip}
|
||||
disabled={disabled}
|
||||
selectDisabled={disabled}
|
||||
className={className}
|
||||
@@ -44,7 +52,7 @@ export const GrantedConnectionsTableItem = observer<Props>(function GrantedConne
|
||||
<TableColumnValue centerContent flex>
|
||||
<TableItemSelect disabled={disabled} />
|
||||
</TableColumnValue>
|
||||
<TableColumnValue>{icon && <StaticImage icon={icon} title={translate(iconTooltip)} />}</TableColumnValue>
|
||||
<TableColumnValue>{icon && <StaticImage icon={icon} title={iconTooltip} />}</TableColumnValue>
|
||||
<TableColumnValue>{name}</TableColumnValue>
|
||||
<TableColumnValue>{host && host}</TableColumnValue>
|
||||
</TableItem>
|
||||
|
||||
+27
-21
@@ -21,7 +21,8 @@ import {
|
||||
Group,
|
||||
Button,
|
||||
useObjectRef,
|
||||
getComputed
|
||||
getComputed,
|
||||
getSelectedItems
|
||||
} from '@cloudbeaver/core-blocks';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { TLocalizationToken, useTranslate } from '@cloudbeaver/core-localization';
|
||||
@@ -68,10 +69,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export const GrantedUserList = observer<Props>(function GrantedUserList({
|
||||
grantedUsers,
|
||||
disabled,
|
||||
onRevoke,
|
||||
onEdit,
|
||||
grantedUsers, disabled, onRevoke, onEdit,
|
||||
}) {
|
||||
const props = useObjectRef({ onRevoke, onEdit });
|
||||
const style = useStyles(styles, BASE_CONTAINERS_STYLES);
|
||||
@@ -84,14 +82,14 @@ export const GrantedUserList = observer<Props>(function GrantedUserList({
|
||||
|
||||
const selected = getComputed(() => Array.from(selectedSubjects.values()).some(v => v));
|
||||
|
||||
const users = getFilteredUsers(grantedUsers, filterState.filterValue);
|
||||
const keys = users.map(user => user.userId);
|
||||
|
||||
const revoke = useCallback(() => {
|
||||
const selectedList = Array.from(selectedSubjects.entries()).filter(([key, value]) => value).map(([key]) => key);
|
||||
props.onRevoke(selectedList);
|
||||
props.onRevoke(getSelectedItems(selectedSubjects));
|
||||
selectedSubjects.clear();
|
||||
}, []);
|
||||
|
||||
const users = getFilteredUsers(grantedUsers, filterState.filterValue);
|
||||
|
||||
let tableInfoText: TLocalizationToken | null = null;
|
||||
if (!users.length) {
|
||||
if (filterState.filterValue) {
|
||||
@@ -109,8 +107,12 @@ export const GrantedUserList = observer<Props>(function GrantedUserList({
|
||||
<Button disabled={disabled} mod={['unelevated']} onClick={props.onEdit}>{translate('ui_edit')}</Button>
|
||||
</GrantedUsersTableHeader>
|
||||
<table-container>
|
||||
<Table selectedItems={selectedSubjects}>
|
||||
<GrantedUsersTableInnerHeader />
|
||||
<Table
|
||||
keys={keys}
|
||||
selectedItems={selectedSubjects}
|
||||
isItemSelectable={item => !usersResource.isActiveUser(item)}
|
||||
>
|
||||
<GrantedUsersTableInnerHeader disabled={disabled} />
|
||||
<TableBody>
|
||||
<TableItem item='tableInfo' selectDisabled>
|
||||
{tableInfoText && (
|
||||
@@ -119,16 +121,20 @@ export const GrantedUserList = observer<Props>(function GrantedUserList({
|
||||
</TableColumnValue>
|
||||
)}
|
||||
</TableItem>
|
||||
{users.map(user => (
|
||||
<GrantedUsersTableItem
|
||||
key={user.userId}
|
||||
id={user.userId}
|
||||
name={`${user.userId}${usersResource.isActiveUser(user.userId) ? ' (you)' : ''}`}
|
||||
icon='/icons/user.svg'
|
||||
iconTooltip={translate('authentication_user_icon_tooltip')}
|
||||
disabled={disabled}
|
||||
/>
|
||||
))}
|
||||
{users.map(user => {
|
||||
const activeUser = usersResource.isActiveUser(user.userId);
|
||||
return (
|
||||
<GrantedUsersTableItem
|
||||
key={user.userId}
|
||||
id={user.userId}
|
||||
name={`${user.userId}${activeUser ? ' (you)' : ''}`}
|
||||
tooltip={activeUser ? translate('administration_roles_role_granted_users_permission_denied') : user.userId}
|
||||
icon='/icons/user.svg'
|
||||
iconTooltip={translate('authentication_user_icon_tooltip')}
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</table-container>
|
||||
|
||||
+7
-3
@@ -9,11 +9,12 @@
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { TableColumnHeader, TableHeader } from '@cloudbeaver/core-blocks';
|
||||
import { TableColumnHeader, TableHeader, TableSelect } from '@cloudbeaver/core-blocks';
|
||||
import { useTranslate } from '@cloudbeaver/core-localization';
|
||||
import { composes, useStyles } from '@cloudbeaver/core-theming';
|
||||
|
||||
interface Props {
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@@ -32,11 +33,14 @@ const styles = composes(
|
||||
`
|
||||
);
|
||||
|
||||
export const GrantedUsersTableInnerHeader = observer<Props>(function GrantedUsersTableInnerHeader({ className }) {
|
||||
export const GrantedUsersTableInnerHeader = observer<Props>(function GrantedUsersTableInnerHeader({ disabled, className }) {
|
||||
const translate = useTranslate();
|
||||
|
||||
return styled(useStyles(styles))(
|
||||
<TableHeader className={className}>
|
||||
<TableColumnHeader min />
|
||||
<TableColumnHeader min>
|
||||
<TableSelect id='selectUsers' disabled={disabled} />
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader min />
|
||||
<TableColumnHeader>{translate('administration_roles_role_granted_users_user_id')}</TableColumnHeader>
|
||||
</TableHeader>
|
||||
|
||||
+15
-6
@@ -7,17 +7,18 @@
|
||||
*/
|
||||
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useContext } from 'react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { StaticImage, TableColumnValue, TableItem, TableItemSelect } from '@cloudbeaver/core-blocks';
|
||||
import { TLocalizationToken, useTranslate } from '@cloudbeaver/core-localization';
|
||||
import { StaticImage, TableColumnValue, TableContext, TableItem, TableItemSelect } from '@cloudbeaver/core-blocks';
|
||||
|
||||
interface Props {
|
||||
id: any;
|
||||
name: string;
|
||||
icon: string;
|
||||
disabled: boolean;
|
||||
iconTooltip?: TLocalizationToken;
|
||||
iconTooltip?: string;
|
||||
tooltip?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@@ -29,12 +30,20 @@ const style = css`
|
||||
`;
|
||||
|
||||
export const GrantedUsersTableItem = observer<Props>(function GrantedUsersTableItem({
|
||||
id, name, icon, iconTooltip, disabled, className,
|
||||
id, name, icon, iconTooltip, tooltip, disabled: tableDisabled, className,
|
||||
}) {
|
||||
const translate = useTranslate();
|
||||
const tableContext = useContext(TableContext);
|
||||
|
||||
if (!tableContext) {
|
||||
throw new Error('Context must be provided');
|
||||
}
|
||||
|
||||
const disabled = tableDisabled || tableContext.state.isItemSelectable?.(id) === false;
|
||||
|
||||
return styled(style)(
|
||||
<TableItem
|
||||
item={id}
|
||||
title={tooltip}
|
||||
disabled={disabled}
|
||||
selectDisabled={disabled}
|
||||
className={className}
|
||||
@@ -42,7 +51,7 @@ export const GrantedUsersTableItem = observer<Props>(function GrantedUsersTableI
|
||||
<TableColumnValue centerContent flex>
|
||||
<TableItemSelect disabled={disabled} />
|
||||
</TableColumnValue>
|
||||
<TableColumnValue><StaticImage icon={icon} title={translate(iconTooltip)} /></TableColumnValue>
|
||||
<TableColumnValue><StaticImage icon={icon} title={iconTooltip} /></TableColumnValue>
|
||||
<TableColumnValue>{name}</TableColumnValue>
|
||||
</TableItem>
|
||||
);
|
||||
|
||||
+21
-16
@@ -22,6 +22,7 @@ import {
|
||||
Button,
|
||||
useObjectRef,
|
||||
getComputed,
|
||||
getSelectedItems,
|
||||
} from '@cloudbeaver/core-blocks';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { useTranslate } from '@cloudbeaver/core-localization';
|
||||
@@ -84,14 +85,14 @@ export const UserList = observer<Props>(function UserList({
|
||||
|
||||
const selected = getComputed(() => Array.from(selectedSubjects.values()).some(v => v));
|
||||
|
||||
const users = getFilteredUsers(userList, filterState.filterValue);
|
||||
const keys = users.map(user => user.userId);
|
||||
|
||||
const grant = useCallback(() => {
|
||||
const selectedList = Array.from(selectedSubjects.entries()).filter(([key, value]) => value).map(([key]) => key);
|
||||
props.onGrant(selectedList);
|
||||
props.onGrant(getSelectedItems(selectedSubjects));
|
||||
selectedSubjects.clear();
|
||||
}, []);
|
||||
|
||||
const users = getFilteredUsers(userList, filterState.filterValue);
|
||||
|
||||
return styled(style)(
|
||||
<Group box medium overflow>
|
||||
<container>
|
||||
@@ -99,8 +100,8 @@ export const UserList = observer<Props>(function UserList({
|
||||
<Button disabled={disabled || !selected} mod={['unelevated']} onClick={grant}>{translate('ui_add')}</Button>
|
||||
</GrantedUsersTableHeader>
|
||||
<table-container>
|
||||
<Table selectedItems={selectedSubjects}>
|
||||
<GrantedUsersTableInnerHeader />
|
||||
<Table keys={keys} selectedItems={selectedSubjects} isItemSelectable={item => !grantedUsers.includes(item)}>
|
||||
<GrantedUsersTableInnerHeader disabled={disabled} />
|
||||
<TableBody>
|
||||
{!users.length && filterState.filterValue && (
|
||||
<TableItem item='tableInfo' selectDisabled>
|
||||
@@ -109,16 +110,20 @@ export const UserList = observer<Props>(function UserList({
|
||||
</TableColumnValue>
|
||||
</TableItem>
|
||||
)}
|
||||
{users.map(user => (
|
||||
<GrantedUsersTableItem
|
||||
key={user.userId}
|
||||
id={user.userId}
|
||||
name={`${user.userId}${usersResource.isActiveUser(user.userId) ? ' (you)' : ''}`}
|
||||
icon='/icons/user.svg'
|
||||
iconTooltip={translate('authentication_user_icon_tooltip')}
|
||||
disabled={disabled || !!grantedUsers.includes(user.userId)}
|
||||
/>
|
||||
))}
|
||||
{users.map(user => {
|
||||
const activeUser = usersResource.isActiveUser(user.userId);
|
||||
return (
|
||||
<GrantedUsersTableItem
|
||||
key={user.userId}
|
||||
id={user.userId}
|
||||
name={`${user.userId}${activeUser ? ' (you)' : ''}`}
|
||||
tooltip={activeUser ? translate('administration_roles_role_granted_users_permission_denied') : user.userId}
|
||||
icon='/icons/user.svg'
|
||||
iconTooltip={translate('authentication_user_icon_tooltip')}
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</table-container>
|
||||
|
||||
@@ -98,6 +98,7 @@ export default [
|
||||
['administration_roles_role_granted_users_user_id', 'User ID'],
|
||||
['administration_roles_role_granted_users_user_name', 'User name'],
|
||||
['administration_roles_role_granted_users_empty', 'No available users'],
|
||||
['administration_roles_role_granted_users_permission_denied', "You can't edit your own permissions"],
|
||||
|
||||
['administration_roles_role_granted_connections_tab_title', 'Connections'],
|
||||
['administration_roles_role_granted_connections_search_placeholder', 'Search for connection name...'],
|
||||
|
||||
@@ -86,6 +86,7 @@ export default [
|
||||
['administration_roles_role_granted_users_user_id', 'ID пользователя'],
|
||||
['administration_roles_role_granted_users_user_name', 'Имя пользователя'],
|
||||
['administration_roles_role_granted_users_empty', 'Нет доступных пользователей'],
|
||||
['administration_roles_role_granted_users_permission_denied', 'Вы не можете редактировать свои настройки доступа'],
|
||||
|
||||
['administration_roles_role_granted_connections_tab_title', 'Подключения'],
|
||||
['administration_roles_role_granted_connections_search_placeholder', 'Поиск по названию подключения...'],
|
||||
|
||||
+1
-1
@@ -140,7 +140,7 @@ export class PublicConnectionFormService {
|
||||
const result = await this.commonDialogService.open(ConfirmationDialog, {
|
||||
title: 'connections_public_connection_edit_reconnect_title',
|
||||
message: 'connections_public_connection_edit_reconnect_message',
|
||||
confirmActionText: 'ui_yes',
|
||||
confirmActionText: 'ui_reconnect',
|
||||
});
|
||||
|
||||
if (result === DialogueStateResult.Rejected) {
|
||||
|
||||
Reference in New Issue
Block a user