mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2024-11-22 08:46:39 -06:00
Upgrade react-table from v7 to v8. #7419
This commit is contained in:
parent
3e6bd29198
commit
5ec6faff13
@ -86,6 +86,8 @@
|
||||
"@projectstorm/react-diagrams": "^6.6.1",
|
||||
"@simonwep/pickr": "^1.5.1",
|
||||
"@szhsin/react-menu": "^2.2.0",
|
||||
"@tanstack/react-table": "^8.16.0",
|
||||
"@tanstack/react-virtual": "^3.4.0",
|
||||
"@types/classnames": "^2.2.6",
|
||||
"@types/react": "^17.0.80",
|
||||
"@types/react-dom": "^17.0.25",
|
||||
@ -143,10 +145,8 @@
|
||||
"react-resize-detector": "^9.1.0",
|
||||
"react-rnd": "^10.3.5",
|
||||
"react-select": "^5.7.2",
|
||||
"react-table": "^7.6.3",
|
||||
"react-timer-hook": "^3.0.5",
|
||||
"react-virtualized-auto-sizer": "^1.0.6",
|
||||
"react-window": "^1.8.5",
|
||||
"snapsvg-cjs": "^0.0.6",
|
||||
"socket.io-client": "^4.5.0",
|
||||
"split.js": "^1.5.10",
|
||||
|
@ -453,7 +453,7 @@ export class ColumnSchema extends BaseUISchema {
|
||||
},{
|
||||
id: 'max_val_attlen', skipChange: true, visible: false, type: '',
|
||||
},{
|
||||
id: 'attprecision', label: gettext('Scale'), width: 60, disableResizing: true,
|
||||
id: 'attprecision', label: gettext('Scale'), width: 60, enableResizing: false,
|
||||
deps: ['cltype'], type: 'int', group: gettext('Definition'),
|
||||
cell: (state)=>{
|
||||
return obj.attCell(state);
|
||||
|
@ -166,7 +166,7 @@ export default class ColumnSchema extends BaseUISchema {
|
||||
// Need to show this field only when creating new table
|
||||
// [in SubNode control]
|
||||
id: 'is_primary_key', label: gettext('Primary key?'),
|
||||
cell: 'switch', type: 'switch', width: 100, disableResizing: true, deps:['name', ['primary_key']],
|
||||
cell: 'switch', type: 'switch', width: 100, enableResizing: false, deps:['name', ['primary_key']],
|
||||
visible: ()=>{
|
||||
return obj.top?.nodeInfo && _.isUndefined(
|
||||
obj.top.nodeInfo['table'] || obj.top.nodeInfo['view'] ||
|
||||
@ -280,7 +280,7 @@ export default class ColumnSchema extends BaseUISchema {
|
||||
},
|
||||
},{
|
||||
id: 'attlen', label: gettext('Length/Precision'),
|
||||
deps: ['cltype'], type: 'int', group: gettext('Definition'), width: 120, disableResizing: true,
|
||||
deps: ['cltype'], type: 'int', group: gettext('Definition'), width: 120, enableResizing: false,
|
||||
cell: (state)=>{
|
||||
return obj.attCell(state);
|
||||
},
|
||||
@ -311,7 +311,7 @@ export default class ColumnSchema extends BaseUISchema {
|
||||
},{
|
||||
id: 'max_val_attlen', skipChange: true, visible: false, type: '',
|
||||
},{
|
||||
id: 'attprecision', label: gettext('Scale'), width: 60, disableResizing: true,
|
||||
id: 'attprecision', label: gettext('Scale'), width: 60, enableResizing: false,
|
||||
deps: ['cltype'], type: 'int', group: gettext('Definition'),
|
||||
cell: (state)=>{
|
||||
return obj.attCell(state);
|
||||
@ -439,7 +439,7 @@ export default class ColumnSchema extends BaseUISchema {
|
||||
},
|
||||
},{
|
||||
id: 'attnotnull', label: gettext('Not NULL?'), cell: 'switch',
|
||||
type: 'switch', width: 80, disableResizing: true,
|
||||
type: 'switch', width: 80, enableResizing: false,
|
||||
group: gettext('Constraints'), editable: this.editableCheckForTable,
|
||||
deps: ['colconstype'],
|
||||
readonly: (state) => {
|
||||
|
@ -137,7 +137,7 @@ class ExclusionColumnSchema extends BaseUISchema {
|
||||
let obj = this;
|
||||
return [{
|
||||
id: 'is_exp', label: '', type:'', cell: '', editable: false, width: 20,
|
||||
disableResizing: true,
|
||||
enableResizing: false,
|
||||
controlProps: {
|
||||
formatter: {
|
||||
fromRaw: function (rawValue) {
|
||||
@ -162,7 +162,7 @@ class ExclusionColumnSchema extends BaseUISchema {
|
||||
{label: 'ASC', value: true},
|
||||
{label: 'DESC', value: false},
|
||||
],
|
||||
editable: obj.isEditable, width: 110, disableResizing: true,
|
||||
editable: obj.isEditable, width: 110, enableResizing: false,
|
||||
controlProps: {
|
||||
allowClear: false,
|
||||
},
|
||||
@ -172,7 +172,7 @@ class ExclusionColumnSchema extends BaseUISchema {
|
||||
{label: 'FIRST', value: true},
|
||||
{label: 'LAST', value: false},
|
||||
], controlProps: {allowClear: false},
|
||||
editable: obj.isEditable, width: 110, disableResizing: true,
|
||||
editable: obj.isEditable, width: 110, enableResizing: false,
|
||||
},{
|
||||
id: 'operator', label: gettext('Operator'), type: 'select',
|
||||
width: 95,
|
||||
|
@ -130,7 +130,7 @@ class IndexColumnSchema extends BaseUISchema {
|
||||
return [
|
||||
{
|
||||
id: 'is_exp', label: '', type:'', cell: '', editable: false, width: 20,
|
||||
disableResizing: true,
|
||||
enableResizing: false,
|
||||
controlProps: {
|
||||
formatter: {
|
||||
fromRaw: function (rawValue) {
|
||||
@ -182,7 +182,7 @@ class IndexColumnSchema extends BaseUISchema {
|
||||
{label: 'ASC', value: false},
|
||||
{label: 'DESC', value: true},
|
||||
],
|
||||
width: 110, disableResizing: true,
|
||||
width: 110, enableResizing: false,
|
||||
controlProps: {
|
||||
allowClear: false,
|
||||
},
|
||||
@ -211,7 +211,7 @@ class IndexColumnSchema extends BaseUISchema {
|
||||
{label: 'FIRST', value: true},
|
||||
{label: 'LAST', value: false},
|
||||
], controlProps: {allowClear: false},
|
||||
width: 110, disableResizing: true,
|
||||
width: 110, enableResizing: false,
|
||||
editable: function(state) {
|
||||
return obj.isEditable(state);
|
||||
},
|
||||
@ -243,7 +243,7 @@ export class WithSchema extends BaseUISchema {
|
||||
super({});
|
||||
this.node_info = node_info;
|
||||
}
|
||||
|
||||
|
||||
get baseFields() {
|
||||
let withSchemaObj = this;
|
||||
return [
|
||||
@ -308,7 +308,7 @@ export class WithSchema extends BaseUISchema {
|
||||
depChange: (state, source) => {
|
||||
if (state.amname !== 'btree') {
|
||||
return {deduplicate_items:undefined};
|
||||
} else if (state.amname === 'btree' && source[0] !== 'deduplicate_items' &&
|
||||
} else if (state.amname === 'btree' && source[0] !== 'deduplicate_items' &&
|
||||
withSchemaObj.node_info.server.version >= 130000) {
|
||||
return {deduplicate_items: true};
|
||||
}
|
||||
|
@ -156,7 +156,7 @@ export class PartitionsSchema extends BaseUISchema {
|
||||
mode: ['properties'],
|
||||
},{
|
||||
id: 'is_attach', label:gettext('Operation'), cell: 'select', type: 'select',
|
||||
width: 120, disableResizing: true, options: [
|
||||
width: 120, enableResizing: false, options: [
|
||||
{label: gettext('Attach'), value: true},
|
||||
{label: gettext('Create'), value: false},
|
||||
], controlProps: {allowClear: false},
|
||||
@ -225,7 +225,7 @@ export class PartitionsSchema extends BaseUISchema {
|
||||
},
|
||||
},{
|
||||
id: 'is_default', label: gettext('Default'), type: 'switch', cell:'switch',
|
||||
width: 55, disableResizing: true, min_version: 110000,
|
||||
width: 55, enableResizing: false, min_version: 110000,
|
||||
editable: function(state) {
|
||||
return (obj.top && (obj.top.sessData.partition_type == 'range' ||
|
||||
obj.top.sessData.partition_type == 'list') && obj.isNew(state)
|
||||
|
@ -37,7 +37,7 @@ import { parseApiError } from '../../../static/js/api_instance';
|
||||
import SectionContainer from './components/SectionContainer';
|
||||
import Replication from './Replication';
|
||||
import RefreshButton from './components/RefreshButtons';
|
||||
import {getExpandCell } from '../../../static/js/components/PgTable';
|
||||
import { getExpandCell } from '../../../static/js/components/PgReactTableStyled';
|
||||
|
||||
function parseData(data) {
|
||||
let res = [];
|
||||
@ -132,6 +132,8 @@ const useStyles = makeStyles((theme) => ({
|
||||
}
|
||||
}));
|
||||
|
||||
let activeQSchemaObj = new ActiveQuery();
|
||||
|
||||
function Dashboard({
|
||||
nodeItem, nodeData, node, treeNodeInfo,
|
||||
...props
|
||||
@ -150,7 +152,6 @@ function Dashboard({
|
||||
const [mainTabVal, setMainTabVal] = useState(0);
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
const [activeOnly, setActiveOnly] = useState(false);
|
||||
const [schemaDict, setSchemaDict] = React.useState({});
|
||||
const [systemStatsTabVal, setSystemStatsTabVal] = useState(0);
|
||||
const [ldid, setLdid] = useState(0);
|
||||
|
||||
@ -183,64 +184,62 @@ function Dashboard({
|
||||
|
||||
const serverConfigColumns = [
|
||||
{
|
||||
accessor: 'name',
|
||||
Header: gettext('Name'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 50,
|
||||
width: 100,
|
||||
minResizeWidth: 150,
|
||||
accessorKey: 'name',
|
||||
header: gettext('Name'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
minSize: 100,
|
||||
size: 100,
|
||||
},
|
||||
{
|
||||
accessor: 'category',
|
||||
Header: gettext('Category'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 50,
|
||||
accessorKey: 'category',
|
||||
header: gettext('Category'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
minSize: 50,
|
||||
size: 50,
|
||||
},
|
||||
{
|
||||
accessor: 'setting',
|
||||
Header: gettext('Value'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 50,
|
||||
width: 100,
|
||||
accessorKey: 'setting',
|
||||
header: gettext('Value'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
minSize: 50,
|
||||
size: 100,
|
||||
},
|
||||
{
|
||||
accessor: 'unit',
|
||||
Header: gettext('Unit'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 26,
|
||||
width: 30,
|
||||
accessorKey: 'unit',
|
||||
header: gettext('Unit'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
minSize: 30,
|
||||
size: 30,
|
||||
},
|
||||
{
|
||||
accessor: 'short_desc',
|
||||
Header: gettext('Description'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
accessorKey: 'short_desc',
|
||||
header: gettext('Description'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
},
|
||||
];
|
||||
|
||||
const activityColumns = [
|
||||
{
|
||||
accessor: 'terminate_query',
|
||||
Header: () => null,
|
||||
sortable: true,
|
||||
resizable: false,
|
||||
disableGlobalFilter: false,
|
||||
disableResizing: true,
|
||||
width: 35,
|
||||
maxWidth: 35,
|
||||
minWidth: 35,
|
||||
header: () => null,
|
||||
enableSorting: true,
|
||||
enableResizing: false,
|
||||
enableFilters: false,
|
||||
size: 35,
|
||||
maxSize: 35,
|
||||
minSize: 35,
|
||||
id: 'btn-terminate',
|
||||
// eslint-disable-next-line react/display-name
|
||||
Cell: ({ row }) => {
|
||||
cell: ({ row }) => {
|
||||
let terminate_session_url =
|
||||
url_for('dashboard.index') + 'terminate_session' + '/' + sid,
|
||||
title = gettext('Terminate Session?'),
|
||||
@ -268,7 +267,7 @@ function Dashboard({
|
||||
!canTakeAction(row, 'terminate')
|
||||
)
|
||||
return;
|
||||
let url = action_url + '/' + row.values.pid;
|
||||
let url = action_url + '/' + row.original.pid;
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
title,
|
||||
txtConfirm,
|
||||
@ -302,15 +301,15 @@ function Dashboard({
|
||||
},
|
||||
},
|
||||
{
|
||||
accessor: 'cancel_Query',
|
||||
Header: () => null,
|
||||
sortable: true,
|
||||
resizable: false,
|
||||
disableGlobalFilter: false,
|
||||
width: 35,
|
||||
minWidth: 0,
|
||||
header: () => null,
|
||||
enableSorting: true,
|
||||
enableResizing: false,
|
||||
enableFilters: false,
|
||||
size: 35,
|
||||
maxSize: 35,
|
||||
minSize: 35,
|
||||
id: 'btn-cancel',
|
||||
Cell: ({ row }) => {
|
||||
cell: ({ row }) => {
|
||||
let cancel_query_url =
|
||||
url_for('dashboard.index') + 'cancel_query' + '/' + sid,
|
||||
title = gettext('Cancel Active Query?'),
|
||||
@ -334,7 +333,7 @@ function Dashboard({
|
||||
onClick={() => {
|
||||
if (!canTakeAction(row, 'cancel'))
|
||||
return;
|
||||
let url = action_url + '/' + row.values.pid;
|
||||
let url = action_url + '/' + row.original.pid;
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
title,
|
||||
txtConfirm,
|
||||
@ -370,280 +369,272 @@ function Dashboard({
|
||||
},
|
||||
},
|
||||
{
|
||||
accessor: 'view_active_query',
|
||||
Header: () => null,
|
||||
sortable: true,
|
||||
resizable: false,
|
||||
disableGlobalFilter: false,
|
||||
width: 35,
|
||||
minWidth: 0,
|
||||
header: () => null,
|
||||
enableSorting: true,
|
||||
enableResizing: false,
|
||||
enableFilters: false,
|
||||
size: 35,
|
||||
maxSize: 35,
|
||||
minSize: 35,
|
||||
id: 'btn-edit',
|
||||
Cell: getExpandCell({
|
||||
onClick: (row) => {
|
||||
let schema = new ActiveQuery({
|
||||
query: row.original.query,
|
||||
backend_type: row.original.backend_type,
|
||||
state_change: row.original.state_change,
|
||||
query_start: row.original.query_start,
|
||||
});
|
||||
setSchemaDict(prevState => ({
|
||||
...prevState,
|
||||
[row.id]: schema
|
||||
}));
|
||||
},
|
||||
cell: getExpandCell({
|
||||
title: gettext('View the active session details')
|
||||
}),
|
||||
},
|
||||
{
|
||||
accessor: 'pid',
|
||||
Header: gettext('PID'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 26,
|
||||
width: 60,
|
||||
accessorKey: 'pid',
|
||||
header: gettext('PID'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
minSize: 50,
|
||||
size: 60,
|
||||
},
|
||||
{
|
||||
accessor: 'datname',
|
||||
Header: gettext('Database'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 26,
|
||||
width: 80,
|
||||
isVisible: !did
|
||||
accessorKey: 'datname',
|
||||
header: gettext('Database'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
enableVisibility: !did,
|
||||
minSize: 50,
|
||||
size: 80,
|
||||
},
|
||||
{
|
||||
accessor: 'usename',
|
||||
Header: gettext('User'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 26,
|
||||
width: 60
|
||||
accessorKey: 'usename',
|
||||
header: gettext('User'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
minSize: 50,
|
||||
size: 60,
|
||||
},
|
||||
{
|
||||
accessor: 'application_name',
|
||||
Header: gettext('Application'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 26,
|
||||
accessorKey: 'application_name',
|
||||
header: gettext('Application'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
minSize: 50,
|
||||
},
|
||||
{
|
||||
accessor: 'client_addr',
|
||||
Header: gettext('Client'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 26,
|
||||
accessorKey: 'client_addr',
|
||||
header: gettext('Client'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
minSize: 50,
|
||||
size: 100
|
||||
},
|
||||
{
|
||||
accessor: 'backend_start',
|
||||
Header: gettext('Backend start'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 100,
|
||||
accessorKey: 'backend_start',
|
||||
header: gettext('Backend start'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
minSize: 100,
|
||||
},
|
||||
{
|
||||
accessor: 'xact_start',
|
||||
Header: gettext('Transaction start'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 26,
|
||||
accessorKey: 'xact_start',
|
||||
header: gettext('Transaction start'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
minSize: 100,
|
||||
},
|
||||
{
|
||||
accessor: 'state',
|
||||
Header: gettext('State'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 26,
|
||||
width:40
|
||||
accessorKey: 'state',
|
||||
header: gettext('State'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
minSize: 50,
|
||||
size: 50,
|
||||
},
|
||||
|
||||
{
|
||||
accessor: 'waiting',
|
||||
Header: gettext('Waiting'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
isVisible: treeNodeInfo?.server?.version < 90600
|
||||
accessorKey: 'waiting',
|
||||
header: gettext('Waiting'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
enableVisibility: treeNodeInfo?.server?.version < 90600
|
||||
},
|
||||
{
|
||||
accessor: 'wait_event',
|
||||
Header: gettext('Wait event'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
accessorKey: 'wait_event',
|
||||
header: gettext('Wait event'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
},
|
||||
{
|
||||
accessor: 'blocking_pids',
|
||||
Header: gettext('Blocking PIDs'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
accessorKey: 'blocking_pids',
|
||||
header: gettext('Blocking PIDs'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
},
|
||||
];
|
||||
|
||||
const databaseLocksColumns = [
|
||||
{
|
||||
accessor: 'pid',
|
||||
Header: gettext('PID'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 26,
|
||||
width: 50,
|
||||
accessorKey: 'pid',
|
||||
header: gettext('PID'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
minSize: 50,
|
||||
size: 50,
|
||||
},
|
||||
{
|
||||
accessor: 'datname',
|
||||
Header: gettext('Database'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 26,
|
||||
isVisible: !did,
|
||||
width: 80
|
||||
accessorKey: 'datname',
|
||||
header: gettext('Database'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
enableVisibility: !did,
|
||||
minSize: 50,
|
||||
size: 80,
|
||||
},
|
||||
{
|
||||
accessor: 'locktype',
|
||||
Header: gettext('Lock type'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 26,
|
||||
width: 80,
|
||||
accessorKey: 'locktype',
|
||||
header: gettext('Lock type'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
minSize: 50,
|
||||
size: 80,
|
||||
},
|
||||
{
|
||||
accessor: 'relation',
|
||||
Header: gettext('Target relation'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
accessorKey: 'relation',
|
||||
header: gettext('Target relation'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
},
|
||||
{
|
||||
accessor: 'page',
|
||||
Header: gettext('Page'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 26,
|
||||
width: 80,
|
||||
accessorKey: 'page',
|
||||
header: gettext('Page'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
minSize: 50,
|
||||
size: 80,
|
||||
},
|
||||
{
|
||||
accessor: 'tuple',
|
||||
Header: gettext('Tuple'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 26,
|
||||
accessorKey: 'tuple',
|
||||
header: gettext('Tuple'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
minSize: 50,
|
||||
size: 50,
|
||||
},
|
||||
{
|
||||
accessor: 'virtualxid',
|
||||
Header: gettext('vXID (target)'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 50,
|
||||
width: 80
|
||||
accessorKey: 'virtualxid',
|
||||
header: gettext('vXID (target)'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
minSize: 50,
|
||||
size: 50,
|
||||
},
|
||||
{
|
||||
accessor: 'transactionid',
|
||||
Header: gettext('XID (target)'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 50,
|
||||
width: 80,
|
||||
accessorKey: 'transactionid',
|
||||
header: gettext('XID (target)'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
minSize: 50,
|
||||
size: 80,
|
||||
},
|
||||
{
|
||||
accessor: 'classid',
|
||||
Header: gettext('Class'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 26,
|
||||
width: 80,
|
||||
accessorKey: 'classid',
|
||||
header: gettext('Class'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
minSize: 50,
|
||||
size: 80,
|
||||
},
|
||||
{
|
||||
accessor: 'objid',
|
||||
Header: gettext('Object ID'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 50,
|
||||
width: 80,
|
||||
|
||||
accessorKey: 'objid',
|
||||
header: gettext('Object ID'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
minSize: 50,
|
||||
size: 80,
|
||||
},
|
||||
{
|
||||
accessor: 'virtualtransaction',
|
||||
Header: gettext('vXID (owner)'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 50,
|
||||
accessorKey: 'virtualtransaction',
|
||||
header: gettext('vXID (owner)'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
minSize: 50,
|
||||
size: 50,
|
||||
},
|
||||
{
|
||||
accessor: 'mode',
|
||||
Header: gettext('Mode'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
accessorKey: 'mode',
|
||||
header: gettext('Mode'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
minSize: 50,
|
||||
size: 50,
|
||||
},
|
||||
{
|
||||
id: 'granted',
|
||||
accessor: 'granted',
|
||||
Header: gettext('Granted?'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 30,
|
||||
width: 80,
|
||||
Cell: ({ value }) => String(value)
|
||||
accessorKey: 'granted',
|
||||
header: gettext('Granted?'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
minSize: 50,
|
||||
size: 80,
|
||||
cell: ({ value }) => String(value)
|
||||
},
|
||||
];
|
||||
|
||||
const databasePreparedColumns = [
|
||||
{
|
||||
accessor: 'git',
|
||||
Header: gettext('Name'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
accessorKey: 'git',
|
||||
header: gettext('Name'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
},
|
||||
{
|
||||
accessor: 'datname',
|
||||
Header: gettext('Database'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
accessorKey: 'datname',
|
||||
header: gettext('Database'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableVisibility: !did,
|
||||
enableFilters: true,
|
||||
minWidth: 26,
|
||||
width: 80,
|
||||
isVisible: !did
|
||||
},
|
||||
{
|
||||
accessor: 'Owner',
|
||||
Header: gettext('Owner'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
accessorKey: 'Owner',
|
||||
header: gettext('Owner'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
},
|
||||
{
|
||||
accessor: 'transaction',
|
||||
Header: gettext('XID'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
accessorKey: 'transaction',
|
||||
header: gettext('XID'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
},
|
||||
{
|
||||
accessor: 'prepared',
|
||||
Header: gettext('Prepared at'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
accessorKey: 'prepared',
|
||||
header: gettext('Prepared at'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
},
|
||||
];
|
||||
|
||||
@ -888,15 +879,17 @@ function Dashboard({
|
||||
<TabPanel value={tabVal} index={0} classNameRoot={classes.tabPanel}>
|
||||
<PgTable
|
||||
caveTable={false}
|
||||
tableNoBorder={false}
|
||||
CustomHeader={CustomActiveOnlyHeader}
|
||||
columns={activityColumns}
|
||||
data={filteredDashData}
|
||||
schema={schemaDict}
|
||||
schema={activeQSchemaObj}
|
||||
></PgTable>
|
||||
</TabPanel>
|
||||
<TabPanel value={tabVal} index={1} classNameRoot={classes.tabPanel}>
|
||||
<PgTable
|
||||
caveTable={false}
|
||||
tableNoBorder={false}
|
||||
columns={databaseLocksColumns}
|
||||
data={dashData}
|
||||
></PgTable>
|
||||
@ -904,6 +897,7 @@ function Dashboard({
|
||||
<TabPanel value={tabVal} index={2} classNameRoot={classes.tabPanel}>
|
||||
<PgTable
|
||||
caveTable={false}
|
||||
tableNoBorder={false}
|
||||
columns={databasePreparedColumns}
|
||||
data={dashData}
|
||||
></PgTable>
|
||||
@ -911,6 +905,7 @@ function Dashboard({
|
||||
<TabPanel value={tabVal} index={3} classNameRoot={classes.tabPanel}>
|
||||
<PgTable
|
||||
caveTable={false}
|
||||
tableNoBorder={false}
|
||||
columns={serverConfigColumns}
|
||||
data={dashData}
|
||||
></PgTable>
|
||||
|
@ -17,134 +17,132 @@ import getApiInstance, { parseApiError } from '../../../../static/js/api_instanc
|
||||
import SectionContainer from '../components/SectionContainer';
|
||||
import ReplicationStatsSchema from './replication_stats.ui';
|
||||
import RefreshButton from '../components/RefreshButtons';
|
||||
import { getExpandCell, getSwitchCell } from '../../../../static/js/components/PgTable';
|
||||
import { getExpandCell, getSwitchCell } from '../../../../static/js/components/PgReactTableStyled';
|
||||
import { usePgAdmin } from '../../../../static/js/BrowserComponent';
|
||||
import url_for from 'sources/url_for';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
|
||||
const replicationStatsColumns = [{
|
||||
accessor: 'view_details',
|
||||
Header: () => null,
|
||||
sortable: false,
|
||||
resizable: false,
|
||||
disableGlobalFilter: false,
|
||||
disableResizing: true,
|
||||
width: 35,
|
||||
maxWidth: 35,
|
||||
minWidth: 35,
|
||||
accessorKey: 'view_details',
|
||||
header: () => null,
|
||||
enableSorting: false,
|
||||
enableResizing: false,
|
||||
enableFilters: true,
|
||||
size: 35,
|
||||
maxSize: 35,
|
||||
minSize: 35,
|
||||
id: 'btn-edit',
|
||||
Cell: getExpandCell({
|
||||
cell: getExpandCell({
|
||||
title: gettext('View details')
|
||||
}),
|
||||
},
|
||||
{
|
||||
accessor: 'pid',
|
||||
Header: gettext('PID'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 26,
|
||||
width: 40,
|
||||
accessorKey: 'pid',
|
||||
header: gettext('PID'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
size: 40,
|
||||
minSize: 40,
|
||||
},
|
||||
{
|
||||
accessor: 'client_addr',
|
||||
Header: gettext('Client Addr'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 26,
|
||||
width: 60,
|
||||
accessorKey: 'client_addr',
|
||||
header: gettext('Client Addr'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
size: 100,
|
||||
minSize: 50,
|
||||
},
|
||||
{
|
||||
accessor:'state',
|
||||
Header: gettext('State'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 26,
|
||||
width: 60
|
||||
accessorKey:'state',
|
||||
header: gettext('State'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
size: 100,
|
||||
minSize: 50,
|
||||
},
|
||||
{
|
||||
accessor:'write_lag',
|
||||
Header: gettext('Write Lag'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 26,
|
||||
width: 60
|
||||
accessorKey:'write_lag',
|
||||
header: gettext('Write Lag'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
size: 100,
|
||||
minSize: 50,
|
||||
},
|
||||
{
|
||||
accessor:'flush_lag',
|
||||
Header: gettext('Flush Lag'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 26,
|
||||
width: 60
|
||||
accessorKey:'flush_lag',
|
||||
header: gettext('Flush Lag'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
size: 100,
|
||||
minSize: 50,
|
||||
},
|
||||
{
|
||||
accessor:'replay_lag',
|
||||
Header: gettext('Replay Lag'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 26,
|
||||
width: 60
|
||||
accessorKey:'replay_lag',
|
||||
header: gettext('Replay Lag'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
size: 100,
|
||||
minSize: 50,
|
||||
},
|
||||
{
|
||||
accessor:'reply_time',
|
||||
Header: gettext('Reply Time'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 26,
|
||||
width: 80
|
||||
accessorKey:'reply_time',
|
||||
header: gettext('Reply Time'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
size: 100,
|
||||
minSize: 50,
|
||||
}
|
||||
];
|
||||
|
||||
const replicationSlotsColumns = [{
|
||||
accessor: 'view_details',
|
||||
Header: () => null,
|
||||
sortable: false,
|
||||
resizable: false,
|
||||
disableGlobalFilter: false,
|
||||
disableResizing: true,
|
||||
width: 35,
|
||||
maxWidth: 35,
|
||||
minWidth: 35,
|
||||
accessorKey: 'view_details',
|
||||
header: () => null,
|
||||
enableSorting: false,
|
||||
enableResizing: false,
|
||||
enableFilters: true,
|
||||
size: 35,
|
||||
maxSize: 35,
|
||||
minSize: 35,
|
||||
id: 'btn-details',
|
||||
Cell: getExpandCell({
|
||||
cell: getExpandCell({
|
||||
title: gettext('View details')
|
||||
}),
|
||||
},
|
||||
{
|
||||
accessor: 'active_pid',
|
||||
Header: gettext('Active PID'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 26,
|
||||
width: 50,
|
||||
accessorKey: 'active_pid',
|
||||
header: gettext('Active PID'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
size: 50,
|
||||
minSize: 50,
|
||||
},
|
||||
{
|
||||
accessor: 'slot_name',
|
||||
Header: gettext('Slot Name'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 26,
|
||||
width: 200,
|
||||
accessorKey: 'slot_name',
|
||||
header: gettext('Slot Name'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
size: 200,
|
||||
minSize: 50,
|
||||
},
|
||||
{
|
||||
accessor:'active',
|
||||
Header: gettext('Active'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 26,
|
||||
width: 60,
|
||||
Cell: getSwitchCell(),
|
||||
accessorKey:'active',
|
||||
header: gettext('Active'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
size: 50,
|
||||
minSize: 50,
|
||||
cell: getSwitchCell(),
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -77,25 +77,25 @@ export default function CPU({preferences, sid, did, pageVisible, enablePoll=true
|
||||
|
||||
const tableHeader = [
|
||||
{
|
||||
Header: gettext('PID'),
|
||||
accessor: 'pid',
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
header: gettext('PID'),
|
||||
accessorKey: 'pid',
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
},
|
||||
{
|
||||
Header: gettext('Name'),
|
||||
accessor: 'name',
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
header: gettext('Name'),
|
||||
accessorKey: 'name',
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
},
|
||||
{
|
||||
Header: gettext('CPU usage'),
|
||||
accessor: 'cpu_usage',
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
header: gettext('CPU usage'),
|
||||
accessorKey: 'cpu_usage',
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -75,32 +75,32 @@ export default function Memory({preferences, sid, did, pageVisible, enablePoll=t
|
||||
|
||||
const tableHeader = [
|
||||
{
|
||||
Header: gettext('PID'),
|
||||
accessor: 'pid',
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
header: gettext('PID'),
|
||||
accessorKey: 'pid',
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
},
|
||||
{
|
||||
Header: gettext('Name'),
|
||||
accessor: 'name',
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
header: gettext('Name'),
|
||||
accessorKey: 'name',
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
},
|
||||
{
|
||||
Header: gettext('Memory usage'),
|
||||
accessor: 'memory_usage',
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
header: gettext('Memory usage'),
|
||||
accessorKey: 'memory_usage',
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
},
|
||||
{
|
||||
Header: gettext('Memory bytes'),
|
||||
accessor: 'memory_bytes',
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
header: gettext('Memory bytes'),
|
||||
accessorKey: 'memory_bytes',
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -178,44 +178,44 @@ export default function Storage({preferences, sid, did, pageVisible, enablePoll=
|
||||
|
||||
const tableHeader = [
|
||||
{
|
||||
Header: gettext('File system'),
|
||||
accessor: 'file_system',
|
||||
header: gettext('File system'),
|
||||
accessorKey: 'file_system',
|
||||
},
|
||||
{
|
||||
Header: gettext('File system type'),
|
||||
accessor: 'file_system_type',
|
||||
header: gettext('File system type'),
|
||||
accessorKey: 'file_system_type',
|
||||
},
|
||||
{
|
||||
Header: gettext('Mount point'),
|
||||
accessor: 'mount_point',
|
||||
header: gettext('Mount point'),
|
||||
accessorKey: 'mount_point',
|
||||
},
|
||||
{
|
||||
Header: gettext('Drive letter'),
|
||||
accessor: 'drive_letter',
|
||||
header: gettext('Drive letter'),
|
||||
accessorKey: 'drive_letter',
|
||||
},
|
||||
{
|
||||
Header: gettext('Total space'),
|
||||
accessor: 'total_space',
|
||||
header: gettext('Total space'),
|
||||
accessorKey: 'total_space',
|
||||
},
|
||||
{
|
||||
Header: gettext('Used space'),
|
||||
accessor: 'used_space',
|
||||
header: gettext('Used space'),
|
||||
accessorKey: 'used_space',
|
||||
},
|
||||
{
|
||||
Header: gettext('Free space'),
|
||||
accessor: 'free_space',
|
||||
header: gettext('Free space'),
|
||||
accessorKey: 'free_space',
|
||||
},
|
||||
{
|
||||
Header: gettext('Total inodes'),
|
||||
accessor: 'total_inodes',
|
||||
header: gettext('Total inodes'),
|
||||
accessorKey: 'total_inodes',
|
||||
},
|
||||
{
|
||||
Header: gettext('Used inodes'),
|
||||
accessor: 'used_inodes',
|
||||
header: gettext('Used inodes'),
|
||||
accessorKey: 'used_inodes',
|
||||
},
|
||||
{
|
||||
Header: gettext('Free inodes'),
|
||||
accessor: 'free_inodes',
|
||||
header: gettext('Free inodes'),
|
||||
accessorKey: 'free_inodes',
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -46,7 +46,7 @@ export default function SectionContainer({title, titleExtras, children, style})
|
||||
{titleExtras}
|
||||
</div>
|
||||
</Box>
|
||||
<Box height="100%" display="flex" flexDirection="column">
|
||||
<Box height="100%" display="flex" flexDirection="column" minHeight={0}>
|
||||
{children}
|
||||
</Box>
|
||||
</Box>
|
||||
|
@ -99,7 +99,8 @@ export default function Processes() {
|
||||
const classes = useStyles();
|
||||
const pgAdmin = usePgAdmin();
|
||||
const [tableData, setTableData] = React.useState([]);
|
||||
const [selectedRows, setSelectedRows] = React.useState([]);
|
||||
const [selectedRows, setSelectedRows] = React.useState({});
|
||||
const selectedRowIDs = useMemo(()=>Object.keys(selectedRows).filter((k)=>selectedRows[k]), [selectedRows]);
|
||||
|
||||
const onViewDetailsClick = useCallback((p)=>{
|
||||
const panelTitle = gettext('Process Watcher - %s', p.type_desc);
|
||||
@ -123,7 +124,7 @@ export default function Processes() {
|
||||
row: PropTypes.any,
|
||||
};
|
||||
|
||||
const CancelCell = ({ row }) => {
|
||||
const CancelCell = ({row}) => {
|
||||
return (
|
||||
<PgIconButton
|
||||
size="xs"
|
||||
@ -170,93 +171,91 @@ export default function Processes() {
|
||||
StatusCell.propTypes = cellPropTypes;
|
||||
|
||||
return [{
|
||||
accessor: 'stop_process',
|
||||
Header: () => null,
|
||||
sortable: false,
|
||||
resizable: false,
|
||||
disableGlobalFilter: true,
|
||||
width: 35,
|
||||
maxWidth: 35,
|
||||
minWidth: 35,
|
||||
header: () => null,
|
||||
enableSorting: false,
|
||||
enableResizing: false,
|
||||
enableFilters: false,
|
||||
size: 35,
|
||||
maxSize: 35,
|
||||
minSize: 35,
|
||||
id: 'btn-stop',
|
||||
Cell: CancelCell,
|
||||
cell: CancelCell,
|
||||
},
|
||||
{
|
||||
accessor: 'view_details',
|
||||
Header: () => null,
|
||||
sortable: false,
|
||||
resizable: false,
|
||||
disableGlobalFilter: true,
|
||||
width: 35,
|
||||
maxWidth: 35,
|
||||
minWidth: 35,
|
||||
header: () => null,
|
||||
enableSorting: false,
|
||||
enableResizing: false,
|
||||
enableFilters: false,
|
||||
size: 35,
|
||||
maxSize: 35,
|
||||
minSize: 35,
|
||||
id: 'btn-logs',
|
||||
Cell: LogsCell,
|
||||
cell: LogsCell,
|
||||
},
|
||||
{
|
||||
Header: gettext('PID'),
|
||||
accessor: 'utility_pid',
|
||||
sortable: true,
|
||||
resizable: false,
|
||||
header: gettext('PID'),
|
||||
accessorKey: 'utility_pid',
|
||||
enableSorting: true,
|
||||
enableResizing: false,
|
||||
width: 70,
|
||||
minWidth: 70,
|
||||
disableGlobalFilter: false,
|
||||
enableFilters: true,
|
||||
},
|
||||
{
|
||||
Header: gettext('Type'),
|
||||
accessor: (row)=>row.details?.type,
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
header: gettext('Type'),
|
||||
accessorFn: (row)=>row.details?.type,
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
width: 100,
|
||||
minWidth: 70,
|
||||
disableGlobalFilter: false,
|
||||
enableFilters: true,
|
||||
},
|
||||
{
|
||||
Header: gettext('Server'),
|
||||
accessor: (row)=>row.details?.server,
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
header: gettext('Server'),
|
||||
accessorFn: (row)=>row.details?.server,
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
width: 200,
|
||||
minWidth: 120,
|
||||
disableGlobalFilter: false,
|
||||
enableFilters: true,
|
||||
},
|
||||
{
|
||||
Header: gettext('Object'),
|
||||
accessor: (row)=>row.details?.object,
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
header: gettext('Object'),
|
||||
accessorFn: (row)=>row.details?.object,
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
width: 200,
|
||||
minWidth: 120,
|
||||
disableGlobalFilter: false,
|
||||
enableFilters: true,
|
||||
},
|
||||
{
|
||||
id: 'stime',
|
||||
Header: gettext('Start Time'),
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: true,
|
||||
header: gettext('Start Time'),
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: false,
|
||||
width: 150,
|
||||
minWidth: 150,
|
||||
accessor: (row)=>(new Date(row.stime)),
|
||||
Cell: ({row})=>(new Date(row.original.stime).toLocaleString()),
|
||||
accessorFn: (row)=>(new Date(row.stime)),
|
||||
cell: (info)=>(info.getValue().toLocaleString()),
|
||||
},
|
||||
{
|
||||
Header: gettext('Status'),
|
||||
sortable: true,
|
||||
resizable: false,
|
||||
disableGlobalFilter: false,
|
||||
header: gettext('Status'),
|
||||
enableSorting: true,
|
||||
enableResizing: false,
|
||||
enableFilters: true,
|
||||
width: 120,
|
||||
minWidth: 120,
|
||||
accessor: (row)=>ProcessStateTextAndColor[row.process_state][0],
|
||||
accessorFn: (row)=>ProcessStateTextAndColor[row.process_state][0],
|
||||
dataClassName: classes.noPadding,
|
||||
Cell: StatusCell,
|
||||
cell: StatusCell,
|
||||
},
|
||||
{
|
||||
Header: gettext('Time Taken (sec)'),
|
||||
accessor: 'execution_time',
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: true,
|
||||
header: gettext('Time Taken (sec)'),
|
||||
accessorKey: 'execution_time',
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: false,
|
||||
}];
|
||||
}, []);
|
||||
|
||||
@ -281,10 +280,10 @@ export default function Processes() {
|
||||
columns={columns}
|
||||
data={tableData}
|
||||
sortOptions={[{id: 'stime', desc: true}]}
|
||||
getSelectedRows={(rows)=>{setSelectedRows(rows);}}
|
||||
isSelectRow={true}
|
||||
selectedRows={selectedRows}
|
||||
setSelectedRows={setSelectedRows}
|
||||
hasSelectRow={true}
|
||||
tableProps={{
|
||||
autoResetSelectedRows: false,
|
||||
getRowId: (row)=>{
|
||||
return row.id;
|
||||
}
|
||||
@ -299,10 +298,11 @@ export default function Processes() {
|
||||
title={gettext('Acknowledge and Remove')}
|
||||
onClick={() => {
|
||||
pgAdmin.Browser.notifier.confirm(gettext('Remove Processes'), gettext('Are you sure you want to remove the selected processes?'), ()=>{
|
||||
pgAdmin.Browser.BgProcessManager.acknowledge(selectedRows.map((p)=>p.original.id));
|
||||
pgAdmin.Browser.BgProcessManager.acknowledge(selectedRowIDs);
|
||||
setSelectedRows({});
|
||||
});
|
||||
}}
|
||||
disabled={selectedRows.length <= 0}
|
||||
disabled={selectedRowIDs.length <= 0}
|
||||
></PgIconButton>
|
||||
<PgIconButton
|
||||
icon={<HelpIcon style={{height: '1.4rem'}}/>}
|
||||
|
@ -83,29 +83,30 @@ function Dependencies({ nodeData, nodeItem, node, treeNodeInfo, isActive, isStal
|
||||
|
||||
let columns = [
|
||||
{
|
||||
Header: 'Type',
|
||||
accessor: 'type',
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
Cell: ({row})=>{
|
||||
return pgAdmin.Browser.Nodes?.[row.original.type]?.label??row.original.type;
|
||||
header: 'Type',
|
||||
accessorKey: 'type',
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
cell: (info)=>{
|
||||
const type = info.getValue();
|
||||
return pgAdmin.Browser.Nodes?.[type]?.label ?? type;
|
||||
}
|
||||
},
|
||||
{
|
||||
Header: 'Name',
|
||||
accessor: 'name',
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
header: 'Name',
|
||||
accessorKey: 'name',
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
},
|
||||
{
|
||||
Header: 'Restriction',
|
||||
accessor: 'field',
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 280,
|
||||
header: 'Restriction',
|
||||
accessorKey: 'field',
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
minSize: 280,
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -83,29 +83,30 @@ function Dependents({ nodeData, nodeItem, node, treeNodeInfo, isActive, isStale,
|
||||
|
||||
let columns = [
|
||||
{
|
||||
Header: 'Type',
|
||||
accessor: 'type',
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
Cell: ({row})=>{
|
||||
return pgAdmin.Browser.Nodes?.[row.original.type]?.label??row.original.type;
|
||||
header: 'Type',
|
||||
accessorKey: 'type',
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
cell: (info)=>{
|
||||
const type = info.getValue();
|
||||
return pgAdmin.Browser.Nodes?.[type]?.label ?? type;
|
||||
}
|
||||
},
|
||||
{
|
||||
Header: 'Name',
|
||||
accessor: 'name',
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
header: 'Name',
|
||||
accessorKey: 'name',
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
},
|
||||
{
|
||||
Header: 'Restriction',
|
||||
accessor: 'field',
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 280,
|
||||
header: 'Restriction',
|
||||
accessorKey: 'field',
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
minSize: 280,
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -23,7 +23,7 @@ import EmptyPanelMessage from '../../static/js/components/EmptyPanelMessage';
|
||||
import Loader from 'sources/components/Loader';
|
||||
import { evalFunc } from '../../static/js/utils';
|
||||
import { usePgAdmin } from '../../static/js/BrowserComponent';
|
||||
import { getSwitchCell } from '../../static/js/components/PgTable';
|
||||
import { getSwitchCell } from '../../static/js/components/PgReactTableStyled';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
emptyPanel: {
|
||||
@ -81,48 +81,38 @@ export default function CollectionNodeProperties({
|
||||
|
||||
const [data, setData] = React.useState([]);
|
||||
const [infoMsg, setInfoMsg] = React.useState('Please select an object in the tree view.');
|
||||
const [selectedObject, setSelectedObject] = React.useState([]);
|
||||
const [selectedObject, setSelectedObject] = React.useState({});
|
||||
const [loaderText, setLoaderText] = React.useState('');
|
||||
const schemaRef = React.useRef();
|
||||
|
||||
const [pgTableColumns, setPgTableColumns] = React.useState([
|
||||
{
|
||||
Header: 'properties',
|
||||
accessor: 'Properties',
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
header: 'properties',
|
||||
accessorKey: 'Properties',
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
},
|
||||
{
|
||||
Header: 'value',
|
||||
accessor: 'value',
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
header: 'value',
|
||||
accessorKey: 'value',
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
},
|
||||
]);
|
||||
|
||||
const getTableSelectedRows = (selRows) => {
|
||||
setSelectedObject(selRows);
|
||||
};
|
||||
|
||||
const onDrop = (type) => {
|
||||
let selRowModels = selectedObject,
|
||||
selRows = [],
|
||||
let selRows = [],
|
||||
selItem = pgAdmin.Browser.tree.selected(),
|
||||
selectedItemData = selItem ? pgAdmin.Browser.tree.itemData(selItem) : null,
|
||||
selNode = selectedItemData && pgAdmin.Browser.Nodes[selectedItemData._type],
|
||||
url, msg, title;
|
||||
|
||||
if (selNode?.type == 'coll-constraints') {
|
||||
// In order to identify the constraint type, the type should be passed to the server
|
||||
selRows = selRowModels.map((row) => ({
|
||||
id: row.original.oid,
|
||||
_type: row.original._type,
|
||||
}));
|
||||
} else {
|
||||
selRows = selRowModels.map((row) => row.original[schemaRef.current.idAttribute]);
|
||||
}
|
||||
selRows = Object.keys(selectedObject).map((i)=>(selNode?.type == 'coll-constraints' ? {
|
||||
id: data[i].oid,
|
||||
_type: data[i]._type,
|
||||
} : data[i][schemaRef.current.idAttribute]));
|
||||
|
||||
if (selRows.length === 0) {
|
||||
pgAdmin.Browser.notifier.alert(
|
||||
@ -166,6 +156,7 @@ export default function CollectionNodeProperties({
|
||||
}
|
||||
pgAdmin.Browser.tree.refresh(selItem);
|
||||
setIsStale(true);
|
||||
setSelectedObject({});
|
||||
})
|
||||
.catch(function (error) {
|
||||
pgAdmin.Browser.notifier.alert(
|
||||
@ -209,22 +200,20 @@ export default function CollectionNodeProperties({
|
||||
if (node.columns.indexOf(field.id) > -1) {
|
||||
if (field.label.indexOf('?') > -1) {
|
||||
column = {
|
||||
Header: field.label,
|
||||
accessor: field.id,
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 0,
|
||||
Cell: getSwitchCell()
|
||||
header: field.label,
|
||||
accessorKey: field.id,
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
cell: getSwitchCell()
|
||||
};
|
||||
} else {
|
||||
column = {
|
||||
Header: field.label,
|
||||
accessor: field.id,
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 0,
|
||||
header: field.label,
|
||||
accessorKey: field.id,
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
};
|
||||
}
|
||||
tableColumns.push(column);
|
||||
@ -233,12 +222,11 @@ export default function CollectionNodeProperties({
|
||||
}else{
|
||||
node.columns.forEach((field) => {
|
||||
column = {
|
||||
Header: field,
|
||||
accessor: field,
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 0,
|
||||
header: field,
|
||||
accessorKey: field,
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
};
|
||||
tableColumns.push(column);
|
||||
});
|
||||
@ -283,7 +271,7 @@ export default function CollectionNodeProperties({
|
||||
onDrop('drop');
|
||||
}}
|
||||
disabled={
|
||||
(selectedObject.length > 0)
|
||||
(Object.keys(selectedObject).length > 0)
|
||||
? !canDrop
|
||||
: true
|
||||
}
|
||||
@ -296,7 +284,7 @@ export default function CollectionNodeProperties({
|
||||
onDrop('dropCascade');
|
||||
}}
|
||||
disabled={
|
||||
(selectedObject.length > 0)
|
||||
(Object.keys(selectedObject).length > 0)
|
||||
? !canDropCascade
|
||||
: true
|
||||
}
|
||||
@ -309,7 +297,7 @@ export default function CollectionNodeProperties({
|
||||
onDrop('dropForce');
|
||||
}}
|
||||
disabled={
|
||||
(selectedObject.length > 0)
|
||||
(Object.keys(selectedObject).length > 0)
|
||||
? !canDropForce
|
||||
: true
|
||||
}
|
||||
@ -325,14 +313,15 @@ export default function CollectionNodeProperties({
|
||||
{data.length > 0 ?
|
||||
(
|
||||
<PgTable
|
||||
isSelectRow={!('catalog' in treeNodeInfo) && (nodeData.label !== 'Catalogs') && _.isUndefined(node?.canSelect)}
|
||||
hasSelectRow={!('catalog' in treeNodeInfo) && (nodeData.label !== 'Catalogs') && _.isUndefined(node?.canSelect)}
|
||||
CustomHeader={CustomHeader}
|
||||
className={classes.autoResizer}
|
||||
columns={pgTableColumns}
|
||||
data={data}
|
||||
type={'panel'}
|
||||
isSearch={false}
|
||||
getSelectedRows={getTableSelectedRows}
|
||||
selectedRows={selectedObject}
|
||||
setSelectedRows={setSelectedObject}
|
||||
/>
|
||||
)
|
||||
:
|
||||
|
@ -60,29 +60,29 @@ function getColumn(data, singleLineStatistics, prettifyFields=[]) {
|
||||
if (!_.isUndefined(data)) {
|
||||
data.forEach((row) => {
|
||||
columns.push({
|
||||
Header: row.name,
|
||||
accessor: row.name,
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
header: row.name,
|
||||
accessorKey: row.name,
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
columns = [
|
||||
{
|
||||
Header: gettext('Statistics'),
|
||||
accessor: 'name',
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
header: gettext('Statistics'),
|
||||
accessorKey: 'name',
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
},
|
||||
{
|
||||
Header: 'Value',
|
||||
accessor: 'value',
|
||||
sortable: false,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
header: 'Value',
|
||||
accessorKey: 'value',
|
||||
enableSorting: false,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
},
|
||||
];
|
||||
}
|
||||
@ -153,18 +153,18 @@ function Statistics({ nodeData, nodeItem, node, treeNodeInfo, isActive, isStale,
|
||||
const [loaderText, setLoaderText] = React.useState('');
|
||||
const [columns, setColumns] = React.useState([
|
||||
{
|
||||
Header: 'Statictics',
|
||||
accessor: 'name',
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
header: 'Statictics',
|
||||
accessorKey: 'name',
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
},
|
||||
{
|
||||
Header: 'Value',
|
||||
accessor: 'value',
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
header: 'Value',
|
||||
accessorKey: 'value',
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
},
|
||||
]);
|
||||
const pgAdmin = usePgAdmin();
|
||||
|
@ -18,8 +18,15 @@ import { MappedCellControl } from './MappedControl';
|
||||
import DragIndicatorRoundedIcon from '@mui/icons-material/DragIndicatorRounded';
|
||||
import EditRoundedIcon from '@mui/icons-material/EditRounded';
|
||||
import DeleteRoundedIcon from '@mui/icons-material/DeleteRounded';
|
||||
import { useTable, useFlexLayout, useResizeColumns, useSortBy, useExpanded, useGlobalFilter } from 'react-table';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import {
|
||||
useReactTable,
|
||||
getCoreRowModel,
|
||||
getSortedRowModel,
|
||||
getFilteredRowModel,
|
||||
getExpandedRowModel,
|
||||
flexRender,
|
||||
} from '@tanstack/react-table';
|
||||
import PropTypes from 'prop-types';
|
||||
import _ from 'lodash';
|
||||
import { DndProvider, useDrag, useDrop } from 'react-dnd';
|
||||
@ -35,6 +42,7 @@ import { useIsMounted } from '../custom_hooks';
|
||||
import { InputText } from '../components/FormComponents';
|
||||
import { usePgAdmin } from '../BrowserComponent';
|
||||
import { requestAnimationAndFocus } from '../utils';
|
||||
import { PgReactTable, PgReactTableBody, PgReactTableCell, PgReactTableHeader, PgReactTableRow, PgReactTableRowContent, PgReactTableRowExpandContent } from '../components/PgReactTableStyled';
|
||||
|
||||
const useStyles = makeStyles((theme)=>({
|
||||
grid: {
|
||||
@ -73,10 +81,25 @@ const useStyles = makeStyles((theme)=>({
|
||||
width: '100%',
|
||||
},
|
||||
table: {
|
||||
borderSpacing: 0,
|
||||
width: '100%',
|
||||
overflow: 'auto',
|
||||
backgroundColor: theme.otherVars.tableBg,
|
||||
'&.pgrt-table': {
|
||||
'& .pgrt-body':{
|
||||
'& .pgrt-row': {
|
||||
position: 'unset',
|
||||
backgroundColor: theme.otherVars.emptySpaceBg,
|
||||
|
||||
'& .pgrt-row-content':{
|
||||
'& .pgrd-row-cell': {
|
||||
height: 'auto',
|
||||
padding: theme.spacing(0.5),
|
||||
|
||||
'&.btn-cell, &.expanded-icon-cell': {
|
||||
padding: '2px 0px'
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
tableRowHovered: {
|
||||
position: 'relative',
|
||||
@ -87,16 +110,6 @@ const useStyles = makeStyles((theme)=>({
|
||||
opacity: 0.75,
|
||||
}
|
||||
},
|
||||
tableCell: {
|
||||
margin: 0,
|
||||
padding: theme.spacing(0.5),
|
||||
...theme.mixins.panelBorder.bottom,
|
||||
...theme.mixins.panelBorder.right,
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
},
|
||||
tableCellHeader: {
|
||||
fontWeight: theme.typography.fontWeightBold,
|
||||
padding: theme.spacing(1, 0.5),
|
||||
@ -111,6 +124,7 @@ const useStyles = makeStyles((theme)=>({
|
||||
},
|
||||
btnReorder: {
|
||||
cursor: 'move',
|
||||
padding: '4px 2px',
|
||||
},
|
||||
resizer: {
|
||||
display: 'inline-block',
|
||||
@ -124,9 +138,7 @@ const useStyles = makeStyles((theme)=>({
|
||||
touchAction: 'none',
|
||||
},
|
||||
expandedForm: {
|
||||
borderTopWidth: theme.spacing(0.5),
|
||||
borderStyle: 'solid ',
|
||||
borderColor: theme.palette.grey[400],
|
||||
border: '1px solid '+theme.palette.grey[400],
|
||||
},
|
||||
expandedIconCell: {
|
||||
backgroundColor: theme.palette.grey[400],
|
||||
@ -134,49 +146,6 @@ const useStyles = makeStyles((theme)=>({
|
||||
}
|
||||
}));
|
||||
|
||||
function DataTableHeader({headerGroups, viewHelperProps, schema}) {
|
||||
const classes = useStyles();
|
||||
|
||||
/* Using ref so that schema variable is not frozen in columns closure */
|
||||
const schemaRef = useRef(schema);
|
||||
|
||||
const sortIcon = (isDesc) => {
|
||||
return isDesc ? ' 🔽' : ' 🔼';
|
||||
};
|
||||
return (
|
||||
<div className={classes.tableContentWidth}>
|
||||
{headerGroups.map((headerGroup, hi) => (
|
||||
<div key={hi} {...headerGroup.getHeaderGroupProps()}>
|
||||
{headerGroup.headers.map((column, ci) => {
|
||||
let {modeSupported} = column.field ? getFieldMetaData(column.field, schemaRef.current, {}, viewHelperProps) : {modeSupported: true};
|
||||
return( modeSupported &&
|
||||
<div key={ci} {...column.getHeaderProps()}>
|
||||
<div {...(column.sortable ? column.getSortByToggleProps() : {})} className={clsx(classes.tableCell, classes.tableCellHeader)}>
|
||||
{column.render('Header')}
|
||||
<span>
|
||||
{column.isSorted ? sortIcon(column.isSortedDesc) : ''}
|
||||
</span>
|
||||
</div>
|
||||
{!column.disableResizing &&
|
||||
<div
|
||||
{...column.getResizerProps()}
|
||||
className={classes.resizer}
|
||||
/>}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
DataTableHeader.propTypes = {
|
||||
headerGroups: PropTypes.array.isRequired,
|
||||
viewHelperProps: PropTypes.object.isRequired,
|
||||
schema: CustomPropTypes.schemaUI.isRequired,
|
||||
};
|
||||
|
||||
function DataTableRow({index, row, totalRows, isResizing, isHovered, schema, schemaRef, accessPath, moveRow, setHoverIndex, viewHelperProps}) {
|
||||
const classes = useStyles();
|
||||
const [key, setKey] = useState(false);
|
||||
@ -188,7 +157,7 @@ function DataTableRow({index, row, totalRows, isResizing, isHovered, schema, sch
|
||||
* If table data changes, then react-table re-renders the complete tables
|
||||
* We can avoid re-render by if row data is not changed
|
||||
*/
|
||||
let depsMap = _.values(row.values, Object.keys(row.values).filter((k)=>!k.startsWith('btn')));
|
||||
let depsMap = _.values(row.original, Object.keys(row.original).filter((k)=>!k.startsWith('btn')));
|
||||
const externalDeps = useMemo(()=>{
|
||||
let retVal = [];
|
||||
/* Calculate the fields which depends on the current field
|
||||
@ -284,37 +253,30 @@ function DataTableRow({index, row, totalRows, isResizing, isHovered, schema, sch
|
||||
|
||||
/* External deps values are from top schema sess data */
|
||||
depsMap = depsMap.concat(externalDeps.map((source)=>_.get(schemaRef.current.top?.sessData, source)));
|
||||
depsMap = depsMap.concat([totalRows, row.isExpanded, key, isResizing, isHovered]);
|
||||
depsMap = depsMap.concat([totalRows, row.getIsExpanded(), key, isResizing, isHovered]);
|
||||
|
||||
drag(dragHandleRef);
|
||||
drop(rowRef);
|
||||
|
||||
return useMemo(()=>
|
||||
<div {...row.getRowProps()} ref={rowRef} data-handler-id={handlerId}
|
||||
className={isHovered ? classes.tableRowHovered : null}
|
||||
data-test='data-table-row'
|
||||
>
|
||||
{row.cells.map((cell, ci) => {
|
||||
let classNames = [classes.tableCell];
|
||||
<PgReactTableRowContent ref={rowRef} row={row} data-handler-id={handlerId} className={isHovered ? classes.tableRowHovered : null} data-test='data-table-row' style={{position: 'initial'}}>
|
||||
{row.getVisibleCells().map((cell) => {
|
||||
let {modeSupported} = cell.column.field ? getFieldMetaData(cell.column.field, schemaRef.current, {}, viewHelperProps) : {modeSupported: true};
|
||||
|
||||
let {modeSupported} = cell.column.field? getFieldMetaData(cell.column.field, schemaRef.current, {}, viewHelperProps) : {modeSupported: true};
|
||||
const content = flexRender(cell.column.columnDef.cell, {
|
||||
key: cell.column.columnDef.cell.type,
|
||||
...cell.getContext(),
|
||||
reRenderRow: ()=>{setKey((currKey)=>!currKey);}
|
||||
});
|
||||
|
||||
if(typeof(cell.column.id) == 'string' && cell.column.id.startsWith('btn-')) {
|
||||
classNames.push(classes.btnCell);
|
||||
}
|
||||
if(cell.column.id == 'btn-edit' && row.isExpanded) {
|
||||
classNames.push(classes.expandedIconCell);
|
||||
}
|
||||
return (modeSupported &&
|
||||
<div ref={cell.column.id == 'btn-reorder' ? dragHandleRef : null} key={ci} {...cell.getCellProps()} className={clsx(classNames)}>
|
||||
{cell.render('Cell', {
|
||||
reRenderRow: ()=>{setKey((currKey)=>!currKey);}
|
||||
})}
|
||||
</div>
|
||||
<PgReactTableCell cell={cell} row={row} key={cell.id} ref={cell.column.id == 'btn-reorder' ? dragHandleRef : null}>
|
||||
{content}
|
||||
</PgReactTableCell>
|
||||
);
|
||||
})}
|
||||
<div className='hover-overlay'></div>
|
||||
</div>, depsMap);
|
||||
</PgReactTableRowContent>, depsMap);
|
||||
}
|
||||
|
||||
export function DataGridHeader({label, canAdd, onAddClick, canSearch, onSearchTextChange}) {
|
||||
@ -355,7 +317,7 @@ DataGridHeader.propTypes = {
|
||||
onSearchTextChange: PropTypes.func,
|
||||
};
|
||||
|
||||
export default function DataGridView({
|
||||
function DataGridView({
|
||||
value, viewHelperProps, schema, accessPath, dataDispatch, containerClassName,
|
||||
fixedRows, ...props}) {
|
||||
const classes = useStyles();
|
||||
@ -364,76 +326,77 @@ export default function DataGridView({
|
||||
const [hoverIndex, setHoverIndex] = useState();
|
||||
const newRowIndex = useRef();
|
||||
const pgAdmin = usePgAdmin();
|
||||
const [searchVal, setSearchVal] = useState('');
|
||||
|
||||
/* Using ref so that schema variable is not frozen in columns closure */
|
||||
const schemaRef = useRef(schema);
|
||||
let columns = useMemo(
|
||||
const columns = useMemo(
|
||||
()=>{
|
||||
let cols = [];
|
||||
if(props.canReorder) {
|
||||
let colInfo = {
|
||||
Header: <> </>,
|
||||
header: <> </>,
|
||||
id: 'btn-reorder',
|
||||
accessor: ()=>{/*This is intentional (SonarQube)*/},
|
||||
disableResizing: true,
|
||||
sortable: false,
|
||||
accessorFn: ()=>{/*This is intentional (SonarQube)*/},
|
||||
enableResizing: false,
|
||||
enableSorting: false,
|
||||
dataType: 'reorder',
|
||||
width: 26,
|
||||
minWidth: 26,
|
||||
maxWidth: 26,
|
||||
Cell: ()=>{
|
||||
size: 36,
|
||||
maxSize: 26,
|
||||
minSize: 26,
|
||||
cell: ()=>{
|
||||
return <div className={classes.btnReorder}>
|
||||
<DragIndicatorRoundedIcon fontSize="small" />
|
||||
</div>;
|
||||
}
|
||||
};
|
||||
colInfo.Cell.displayName = 'Cell';
|
||||
colInfo.cell.displayName = 'Cell';
|
||||
cols.push(colInfo);
|
||||
}
|
||||
if(props.canEdit) {
|
||||
let colInfo = {
|
||||
Header: <> </>,
|
||||
header: <> </>,
|
||||
id: 'btn-edit',
|
||||
accessor: ()=>{/*This is intentional (SonarQube)*/},
|
||||
disableResizing: true,
|
||||
sortable: false,
|
||||
accessorFn: ()=>{/*This is intentional (SonarQube)*/},
|
||||
enableResizing: false,
|
||||
enableSorting: false,
|
||||
dataType: 'edit',
|
||||
width: 26,
|
||||
minWidth: 26,
|
||||
maxWidth: 26,
|
||||
Cell: ({row})=>{
|
||||
size: 26,
|
||||
maxSize: 26,
|
||||
minSize: 26,
|
||||
cell: ({row})=>{
|
||||
let canEditRow = true;
|
||||
if(props.canEditRow) {
|
||||
canEditRow = evalFunc(schemaRef.current, props.canEditRow, row.original || {});
|
||||
canEditRow = evalFunc(schemaRef.current, props.canEditRow, row || {});
|
||||
}
|
||||
return <PgIconButton data-test="expand-row" title={gettext('Edit row')} icon={<EditRoundedIcon fontSize="small" />} className={classes.gridRowButton}
|
||||
onClick={()=>{
|
||||
row.toggleRowExpanded(!row.isExpanded);
|
||||
row.toggleExpanded();
|
||||
}} disabled={!canEditRow}
|
||||
/>;
|
||||
}
|
||||
};
|
||||
colInfo.Cell.displayName = 'Cell';
|
||||
colInfo.Cell.propTypes = {
|
||||
colInfo.cell.displayName = 'Cell';
|
||||
colInfo.cell.propTypes = {
|
||||
row: PropTypes.object.isRequired,
|
||||
};
|
||||
cols.push(colInfo);
|
||||
}
|
||||
if(props.canDelete) {
|
||||
let colInfo = {
|
||||
Header: <> </>,
|
||||
header: <> </>,
|
||||
id: 'btn-delete',
|
||||
accessor: ()=>{/*This is intentional (SonarQube)*/},
|
||||
disableResizing: true,
|
||||
sortable: false,
|
||||
accessorFn: ()=>{/*This is intentional (SonarQube)*/},
|
||||
enableResizing: false,
|
||||
enableSorting: false,
|
||||
dataType: 'delete',
|
||||
width: 26,
|
||||
minWidth: 26,
|
||||
maxWidth: 26,
|
||||
Cell: ({row}) => {
|
||||
size: 26,
|
||||
maxSize: 26,
|
||||
minSize: 26,
|
||||
cell: ({row}) => {
|
||||
let canDeleteRow = true;
|
||||
if(props.canDeleteRow) {
|
||||
canDeleteRow = evalFunc(schemaRef.current, props.canDeleteRow, row.original || {});
|
||||
canDeleteRow = evalFunc(schemaRef.current, props.canDeleteRow, row || {});
|
||||
}
|
||||
|
||||
return (
|
||||
@ -449,7 +412,7 @@ export default function DataGridView({
|
||||
};
|
||||
|
||||
if (props.onDelete){
|
||||
props.onDelete(row.original || {}, deleteRow);
|
||||
props.onDelete(row || {}, deleteRow);
|
||||
} else {
|
||||
pgAdmin.Browser.notifier.confirm(
|
||||
props.customDeleteTitle || gettext('Delete Row'),
|
||||
@ -464,8 +427,8 @@ export default function DataGridView({
|
||||
);
|
||||
}
|
||||
};
|
||||
colInfo.Cell.displayName = 'Cell';
|
||||
colInfo.Cell.propTypes = {
|
||||
colInfo.cell.displayName = 'Cell';
|
||||
colInfo.cell.propTypes = {
|
||||
row: PropTypes.object.isRequired,
|
||||
};
|
||||
cols.push(colInfo);
|
||||
@ -482,28 +445,29 @@ export default function DataGridView({
|
||||
}).map((field)=>{
|
||||
let widthParms = {};
|
||||
if(field.width) {
|
||||
widthParms.width = field.width;
|
||||
widthParms.minWidth = field.width;
|
||||
widthParms.size = field.width;
|
||||
widthParms.minSize = field.width;
|
||||
} else {
|
||||
widthParms.width = 75;
|
||||
widthParms.minWidth = 75;
|
||||
widthParms.size = 75;
|
||||
widthParms.minSize = 75;
|
||||
}
|
||||
if(field.minWidth) {
|
||||
widthParms.minWidth = field.minWidth;
|
||||
widthParms.minSize = field.minWidth;
|
||||
}
|
||||
if(field.maxWidth) {
|
||||
widthParms.maxWidth = field.maxWidth;
|
||||
widthParms.maxSize = field.maxWidth;
|
||||
}
|
||||
widthParms.disableResizing = Boolean(field.disableResizing);
|
||||
widthParms.enableResizing = _.isUndefined(field.enableResizing) ? true : Boolean(field.enableResizing);
|
||||
|
||||
let colInfo = {
|
||||
Header: field.label||<> </>,
|
||||
accessor: field.id,
|
||||
header: field.label||<> </>,
|
||||
accessorKey: field.id,
|
||||
field: field,
|
||||
disableResizing: false,
|
||||
sortable: true,
|
||||
enableResizing: true,
|
||||
enableSorting: false,
|
||||
...widthParms,
|
||||
Cell: ({value, row, ...other}) => {
|
||||
cell: ({row, ...other}) => {
|
||||
const value = other.getValue();
|
||||
/* Make sure to take the latest field info from schema */
|
||||
field = _.find(schemaRef.current.fields, (f)=>f.id==field.id) || field;
|
||||
|
||||
@ -514,7 +478,7 @@ export default function DataGridView({
|
||||
}
|
||||
|
||||
return modeSupported && <MappedCellControl rowIndex={row.index} value={value}
|
||||
row={row.original} {...field}
|
||||
row={row} {...field}
|
||||
readonly={!editable}
|
||||
disabled={disabled}
|
||||
visible={true}
|
||||
@ -537,8 +501,8 @@ export default function DataGridView({
|
||||
/>;
|
||||
},
|
||||
};
|
||||
colInfo.Cell.displayName = 'Cell';
|
||||
colInfo.Cell.propTypes = {
|
||||
colInfo.cell.displayName = 'Cell';
|
||||
colInfo.cell.propTypes = {
|
||||
row: PropTypes.object.isRequired,
|
||||
value: PropTypes.any,
|
||||
onCellChange: PropTypes.func,
|
||||
@ -567,35 +531,33 @@ export default function DataGridView({
|
||||
});
|
||||
}, [props.canAddRow, rows?.length]);
|
||||
|
||||
const defaultColumn = useMemo(()=>({
|
||||
}), []);
|
||||
const columnVisibility = useMemo(()=>{
|
||||
const ret = {};
|
||||
|
||||
let tablePlugins = [
|
||||
useGlobalFilter,
|
||||
useFlexLayout,
|
||||
useResizeColumns,
|
||||
useSortBy,
|
||||
useExpanded,
|
||||
];
|
||||
columns.forEach(column => {
|
||||
let {modeSupported} = column.field ? getFieldMetaData(column.field, schemaRef.current, {}, viewHelperProps) : {modeSupported: true};
|
||||
ret[column.id] = modeSupported;
|
||||
});
|
||||
|
||||
const {
|
||||
getTableProps,
|
||||
getTableBodyProps,
|
||||
headerGroups,
|
||||
rows,
|
||||
prepareRow,
|
||||
setGlobalFilter,
|
||||
} = useTable(
|
||||
{
|
||||
columns,
|
||||
data: value,
|
||||
defaultColumn,
|
||||
manualSortBy: true,
|
||||
autoResetSortBy: false,
|
||||
autoResetExpanded: false,
|
||||
return ret;
|
||||
}, [columns, viewHelperProps]);
|
||||
|
||||
const table = useReactTable({
|
||||
columns,
|
||||
data: value,
|
||||
autoResetAll: false,
|
||||
state: {
|
||||
globalFilter: searchVal,
|
||||
columnVisibility: columnVisibility,
|
||||
},
|
||||
...tablePlugins,
|
||||
);
|
||||
columnResizeMode: 'onChange',
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
getExpandedRowModel: getExpandedRowModel(),
|
||||
});
|
||||
|
||||
const rows = table.getRowModel().rows;
|
||||
|
||||
useEffect(()=>{
|
||||
let rowsPromise = fixedRows;
|
||||
@ -617,11 +579,13 @@ export default function DataGridView({
|
||||
|
||||
useEffect(()=>{
|
||||
if(newRowIndex.current >= 0) {
|
||||
rows[newRowIndex.current]?.toggleRowExpanded(true);
|
||||
rows[newRowIndex.current]?.toggleExpanded(true);
|
||||
newRowIndex.current = null;
|
||||
}
|
||||
}, [rows?.length]);
|
||||
|
||||
const tableRef = useRef();
|
||||
|
||||
const moveRow = (dragIndex, hoverIndex) => {
|
||||
dataDispatch({
|
||||
type: SCHEMA_STATE_ACTIONS.MOVE_ROW,
|
||||
@ -631,7 +595,7 @@ export default function DataGridView({
|
||||
});
|
||||
};
|
||||
|
||||
const isResizing = _.flatMap(headerGroups, headerGroup => headerGroup.headers.map(col=>col.isResizing)).includes(true);
|
||||
const isResizing = _.flatMap(table.getHeaderGroups(), headerGroup => headerGroup.headers.map(header=>header.column.getIsResizing())).includes(true);
|
||||
|
||||
if(!props.visible) {
|
||||
return <></>;
|
||||
@ -643,36 +607,46 @@ export default function DataGridView({
|
||||
{(props.label || props.canAdd) && <DataGridHeader label={props.label} canAdd={props.canAdd} onAddClick={onAddClick}
|
||||
canSearch={props.canSearch}
|
||||
onSearchTextChange={(value)=>{
|
||||
setGlobalFilter(value || undefined);
|
||||
setSearchVal(value || undefined);
|
||||
}}
|
||||
/>}
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<div {...getTableProps(()=>({style: {minWidth: 'unset'}}))} className={classes.table} data-test="data-grid-view">
|
||||
<DataTableHeader headerGroups={headerGroups} viewHelperProps={viewHelperProps} schema={schema} />
|
||||
<div {...getTableBodyProps()} className={classes.tableContentWidth}>
|
||||
<PgReactTable ref={tableRef} table={table} data-test="data-grid-view" tableClassName={classes.table}>
|
||||
<PgReactTableHeader table={table} />
|
||||
<PgReactTableBody>
|
||||
{rows.map((row, i) => {
|
||||
prepareRow(row);
|
||||
return <React.Fragment key={row.index}>
|
||||
<DataTableRow index={i} row={row} totalRows={rows.length} isResizing={isResizing}
|
||||
return <PgReactTableRow key={row.index}>
|
||||
<DataTableRow index={i} key={i} row={row} totalRows={rows.length} isResizing={isResizing}
|
||||
schema={schemaRef.current} schemaRef={schemaRef} accessPath={accessPath.concat([row.index])}
|
||||
moveRow={moveRow} isHovered={i == hoverIndex} setHoverIndex={setHoverIndex} viewHelperProps={viewHelperProps}/>
|
||||
{props.canEdit && row.isExpanded &&
|
||||
<FormView value={row.original} viewHelperProps={viewHelperProps} dataDispatch={dataDispatch}
|
||||
schema={schemaRef.current} accessPath={accessPath.concat([row.index])} isNested={true} className={classes.expandedForm}
|
||||
isDataGridForm={true} firstEleRef={(ele)=>{
|
||||
requestAnimationAndFocus(ele);
|
||||
}}/>
|
||||
moveRow={moveRow} isHovered={i == hoverIndex} setHoverIndex={setHoverIndex} viewHelperProps={viewHelperProps}
|
||||
/>
|
||||
{props.canEdit &&
|
||||
<PgReactTableRowExpandContent row={row}>
|
||||
<FormView value={row.original} viewHelperProps={viewHelperProps} dataDispatch={dataDispatch}
|
||||
schema={schemaRef.current} accessPath={accessPath.concat([row.index])} isNested={true} className={classes.expandedForm}
|
||||
isDataGridForm={true} firstEleRef={(ele)=>{
|
||||
requestAnimationAndFocus(ele);
|
||||
}}/>
|
||||
</PgReactTableRowExpandContent>
|
||||
}
|
||||
</React.Fragment>;
|
||||
</PgReactTableRow>;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</PgReactTableBody>
|
||||
</PgReactTable>
|
||||
</DndProvider>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default function DataGridViewMoized({memoDeps, ...props}) {
|
||||
return useMemo(()=><DataGridView {...props} />, memoDeps??[]);
|
||||
}
|
||||
|
||||
DataGridViewMoized.propTypes = {
|
||||
memoDeps: PropTypes.array,
|
||||
};
|
||||
|
||||
DataGridView.propTypes = {
|
||||
label: PropTypes.string,
|
||||
value: PropTypes.array,
|
||||
|
@ -289,10 +289,12 @@ export default function FormView({
|
||||
canDelete = false;
|
||||
}
|
||||
|
||||
const props = {
|
||||
key: field.id, value: value[field.id] || [], viewHelperProps: viewHelperProps,
|
||||
const ctrlProps = {
|
||||
key: field.id, ...field,
|
||||
value: value[field.id] || [], viewHelperProps: viewHelperProps,
|
||||
schema: field.schema, accessPath: accessPath.concat(field.id), dataDispatch: dataDispatch,
|
||||
containerClassName: classes.controlRow, ...field, canAdd: canAdd, canReorder: canReorder,
|
||||
containerClassName: classes.controlRow,
|
||||
canAdd: canAdd, canReorder: canReorder,
|
||||
canEdit: canEdit, canDelete: canDelete,
|
||||
visible: visible, canAddRow: canAddRow, onDelete: field.onDelete, canSearch: field.canSearch,
|
||||
expandEditOnAdd: field.expandEditOnAdd,
|
||||
@ -301,9 +303,14 @@ export default function FormView({
|
||||
};
|
||||
|
||||
if(CustomControl) {
|
||||
tabs[group].push(<CustomControl {...props}/>);
|
||||
tabs[group].push(<CustomControl {...ctrlProps}/>);
|
||||
} else {
|
||||
tabs[group].push(<DataGridView {...props}/>);
|
||||
tabs[group].push(<DataGridView {...ctrlProps} memoDeps={[
|
||||
JSON.stringify(ctrlProps.value),
|
||||
ctrlProps.containerClassName,
|
||||
ctrlProps.visible,
|
||||
...(evalFunc(null, ctrlProps.deps) || []).map((dep)=>value[dep]),
|
||||
]} />);
|
||||
}
|
||||
} else {
|
||||
/* Its a form control */
|
||||
|
@ -254,7 +254,7 @@ MappedFormControl.propTypes = {
|
||||
|
||||
export const MappedCellControl = (props) => {
|
||||
let newProps = { ...props };
|
||||
let cellProps = evalFunc(null, newProps.cell, newProps.row);
|
||||
let cellProps = evalFunc(null, newProps.cell, newProps.row.original);
|
||||
if (typeof (cellProps) === 'object') {
|
||||
newProps = {
|
||||
...newProps,
|
||||
|
@ -751,22 +751,23 @@ function SchemaDialogView({
|
||||
sessDispatch(dispatchPayload);
|
||||
};
|
||||
|
||||
const stateUtils = useMemo(()=>({
|
||||
dataDispatch: sessDispatchWithListener,
|
||||
initOrigData: (path, value)=>{
|
||||
if(path) {
|
||||
let data = prepareData(value);
|
||||
_.set(schema.origData, path, data);
|
||||
sessDispatchWithListener({
|
||||
type: SCHEMA_STATE_ACTIONS.SET_VALUE,
|
||||
path: path,
|
||||
value: data,
|
||||
});
|
||||
}
|
||||
},
|
||||
formResetKey: formResetKey,
|
||||
formErr: formErr,
|
||||
}), [formResetKey, formErr]);
|
||||
const stateUtils = useMemo(()=>{
|
||||
return {
|
||||
dataDispatch: sessDispatchWithListener,
|
||||
initOrigData: (path, value)=>{
|
||||
if(path) {
|
||||
let data = prepareData(value);
|
||||
_.set(schema.origData, path, data);
|
||||
sessDispatchWithListener({
|
||||
type: SCHEMA_STATE_ACTIONS.SET_VALUE,
|
||||
path: path,
|
||||
value: data,
|
||||
});
|
||||
}
|
||||
},
|
||||
formResetKey: formResetKey,
|
||||
formErr: formErr,
|
||||
};}, [formResetKey, formErr.name, formErr.message]);
|
||||
|
||||
const getButtonIcon = () => {
|
||||
if(props.customSaveBtnIconType == 'upload') {
|
||||
|
@ -347,6 +347,7 @@ function getFinalTheme(baseTheme) {
|
||||
overflow: 'auto',
|
||||
backgroundColor: baseTheme.palette.grey[400],
|
||||
position: 'relative',
|
||||
flexGrow: 1,
|
||||
},
|
||||
fontSourceCode: {
|
||||
fontFamily: '"Source Code Pro", SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
|
||||
|
364
web/pgadmin/static/js/components/PgReactTableStyled.jsx
Normal file
364
web/pgadmin/static/js/components/PgReactTableStyled.jsx
Normal file
@ -0,0 +1,364 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import React, { forwardRef } from 'react';
|
||||
import { flexRender } from '@tanstack/react-table';
|
||||
import { styled } from '@mui/styles';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Switch } from '@mui/material';
|
||||
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
|
||||
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
||||
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
||||
import { PgIconButton } from './Buttons';
|
||||
import clsx from 'clsx';
|
||||
import CustomPropTypes from '../custom_prop_types';
|
||||
|
||||
|
||||
const StyledDiv = styled('div')(({theme})=>({
|
||||
'&.pgrt': {
|
||||
display: 'grid',
|
||||
overflow: 'auto',
|
||||
position: 'relative',
|
||||
flexGrow: 1,
|
||||
},
|
||||
|
||||
// by default the table has no outer border.
|
||||
// the parent container has to take care of border.
|
||||
'& .pgrt-table': {
|
||||
borderSpacing: 0,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
display: 'grid',
|
||||
gridAutoRows: 'max-content',
|
||||
flexGrow: 1,
|
||||
flexDirection: 'column',
|
||||
|
||||
'& .pgrt-header': {
|
||||
position: 'sticky',
|
||||
top: 0,
|
||||
zIndex: 1,
|
||||
|
||||
'& .pgrt-header-row': {
|
||||
height: '34px',
|
||||
display: 'flex',
|
||||
|
||||
'& .pgrt-header-cell': {
|
||||
position: 'relative',
|
||||
fontWeight: theme.typography.fontWeightBold,
|
||||
padding: theme.spacing(0.5),
|
||||
textAlign: 'left',
|
||||
alignContent: 'center',
|
||||
backgroundColor: theme.otherVars.tableBg,
|
||||
overflow: 'hidden',
|
||||
...theme.mixins.panelBorder.bottom,
|
||||
...theme.mixins.panelBorder.right,
|
||||
|
||||
'& .pgrt-header-resizer': {
|
||||
display: 'inline-block',
|
||||
width: '5px',
|
||||
height: '100%',
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: 0,
|
||||
transform: 'translateX(50%)',
|
||||
zIndex: 1,
|
||||
cursor: 'col-resize',
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'& .pgrt-body': {
|
||||
position: 'relative',
|
||||
flexGrow: 1,
|
||||
minHeight: 0,
|
||||
|
||||
'& .pgrt-row': {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
|
||||
'& .pgrt-row-content': {
|
||||
display: 'flex',
|
||||
minHeight: 0,
|
||||
|
||||
'& .pgrd-row-cell': {
|
||||
margin: 0,
|
||||
padding: theme.spacing(0.25, 0.5),
|
||||
...theme.mixins.panelBorder.bottom,
|
||||
...theme.mixins.panelBorder.right,
|
||||
position: 'relative',
|
||||
height: '30px',
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
backgroundColor: theme.otherVars.tableBg,
|
||||
|
||||
'&.btn-cell': {
|
||||
textAlign: 'center',
|
||||
},
|
||||
'&.expanded-icon-cell': {
|
||||
backgroundColor: theme.palette.grey[400],
|
||||
borderBottom: 'none',
|
||||
},
|
||||
'&.row-warning': {
|
||||
backgroundColor: theme.palette.warning.main + '!important'
|
||||
},
|
||||
'&.row-alert': {
|
||||
backgroundColor: theme.palette.error.main + '!important'
|
||||
},
|
||||
'&.cell-with-icon': {
|
||||
paddingLeft: '1.8em',
|
||||
borderRadius: 0,
|
||||
backgroundPosition: '1%',
|
||||
},
|
||||
|
||||
'& .pgrd-row-cell-content': {
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
userSelect: 'text',
|
||||
width: '100%',
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'& .pgrt-expanded-content': {
|
||||
...theme.mixins.panelBorder.all,
|
||||
margin: '8px',
|
||||
flexGrow: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
export const PgReactTableCell = forwardRef(({row, cell, children, className}, ref)=>{
|
||||
let classNames = ['pgrd-row-cell'];
|
||||
if (typeof (cell.column.id) == 'string' && cell.column.id.startsWith('btn-')) {
|
||||
classNames.push('btn-cell');
|
||||
}
|
||||
if (cell.column.id == 'btn-edit' && row.getIsExpanded()) {
|
||||
classNames.push('expanded-icon-cell');
|
||||
}
|
||||
if (row.original.row_type === 'warning') {
|
||||
classNames.push('row-warning');
|
||||
}
|
||||
if (row.original.row_type === 'alert') {
|
||||
classNames.push('row-alert');
|
||||
}
|
||||
if(row.original.icon && row.original.icon[cell.column.id]) {
|
||||
classNames.push(row.original.icon[cell.column.id], 'cell-with-icon');
|
||||
}
|
||||
|
||||
classNames.push(className);
|
||||
|
||||
return (
|
||||
<div ref={ref} key={cell.id} style={{
|
||||
flex: `var(--col-${cell.column.id}-size) 0 auto`,
|
||||
width: `calc(var(--col-${cell.column.id}-size)*1px)`,
|
||||
...(cell.column.columnDef.maxSize ? { maxWidth: `${cell.column.columnDef.maxSize}px` } : {})
|
||||
}} role='cell'
|
||||
className={clsx(...classNames)}
|
||||
title={String(cell.getValue() ?? '')}>
|
||||
<div className='pgrd-row-cell-content'>{children}</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
PgReactTableCell.displayName = 'PgReactTableCell';
|
||||
PgReactTableCell.propTypes = {
|
||||
row: PropTypes.object,
|
||||
cell: PropTypes.object,
|
||||
children: CustomPropTypes.children,
|
||||
className: PropTypes.any,
|
||||
};
|
||||
|
||||
export const PgReactTableRow = forwardRef(({ children, className, ...props }, ref)=>{
|
||||
return (
|
||||
<div className={clsx('pgrt-row', className)} ref={ref} role="row" {...props}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
PgReactTableRow.displayName = 'PgReactTableRow';
|
||||
PgReactTableRow.propTypes = {
|
||||
children: CustomPropTypes.children,
|
||||
className: PropTypes.any,
|
||||
};
|
||||
|
||||
export const PgReactTableRowContent = forwardRef(({children, className, ...props}, ref)=>{
|
||||
return (
|
||||
<div className={clsx('pgrt-row-content', className)} ref={ref} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
PgReactTableRowContent.displayName = 'PgReactTableRowContent';
|
||||
PgReactTableRowContent.propTypes = {
|
||||
children: CustomPropTypes.children,
|
||||
className: PropTypes.any,
|
||||
};
|
||||
|
||||
|
||||
export function PgReactTableRowExpandContent({row, children}) {
|
||||
if(!row.getIsExpanded()) {
|
||||
return <></>;
|
||||
}
|
||||
return (
|
||||
<div className='pgrt-expanded-content' style={{ maxWidth: 'calc(var(--expand-width)*1px)' }}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
PgReactTableRowExpandContent.propTypes = {
|
||||
row: PropTypes.object,
|
||||
children: CustomPropTypes.children,
|
||||
};
|
||||
|
||||
export function PgReactTableHeader({table}) {
|
||||
return (
|
||||
<div className='pgrt-header'>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<div key={''} className='pgrt-header-row' style={{ }}>
|
||||
{headerGroup.headers.map((header) => (
|
||||
<div
|
||||
key={header.id}
|
||||
className='pgrt-header-cell'
|
||||
style={{
|
||||
flex: `var(--header-${header?.id}-size) 0 auto`,
|
||||
width: `calc(var(--header-${header?.id}-size)*1px)`,
|
||||
...(header.column.columnDef.maxSize ? { maxWidth: `${header.column.columnDef.maxSize}px` } : {}),
|
||||
cursor: header.column.getCanSort() ? 'pointer' : 'initial',
|
||||
}}
|
||||
onClick={header.column.getCanSort() ? header.column.getToggleSortingHandler() : undefined}
|
||||
>
|
||||
<div>
|
||||
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||
{header.column.getCanSort() && header.column.getIsSorted() &&
|
||||
<span>
|
||||
{header.column.getIsSorted() == 'desc' ?
|
||||
<KeyboardArrowDownIcon style={{ fontSize: '1.2rem' }} />
|
||||
: <KeyboardArrowUpIcon style={{ fontSize: '1.2rem' }} />}
|
||||
</span>}
|
||||
</div>
|
||||
{header.column.getCanResize() && (
|
||||
<div
|
||||
onDoubleClick={() => header.column.resetSize()}
|
||||
onMouseDown={header.getResizeHandler()}
|
||||
onTouchStart={header.getResizeHandler()}
|
||||
className='pgrt-header-resizer'
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
PgReactTableHeader.propTypes = {
|
||||
table: PropTypes.object,
|
||||
};
|
||||
|
||||
export function PgReactTableBody({children, style}) {
|
||||
return (
|
||||
<div className='pgrt-body' style={style}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
PgReactTableBody.propTypes = {
|
||||
style: PropTypes.object,
|
||||
children: CustomPropTypes.children,
|
||||
};
|
||||
|
||||
export const PgReactTable = forwardRef(({children, table, rootClassName, tableClassName, ...props}, ref)=>{
|
||||
const columns = table.getAllColumns();
|
||||
// Render the UI for your table
|
||||
const maxExpandWidth = (ref.current?.getBoundingClientRect().width ?? 430) - 30; //margin,scrollbar,etc.
|
||||
|
||||
const columnSizeVars = React.useMemo(() => {
|
||||
const headers = table.getFlatHeaders();
|
||||
const colSizes = {};
|
||||
for (let i = 0; i < headers.length; i++) {
|
||||
const header = headers[i];
|
||||
colSizes[`--header-${header.id}-size`] = header.getSize();
|
||||
colSizes[`--col-${header.column.id}-size`] = header.column.getSize();
|
||||
}
|
||||
return colSizes;
|
||||
}, [columns, table.getState().columnSizingInfo]);
|
||||
|
||||
return (
|
||||
<StyledDiv className={clsx('pgrt', rootClassName)} style={{'--expand-width': maxExpandWidth }} ref={ref} >
|
||||
<div className={clsx('pgrt-table', tableClassName)} style={{ ...columnSizeVars }} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
</StyledDiv>
|
||||
);
|
||||
});
|
||||
PgReactTable.displayName = 'PgReactTable';
|
||||
PgReactTable.propTypes = {
|
||||
table: PropTypes.object,
|
||||
rootClassName: PropTypes.any,
|
||||
tableClassName: PropTypes.any,
|
||||
children: CustomPropTypes.children,
|
||||
};
|
||||
|
||||
export function getExpandCell({ onClick, ...props }) {
|
||||
const Cell = ({ row }) => {
|
||||
const onClickFinal = (e) => {
|
||||
e.preventDefault();
|
||||
row.toggleExpanded();
|
||||
onClick?.(row, e);
|
||||
};
|
||||
return (
|
||||
<PgIconButton
|
||||
size="xs"
|
||||
icon={
|
||||
row.getIsExpanded() ? (
|
||||
<KeyboardArrowDownIcon />
|
||||
) : (
|
||||
<ChevronRightIcon />
|
||||
)
|
||||
}
|
||||
noBorder
|
||||
{...props}
|
||||
onClick={onClickFinal}
|
||||
aria-label={props.title}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
Cell.displayName = 'ExpandCell';
|
||||
Cell.propTypes = {
|
||||
title: PropTypes.string,
|
||||
row: PropTypes.any,
|
||||
};
|
||||
|
||||
return Cell;
|
||||
}
|
||||
|
||||
const ReadOnlySwitch = styled(Switch)(({theme})=>({
|
||||
opacity: 0.75,
|
||||
'& .MuiSwitch-track': {
|
||||
opacity: theme.palette.action.disabledOpacity,
|
||||
}
|
||||
}));
|
||||
export function getSwitchCell() {
|
||||
const Cell = ({ value }) => {
|
||||
return <ReadOnlySwitch color="primary" checked={value} value={value} readOnly title={String(value)} />;
|
||||
};
|
||||
|
||||
Cell.displayName = 'SwitchCell';
|
||||
Cell.propTypes = {
|
||||
value: PropTypes.any,
|
||||
};
|
||||
|
||||
return Cell;
|
||||
}
|
@ -7,484 +7,244 @@
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import React from 'react';
|
||||
import React, { useMemo, useRef } from 'react';
|
||||
import {
|
||||
useTable,
|
||||
useRowSelect,
|
||||
useSortBy,
|
||||
useResizeColumns,
|
||||
useFlexLayout,
|
||||
useGlobalFilter,
|
||||
useExpanded,
|
||||
} from 'react-table';
|
||||
import { VariableSizeList } from 'react-window';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import clsx from 'clsx';
|
||||
useReactTable,
|
||||
getCoreRowModel,
|
||||
getSortedRowModel,
|
||||
getFilteredRowModel,
|
||||
getExpandedRowModel,
|
||||
flexRender,
|
||||
} from '@tanstack/react-table';
|
||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||
import { styled } from '@mui/styles';
|
||||
import PropTypes from 'prop-types';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
import { Checkbox, Box, Switch } from '@mui/material';
|
||||
import { Checkbox, Box } from '@mui/material';
|
||||
import { InputText } from './FormComponents';
|
||||
import _ from 'lodash';
|
||||
import gettext from 'sources/gettext';
|
||||
import SchemaView from '../SchemaView';
|
||||
import EmptyPanelMessage from './EmptyPanelMessage';
|
||||
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
|
||||
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
||||
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
||||
import { PgIconButton } from './Buttons';
|
||||
import { PgReactTable, PgReactTableBody, PgReactTableCell, PgReactTableHeader, PgReactTableRow, PgReactTableRowContent, PgReactTableRowExpandContent } from './PgReactTableStyled';
|
||||
|
||||
/* eslint-disable react/display-name */
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
const ROW_HEIGHT = 30;
|
||||
function TableRow({ index, style, schema, row, measureElement }) {
|
||||
const [expandComplete, setExpandComplete] = React.useState(false);
|
||||
const rowRef = React.useRef();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (rowRef.current) {
|
||||
if (!expandComplete && rowRef.current.style.height == `${ROW_HEIGHT}px`) {
|
||||
return;
|
||||
}
|
||||
measureElement(rowRef.current);
|
||||
}
|
||||
}, [row.getIsExpanded(), expandComplete]);
|
||||
|
||||
return (
|
||||
<PgReactTableRow data-index={index} ref={rowRef} style={style} row={row}>
|
||||
<PgReactTableRowContent>
|
||||
{row.getVisibleCells().map((cell) => {
|
||||
const content = flexRender(cell.column.columnDef.cell, cell.getContext());
|
||||
|
||||
return (
|
||||
<PgReactTableCell cell={cell} row={row} key={cell.id}>
|
||||
{content}
|
||||
</PgReactTableCell>
|
||||
);
|
||||
})}
|
||||
</PgReactTableRowContent>
|
||||
<PgReactTableRowExpandContent row={row}>
|
||||
<SchemaView
|
||||
getInitData={() => Promise.resolve(row.original)}
|
||||
viewHelperProps={{ mode: 'properties' }}
|
||||
schema={schema}
|
||||
showFooter={false}
|
||||
onDataChange={() => { setExpandComplete(true); }}
|
||||
/>
|
||||
</PgReactTableRowExpandContent>
|
||||
</PgReactTableRow>
|
||||
);
|
||||
}
|
||||
TableRow.propTypes = {
|
||||
index: PropTypes.number,
|
||||
style: PropTypes.object,
|
||||
row: PropTypes.object,
|
||||
schema: PropTypes.object,
|
||||
measureElement: PropTypes.func,
|
||||
};
|
||||
|
||||
export function Table({ columns, data, hasSelectRow, schema, sortOptions, tableProps, searchVal, ...props }) {
|
||||
const defaultColumn = React.useMemo(
|
||||
() => ({
|
||||
size: 150,
|
||||
minSize: 100,
|
||||
maxSize: 1200,
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
const finalColumns = useMemo(() => (hasSelectRow ? [{
|
||||
id: 'selection',
|
||||
header: ({ table }) => {
|
||||
return (
|
||||
<div style={{textAlign: 'center', minWidth: 20}}>
|
||||
<Checkbox
|
||||
color="primary"
|
||||
checked={table.getIsAllRowsSelected()}
|
||||
indeterminate={table.getIsSomeRowsSelected()}
|
||||
onChange={table.getToggleAllRowsSelectedHandler()}
|
||||
inputProps={{ 'aria-label': gettext('Select All Rows') }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => (
|
||||
<div style={{textAlign: 'center', minWidth: 20}}>
|
||||
<Checkbox
|
||||
color="primary"
|
||||
checked={row.getIsSelected()}
|
||||
indeterminate={row.getIsSomeSelected()}
|
||||
disabled={!row.getCanSelect()}
|
||||
onChange={row.getToggleSelectedHandler()}
|
||||
inputProps={{ 'aria-label': gettext('Select Row') }}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
enableSorting: false,
|
||||
enableResizing: false,
|
||||
maxSize: 35,
|
||||
}] : []).concat(
|
||||
columns.filter((c)=>_.isUndefined(c.enableVisibility) ? true : c.enableVisibility)
|
||||
), [hasSelectRow, columns]);
|
||||
|
||||
// Render the UI for your table
|
||||
const tableRef = useRef();
|
||||
|
||||
const table = useReactTable({
|
||||
columns: finalColumns,
|
||||
data,
|
||||
defaultColumn,
|
||||
autoResetAll: false,
|
||||
initialState: {
|
||||
sorting: sortOptions || [],
|
||||
},
|
||||
state: {
|
||||
rowSelection: props.selectedRows ?? {},
|
||||
globalFilter: searchVal,
|
||||
},
|
||||
columnResizeMode: 'onChange',
|
||||
onRowSelectionChange: props.setSelectedRows,
|
||||
enableRowSelection: (row) => (hasSelectRow && (_.isUndefined(row.original.canDrop) ? true : row.original.canDrop)),
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
getExpandedRowModel: getExpandedRowModel(),
|
||||
...tableProps,
|
||||
});
|
||||
|
||||
const rows = table.getRowModel().rows;
|
||||
|
||||
const virtualizer = useVirtualizer({
|
||||
count: rows.length,
|
||||
getScrollElement: () => tableRef.current,
|
||||
estimateSize: () => ROW_HEIGHT,
|
||||
measureElement:
|
||||
typeof window !== 'undefined' &&
|
||||
navigator.userAgent.indexOf('Firefox') === -1
|
||||
? element => element?.getBoundingClientRect().height
|
||||
: undefined,
|
||||
overscan: 20,
|
||||
});
|
||||
|
||||
return (
|
||||
<PgReactTable ref={tableRef} table={table}>
|
||||
<PgReactTableHeader table={table} />
|
||||
{rows.length == 0 ?
|
||||
<EmptyPanelMessage text={gettext('No rows found')} /> :
|
||||
<PgReactTableBody style={{ height: virtualizer.getTotalSize() + 'px'}}>
|
||||
{virtualizer.getVirtualItems().map((virtualRow) => {
|
||||
const row = rows[virtualRow.index];
|
||||
return <TableRow index={row.index} key={row.index} row={row} schema={schema}
|
||||
measureElement={virtualizer.measureElement}
|
||||
style={{
|
||||
transform: `translateY(${virtualRow.start}px)`, //this should always be a `style` as it changes on scroll
|
||||
}}
|
||||
/>;
|
||||
})}
|
||||
</PgReactTableBody>}
|
||||
</PgReactTable>
|
||||
);
|
||||
}
|
||||
Table.propTypes = {
|
||||
columns: PropTypes.array,
|
||||
data: PropTypes.array,
|
||||
hasSelectRow: PropTypes.bool,
|
||||
schema: PropTypes.object,
|
||||
sortOptions: PropTypes.arrayOf(PropTypes.object),
|
||||
tableProps: PropTypes.object,
|
||||
selectedRows: PropTypes.object,
|
||||
setSelectedRows: PropTypes.func,
|
||||
searchVal: PropTypes.string,
|
||||
};
|
||||
|
||||
const StyledPgTableRoot = styled('div')(({theme})=>({
|
||||
display: 'flex',
|
||||
flexGrow: 1,
|
||||
overflow: 'hidden',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
|
||||
|
||||
'& .pgtable-header': {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
...theme.mixins.panelBorder,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
background: theme.palette.background.default,
|
||||
padding: '8px 2px 4px',
|
||||
|
||||
'& .pgtable-search-input': {
|
||||
minWidth: '300px'
|
||||
},
|
||||
},
|
||||
autoResizerContainer: {
|
||||
flexGrow: 1,
|
||||
minHeight: 0
|
||||
},
|
||||
autoResizer: {
|
||||
width: '100% !important',
|
||||
},
|
||||
fixedSizeList: {
|
||||
direction: 'ltr',
|
||||
overflowX: 'hidden !important',
|
||||
overflow: 'overlay !important',
|
||||
},
|
||||
CustomHeader:{
|
||||
marginTop: '8px',
|
||||
marginLeft: '4px'
|
||||
},
|
||||
warning: {
|
||||
backgroundColor: theme.palette.warning.main + '!important'
|
||||
},
|
||||
alert: {
|
||||
backgroundColor: theme.palette.error.main + '!important'
|
||||
},
|
||||
searchInput: {
|
||||
minWidth: '300px'
|
||||
},
|
||||
tableContainer: {
|
||||
overflowX: 'auto',
|
||||
|
||||
'& .pgtable-body': {
|
||||
flexGrow: 1,
|
||||
minHeight: 0,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
backgroundColor: theme.otherVars.emptySpaceBg,
|
||||
},
|
||||
table: {
|
||||
borderSpacing: 0,
|
||||
overflow: 'hidden',
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
border: '1px solid '+theme.otherVars.borderColor,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
},
|
||||
pgTableContainer: {
|
||||
display: 'flex',
|
||||
flexGrow: 1,
|
||||
overflow: 'hidden',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
},
|
||||
pgTableHeader: {
|
||||
display: 'flex',
|
||||
background: theme.palette.background.default,
|
||||
padding: '8px 8px 4px',
|
||||
},
|
||||
tableRowContent:{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
minHeight: 0,
|
||||
},
|
||||
|
||||
expandedForm: {
|
||||
...theme.mixins.panelBorder.all,
|
||||
margin: '8px',
|
||||
flexGrow: 1,
|
||||
},
|
||||
|
||||
tableCell: {
|
||||
margin: 0,
|
||||
padding: theme.spacing(0.5),
|
||||
...theme.mixins.panelBorder.bottom,
|
||||
...theme.mixins.panelBorder.right,
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
height: '34px',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
backgroundColor: theme.otherVars.tableBg,
|
||||
userSelect: 'text'
|
||||
},
|
||||
selectCell: {
|
||||
textAlign: 'center',
|
||||
minWidth: 20
|
||||
},
|
||||
tableHeader: {
|
||||
backgroundColor: theme.otherVars.tableBg,
|
||||
},
|
||||
tableCellHeader: {
|
||||
fontWeight: theme.typography.fontWeightBold,
|
||||
padding: theme.spacing(1, 0.5),
|
||||
textAlign: 'left',
|
||||
alignContent: 'center',
|
||||
backgroundColor: theme.otherVars.tableBg,
|
||||
overflow: 'hidden',
|
||||
...theme.mixins.panelBorder.bottom,
|
||||
...theme.mixins.panelBorder.right,
|
||||
...theme.mixins.panelBorder.top,
|
||||
...theme.mixins.panelBorder.left,
|
||||
},
|
||||
resizer: {
|
||||
display: 'inline-block',
|
||||
width: '5px',
|
||||
height: '100%',
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: 0,
|
||||
transform: 'translateX(50%)',
|
||||
zIndex: 1,
|
||||
touchAction: 'none',
|
||||
},
|
||||
cellIcon: {
|
||||
paddingLeft: '1.8em',
|
||||
paddingTop: '0.35em',
|
||||
borderRadius: 0,
|
||||
backgroundPosition: '1%',
|
||||
},
|
||||
emptyPanel: {
|
||||
minHeight: '100%',
|
||||
minWidth: '100%',
|
||||
overflow: 'auto',
|
||||
padding: '8px',
|
||||
display: 'flex',
|
||||
},
|
||||
caveTable: {
|
||||
margin: '8px',
|
||||
},
|
||||
panelIcon: {
|
||||
width: '80%',
|
||||
margin: '0 auto',
|
||||
marginTop: '25px !important',
|
||||
position: 'relative',
|
||||
textAlign: 'center',
|
||||
},
|
||||
panelMessage: {
|
||||
marginLeft: '0.5rem',
|
||||
fontSize: '0.875rem',
|
||||
},
|
||||
expandedIconCell: {
|
||||
backgroundColor: theme.palette.grey[400],
|
||||
...theme.mixins.panelBorder.top,
|
||||
borderBottom: 'none',
|
||||
},
|
||||
btnCell: {
|
||||
padding: theme.spacing(0.5, 0),
|
||||
textAlign: 'center',
|
||||
},
|
||||
btnExpanded: {
|
||||
backgroundColor: theme.palette.grey[400]
|
||||
},
|
||||
readOnlySwitch: {
|
||||
opacity: 0.75,
|
||||
'& .MuiSwitch-track': {
|
||||
opacity: theme.palette.action.disabledOpacity,
|
||||
'&.pgtable-pgrt-border': {
|
||||
'& .pgrt': {
|
||||
border: '1px solid ' + theme.otherVars.borderColor,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'&.pgtable-pgrt-cave': {
|
||||
'& .pgtable-body': {
|
||||
padding: '8px',
|
||||
},
|
||||
'& .pgtable-header': {
|
||||
padding: '8px 8px 4px',
|
||||
},
|
||||
'& .pgrt': {
|
||||
border: '1px solid ' + theme.otherVars.borderColor,
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
const IndeterminateCheckbox = React.forwardRef(
|
||||
({ indeterminate, label, ...rest }, ref) => {
|
||||
const defaultRef = React.useRef();
|
||||
const resolvedRef = ref || defaultRef;
|
||||
|
||||
React.useEffect(() => {
|
||||
resolvedRef.current.indeterminate = indeterminate;
|
||||
}, [resolvedRef, indeterminate]);
|
||||
return (
|
||||
<Checkbox
|
||||
color="primary"
|
||||
ref={resolvedRef} {...rest}
|
||||
inputProps={{'aria-label': label}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
IndeterminateCheckbox.displayName = 'SelectCheckbox';
|
||||
|
||||
IndeterminateCheckbox.propTypes = {
|
||||
indeterminate: PropTypes.bool,
|
||||
rest: PropTypes.func,
|
||||
getToggleAllRowsSelectedProps: PropTypes.func,
|
||||
row: PropTypes.object,
|
||||
label: PropTypes.string,
|
||||
};
|
||||
|
||||
const ROW_HEIGHT = 34;
|
||||
|
||||
function SortIcon ({column}) {
|
||||
if (column.isSorted) {
|
||||
return column.isSortedDesc ? <KeyboardArrowDownIcon style={{fontSize: '1.2rem'}} /> : <KeyboardArrowUpIcon style={{fontSize: '1.2rem'}} />;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
SortIcon.propTypes = {
|
||||
column: PropTypes.object
|
||||
};
|
||||
|
||||
function RenderRow({ index, style, schema, row, prepareRow, setRowHeight, ExpandedComponent }) {
|
||||
const [expandComplete, setExpandComplete] = React.useState(false);
|
||||
const rowRef = React.useRef() ;
|
||||
const classes = useStyles();
|
||||
prepareRow(row);
|
||||
|
||||
React.useEffect(()=>{
|
||||
if(rowRef.current) {
|
||||
if(!expandComplete && rowRef.current.style.height == `${ROW_HEIGHT}px`) {
|
||||
return;
|
||||
}
|
||||
let rowHeight;
|
||||
rowRef.current.style.height = 'unset';
|
||||
if(expandComplete) {
|
||||
rowHeight = rowRef.current.offsetHeight;
|
||||
} else {
|
||||
rowHeight = ROW_HEIGHT;
|
||||
rowRef.current.style.height = ROW_HEIGHT;
|
||||
}
|
||||
rowRef.current.style.height = rowHeight + 'px';
|
||||
setRowHeight(index, rowHeight);
|
||||
}
|
||||
}, [expandComplete]);
|
||||
|
||||
return (
|
||||
<div style={style} key={row.id} ref={rowRef} data-test="row-container">
|
||||
<div className={classes.tableRowContent}>
|
||||
<div {...row.getRowProps()} className={classes.tr}>
|
||||
{row.cells.map((cell) => {
|
||||
let classNames = [classes.tableCell];
|
||||
if(typeof(cell.column.id) == 'string' && cell.column.id.startsWith('btn-')) {
|
||||
classNames.push(classes.btnCell);
|
||||
}
|
||||
if(cell.column.id == 'btn-edit' && row.isExpanded) {
|
||||
classNames.push(classes.expandedIconCell);
|
||||
}
|
||||
if (row.original.row_type === 'warning'){
|
||||
classNames.push(classes.warning);
|
||||
}
|
||||
if (row.original.row_type === 'alert'){
|
||||
classNames.push(classes.alert);
|
||||
}
|
||||
return (
|
||||
<div key={cell.column.id} {...cell.getCellProps()} className={clsx(classNames, cell.column?.dataClassName, row.original.icon?.[cell.column.id], row.original.icon?.[cell.column.id] && classes.cellIcon)}
|
||||
title={_.isUndefined(cell.value) || _.isNull(cell.value) ? '': String(cell.value)}>
|
||||
{cell.render('Cell')}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{!_.isUndefined(row) && row.isExpanded && (
|
||||
<Box key={row.id} className={classes.expandedForm}>
|
||||
{schema && <SchemaView
|
||||
getInitData={()=>Promise.resolve(row.original)}
|
||||
viewHelperProps={{ mode: 'properties' }}
|
||||
schema={schema[row.id]??schema}
|
||||
showFooter={false}
|
||||
onDataChange={()=>{setExpandComplete(true);}}
|
||||
/>}
|
||||
{ExpandedComponent && <ExpandedComponent row={row} onExpandComplete={()=>setExpandComplete(true)}/>}
|
||||
</Box>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
RenderRow.propTypes = {
|
||||
index: PropTypes.number,
|
||||
style: PropTypes.object,
|
||||
row: PropTypes.object,
|
||||
schema: PropTypes.object,
|
||||
prepareRow: PropTypes.func,
|
||||
setRowHeight: PropTypes.func,
|
||||
ExpandedComponent: PropTypes.node,
|
||||
};
|
||||
|
||||
export default function PgTable({ columns, data, isSelectRow, caveTable=true, schema, ExpandedComponent, sortOptions, tableProps, ...props }) {
|
||||
// Use the state and functions returned from useTable to build your UI
|
||||
const classes = useStyles();
|
||||
export default function PgTable({ caveTable = true, tableNoBorder = true, ...props }) {
|
||||
const [searchVal, setSearchVal] = React.useState('');
|
||||
const windowTableRef = React.useRef();
|
||||
const rowHeights = React.useRef({});
|
||||
|
||||
// Reset Search value on tab changes.
|
||||
|
||||
React.useEffect(()=>{
|
||||
setSearchVal(prevState => (prevState));
|
||||
setGlobalFilter(searchVal || undefined);
|
||||
rowHeights.current = {};
|
||||
windowTableRef.current?.resetAfterIndex(0);
|
||||
}, [data]);
|
||||
|
||||
function getRowHeight(index) {
|
||||
return rowHeights.current[index] || ROW_HEIGHT;
|
||||
}
|
||||
|
||||
const setRowHeight = (index, size) => {
|
||||
if(windowTableRef.current) {
|
||||
if(size == ROW_HEIGHT) {
|
||||
delete rowHeights.current[index];
|
||||
} else {
|
||||
rowHeights.current[index] = size;
|
||||
}
|
||||
windowTableRef.current.resetAfterIndex(index);
|
||||
}
|
||||
};
|
||||
|
||||
const defaultColumn = React.useMemo(
|
||||
() => ({
|
||||
minWidth: 50,
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
const {
|
||||
getTableProps,
|
||||
getTableBodyProps,
|
||||
headerGroups,
|
||||
rows,
|
||||
prepareRow,
|
||||
selectedFlatRows,
|
||||
state: { selectedRowIds },
|
||||
setGlobalFilter,
|
||||
setHiddenColumns,
|
||||
totalColumnsWidth
|
||||
} = useTable(
|
||||
{
|
||||
columns,
|
||||
data,
|
||||
defaultColumn,
|
||||
isSelectRow,
|
||||
autoResetSortBy: false,
|
||||
initialState: {
|
||||
sortBy: sortOptions || [],
|
||||
},
|
||||
...tableProps,
|
||||
},
|
||||
useGlobalFilter,
|
||||
useSortBy,
|
||||
useExpanded,
|
||||
useRowSelect,
|
||||
useResizeColumns,
|
||||
useFlexLayout,
|
||||
(hooks) => {
|
||||
hooks.visibleColumns.push((CLOUMNS) => {
|
||||
if (isSelectRow) {
|
||||
return [
|
||||
// Let's make a column for selection
|
||||
{
|
||||
id: 'selection',
|
||||
resizable: false,
|
||||
// The header can use the table's getToggleAllRowsSelectedProps method
|
||||
// to render a checkbox
|
||||
Header: ({ getToggleAllRowsSelectedProps, toggleRowSelected, isAllRowsSelected, rows }) => {
|
||||
|
||||
const modifiedOnChange = (event) => {
|
||||
rows.forEach((row) => {
|
||||
//check each row if it is not disabled
|
||||
!(!_.isUndefined(row.original.canDrop) && !(row.original.canDrop)) && toggleRowSelected(row.id, event.currentTarget.checked);
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
let allTableRows = 0;
|
||||
let selectedTableRows = 0;
|
||||
rows.forEach((row) => {
|
||||
row.isSelected && selectedTableRows++;
|
||||
(_.isUndefined(row.original.canDrop) || row.original.canDrop) && allTableRows++;
|
||||
});
|
||||
const disabled = allTableRows === 0;
|
||||
const checked =
|
||||
(isAllRowsSelected ||
|
||||
allTableRows === selectedTableRows) &&
|
||||
!disabled;
|
||||
return(
|
||||
<div className={classes.selectCell}>
|
||||
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()
|
||||
}
|
||||
onChange={modifiedOnChange}
|
||||
checked={checked}
|
||||
label={gettext('Select All Rows')}
|
||||
/>
|
||||
</div>
|
||||
);},
|
||||
// The cell can use the individual row's getToggleRowSelectedProps method
|
||||
// to the render a checkbox
|
||||
Cell: ({ row }) => (
|
||||
<div className={classes.selectCell}>
|
||||
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()}
|
||||
disabled={!_.isUndefined(row.original.canDrop) ? !(row.original.canDrop) : false}
|
||||
label={gettext('Select Row')}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
sortable: false,
|
||||
disableResizing: true,
|
||||
width: 35,
|
||||
maxWidth: 35,
|
||||
minWidth: 35
|
||||
},
|
||||
...CLOUMNS,
|
||||
];
|
||||
} else {
|
||||
return [...CLOUMNS];
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
setHiddenColumns(
|
||||
columns
|
||||
.filter((column) => {
|
||||
return !(column.isVisible === undefined || column.isVisible === true);
|
||||
}
|
||||
)
|
||||
.map((column) => column.accessor)
|
||||
);
|
||||
}, [setHiddenColumns, columns]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (props.setSelectedRows) {
|
||||
props.setSelectedRows(selectedFlatRows);
|
||||
}
|
||||
}, [selectedRowIds]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (props.getSelectedRows) {
|
||||
props.getSelectedRows(selectedFlatRows);
|
||||
}
|
||||
}, [selectedRowIds]);
|
||||
|
||||
React.useEffect(() => {
|
||||
setGlobalFilter(searchVal || undefined);
|
||||
}, [searchVal]);
|
||||
|
||||
// Render the UI for your table
|
||||
return (
|
||||
<Box className={classes.pgTableContainer} data-test={props['data-test']}>
|
||||
<Box className={classes.pgTableHeader}>
|
||||
{props.CustomHeader && (<Box className={classes.customHeader}> <props.CustomHeader /></Box>)}
|
||||
<StyledPgTableRoot className={[tableNoBorder ? '' : 'pgtable-pgrt-border', caveTable ? 'pgtable-pgrt-cave' : ''].join(' ')} data-test={props['data-test']}>
|
||||
<Box className='pgtable-header'>
|
||||
{props.CustomHeader && (<Box className='pgtable-custom-header-section'> <props.CustomHeader /></Box>)}
|
||||
<Box marginLeft="auto">
|
||||
<InputText
|
||||
placeholder={gettext('Search')}
|
||||
controlProps={{title: gettext('Search')}}
|
||||
className={classes.searchInput}
|
||||
controlProps={{ title: gettext('Search') }}
|
||||
className='pgtable-search-input'
|
||||
value={searchVal}
|
||||
onChange={(val) => {
|
||||
setSearchVal(val);
|
||||
@ -492,149 +252,16 @@ export default function PgTable({ columns, data, isSelectRow, caveTable=true, sc
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
<div className={classes.tableContainer}>
|
||||
<div {...getTableProps({style:{minWidth: totalColumnsWidth}})} className={clsx(classes.table, caveTable ? classes.caveTable : '')}>
|
||||
<div>
|
||||
{headerGroups.map((headerGroup) => (
|
||||
<div key={''} {...headerGroup.getHeaderGroupProps((column)=>({
|
||||
style: {
|
||||
...column.style,
|
||||
height: '40px',
|
||||
}
|
||||
}))}>
|
||||
{headerGroup.headers.map((column) => (
|
||||
<div
|
||||
key={column.id}
|
||||
{...column.getHeaderProps()}
|
||||
className={clsx(classes.tableCellHeader, column.className)}
|
||||
>
|
||||
<div
|
||||
{...(column.sortable ? column.getSortByToggleProps() : {})}
|
||||
>
|
||||
{column.render('Header')}
|
||||
<span>
|
||||
<SortIcon column={column} />
|
||||
</span>
|
||||
</div>
|
||||
{column.resizable && (
|
||||
<div
|
||||
{...column.getResizerProps()}
|
||||
className={classes.resizer}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{
|
||||
data.length > 0 ? (
|
||||
<div {...getTableBodyProps()} className={classes.autoResizerContainer}>
|
||||
<AutoSizer
|
||||
className={classes.autoResizer}
|
||||
>
|
||||
{({ height }) => (
|
||||
<VariableSizeList
|
||||
ref={windowTableRef}
|
||||
className={classes.fixedSizeList}
|
||||
height={isNaN(height) ? 100 : height}
|
||||
itemCount={rows.length}
|
||||
itemSize={getRowHeight}
|
||||
itemData={{rows, prepareRow, setRowHeight}}
|
||||
>
|
||||
{({index, style})=>(
|
||||
<RenderRow index={index} style={style} row={rows[index]} schema={schema} prepareRow={prepareRow}
|
||||
setRowHeight={setRowHeight} ExpandedComponent={ExpandedComponent} />
|
||||
)}
|
||||
</VariableSizeList>)}
|
||||
</AutoSizer>
|
||||
</div>
|
||||
) : (
|
||||
<EmptyPanelMessage text={gettext('No rows found')}/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<div className={'pgtable-body'}>
|
||||
<Table {...props} searchVal={searchVal} />
|
||||
</div>
|
||||
</Box>
|
||||
</StyledPgTableRoot>
|
||||
);
|
||||
}
|
||||
|
||||
PgTable.propTypes = {
|
||||
stepId: PropTypes.number,
|
||||
height: PropTypes.number,
|
||||
CustomHeader: PropTypes.func,
|
||||
className: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||
caveTable: PropTypes.bool,
|
||||
fixedSizeList: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node,
|
||||
]),
|
||||
getToggleAllRowsSelectedProps: PropTypes.func,
|
||||
toggleRowSelected: PropTypes.func,
|
||||
columns: PropTypes.array,
|
||||
data: PropTypes.array,
|
||||
isSelectRow: PropTypes.bool,
|
||||
isAllRowsSelected: PropTypes.bool,
|
||||
row: PropTypes.func,
|
||||
setSelectedRows: PropTypes.func,
|
||||
getSelectedRows: PropTypes.func,
|
||||
searchText: PropTypes.string,
|
||||
sortOptions: PropTypes.array,
|
||||
schema: PropTypes.object,
|
||||
rows: PropTypes.object,
|
||||
ExpandedComponent: PropTypes.node,
|
||||
tableProps: PropTypes.object,
|
||||
tableNoBorder: PropTypes.bool,
|
||||
'data-test': PropTypes.string
|
||||
};
|
||||
|
||||
|
||||
export function getExpandCell({onClick, ...props}) {
|
||||
const Cell = ({ row }) => {
|
||||
const classes = useStyles();
|
||||
const onClickFinal = (e)=>{
|
||||
e.preventDefault();
|
||||
row.toggleRowExpanded(!row.isExpanded);
|
||||
onClick?.(row, e);
|
||||
};
|
||||
return (
|
||||
<PgIconButton
|
||||
size="xs"
|
||||
className={row.isExpanded ? classes.btnExpanded : ''}
|
||||
icon={
|
||||
row.isExpanded ? (
|
||||
<KeyboardArrowDownIcon />
|
||||
) : (
|
||||
<ChevronRightIcon />
|
||||
)
|
||||
}
|
||||
noBorder
|
||||
{...props}
|
||||
onClick={onClickFinal}
|
||||
aria-label={props.title}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
Cell.displayName = 'ExpandCell';
|
||||
Cell.propTypes = {
|
||||
title: PropTypes.string,
|
||||
row: PropTypes.any,
|
||||
};
|
||||
|
||||
return Cell;
|
||||
}
|
||||
|
||||
export function getSwitchCell() {
|
||||
const Cell = ({value})=>{
|
||||
const classes = useStyles();
|
||||
return <Switch color="primary" checked={value} className={classes.readOnlySwitch} value={value} readOnly title={String(value)} />;
|
||||
};
|
||||
|
||||
Cell.displayName = 'SwitchCell';
|
||||
Cell.propTypes = {
|
||||
value: PropTypes.any,
|
||||
};
|
||||
|
||||
return Cell;
|
||||
}
|
||||
};
|
@ -10,7 +10,7 @@
|
||||
import gettext from 'sources/gettext';
|
||||
import _ from 'lodash';
|
||||
import url_for from 'sources/url_for';
|
||||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { Box } from '@mui/material';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import Wizard from '../../../../static/js/helpers/wizard/Wizard';
|
||||
@ -63,63 +63,64 @@ export default function GrantWizard({ sid, did, nodeInfo, nodeData, onClose }) {
|
||||
let columns = [
|
||||
{
|
||||
|
||||
Header: 'Object Type',
|
||||
accessor: 'object_type',
|
||||
sortable: true,
|
||||
resizable: false,
|
||||
disableGlobalFilter: true
|
||||
header: 'Object Type',
|
||||
accessorKey: 'object_type',
|
||||
enableSorting: true,
|
||||
enableResizing: false,
|
||||
enableFilters: false
|
||||
},
|
||||
{
|
||||
Header: 'Schema',
|
||||
accessor: 'nspname',
|
||||
sortable: true,
|
||||
resizable: false,
|
||||
disableGlobalFilter: true
|
||||
header: 'Schema',
|
||||
accessorKey: 'nspname',
|
||||
enableSorting: true,
|
||||
enableResizing: false,
|
||||
enableFilters: false
|
||||
},
|
||||
{
|
||||
Header: 'Name',
|
||||
accessor: 'name_with_args',
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
disableGlobalFilter: false,
|
||||
minWidth: 280
|
||||
header: 'Name',
|
||||
accessorKey: 'name_with_args',
|
||||
enableSorting: true,
|
||||
enableResizing: true,
|
||||
enableFilters: true,
|
||||
minSize: 280
|
||||
},
|
||||
{
|
||||
Header: 'parameters',
|
||||
accessor: 'proargs',
|
||||
sortable: false,
|
||||
resizable: false,
|
||||
disableGlobalFilter: false,
|
||||
header: 'parameters',
|
||||
accessorKey: 'proargs',
|
||||
enableSorting: false,
|
||||
enableResizing: false,
|
||||
enableFilters: true,
|
||||
enableVisibility: false,
|
||||
minWidth: 280,
|
||||
isVisible: false
|
||||
},
|
||||
{
|
||||
Header: 'Name',
|
||||
accessor: 'name',
|
||||
sortable: false,
|
||||
resizable: false,
|
||||
disableGlobalFilter: false,
|
||||
header: 'Name',
|
||||
accessorKey: 'name',
|
||||
enableSorting: false,
|
||||
enableResizing: false,
|
||||
enableFilters: true,
|
||||
enableVisibility: false,
|
||||
minWidth: 280,
|
||||
isVisible: false
|
||||
},
|
||||
{
|
||||
Header: 'ID',
|
||||
accessor: 'oid',
|
||||
sortable: false,
|
||||
resizable: false,
|
||||
disableGlobalFilter: false,
|
||||
header: 'ID',
|
||||
accessorKey: 'oid',
|
||||
enableSorting: false,
|
||||
enableResizing: false,
|
||||
enableFilters: true,
|
||||
enableVisibility: false,
|
||||
minWidth: 280,
|
||||
isVisible: false
|
||||
}
|
||||
];
|
||||
let steps = [gettext('Object Selection'), gettext('Privilege Selection'), gettext('Review')];
|
||||
const [selectedObject, setSelectedObject] = React.useState([]);
|
||||
const [selectedRows, setSelectedRows] = React.useState({});
|
||||
const [selectedAcl, setSelectedAcl] = React.useState({});
|
||||
const [msqlData, setMSQLData] = React.useState('');
|
||||
const [loaderText, setLoaderText] = React.useState('');
|
||||
const [tableData, setTableData] = React.useState([]);
|
||||
const [privOptions, setPrivOptions] = React.useState({});
|
||||
const [privileges, setPrivileges] = React.useState([]);
|
||||
const selectedObject = React.useRef([]);
|
||||
const privileges = React.useRef([]);
|
||||
const [privSchemaInstance, setPrivSchemaInstance] = React.useState();
|
||||
const [errMsg, setErrMsg] = React.useState('');
|
||||
const pgAdmin = usePgAdmin();
|
||||
@ -135,11 +136,6 @@ export default function GrantWizard({ sid, did, nodeInfo, nodeData, onClose }) {
|
||||
return !isValid;
|
||||
};
|
||||
|
||||
|
||||
React.useEffect(() => {
|
||||
privSchemaInstance?.privilegeRoleSchema.updateSupportedPrivs(privileges);
|
||||
}, [privileges]);
|
||||
|
||||
React.useEffect(() => {
|
||||
const privSchema = new PrivilegeSchema((privs) => getNodePrivilegeRoleSchema('', nodeInfo, nodeData, privs));
|
||||
setPrivSchemaInstance(privSchema);
|
||||
@ -197,7 +193,7 @@ export default function GrantWizard({ sid, did, nodeInfo, nodeData, onClose }) {
|
||||
});
|
||||
let post_data = {
|
||||
acl: selectedAcl.privilege,
|
||||
objects: selectedObject
|
||||
objects: selectedObject.current
|
||||
};
|
||||
api.post(msql_url, post_data)
|
||||
.then(res => {
|
||||
@ -219,7 +215,7 @@ export default function GrantWizard({ sid, did, nodeInfo, nodeData, onClose }) {
|
||||
});
|
||||
const post_data = {
|
||||
acl: selectedAcl.privilege,
|
||||
objects: selectedObject
|
||||
objects: selectedObject.current
|
||||
};
|
||||
api.post(_url, post_data)
|
||||
.then(() => {
|
||||
@ -233,7 +229,7 @@ export default function GrantWizard({ sid, did, nodeInfo, nodeData, onClose }) {
|
||||
};
|
||||
|
||||
const disableNextCheck = (stepId) => {
|
||||
if (selectedObject.length > 0 && stepId === 0) {
|
||||
if (Object.keys(selectedRows).length > 0 && stepId === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -244,14 +240,14 @@ export default function GrantWizard({ sid, did, nodeInfo, nodeData, onClose }) {
|
||||
window.open(url_for('help.static', { 'filename': 'grant_wizard.html' }), 'pgadmin_help');
|
||||
};
|
||||
|
||||
const getTableSelectedRows = (selRows) => {
|
||||
useEffect(()=>{
|
||||
let selObj = [];
|
||||
let objectTypes = new Set();
|
||||
if (selRows.length > 0) {
|
||||
|
||||
selRows.forEach((row) => {
|
||||
if (Object.keys(selectedRows).length > 0) {
|
||||
Object.keys(selectedRows).forEach((rowId) => {
|
||||
const row = tableData[rowId];
|
||||
let object_type = '';
|
||||
switch (row.values.object_type) {
|
||||
switch (row.object_type) {
|
||||
case 'Function':
|
||||
object_type = 'function';
|
||||
break;
|
||||
@ -284,7 +280,7 @@ export default function GrantWizard({ sid, did, nodeInfo, nodeData, onClose }) {
|
||||
}
|
||||
|
||||
objectTypes.add(object_type);
|
||||
selObj.push(row.values);
|
||||
selObj.push(row);
|
||||
});
|
||||
}
|
||||
let privs = new Set();
|
||||
@ -293,10 +289,11 @@ export default function GrantWizard({ sid, did, nodeInfo, nodeData, onClose }) {
|
||||
privs.add(priv);
|
||||
});
|
||||
});
|
||||
setPrivileges(Array.from(privs));
|
||||
setSelectedObject(selObj);
|
||||
privileges.current = Array.from(privs);
|
||||
selectedObject.current = selObj;
|
||||
privSchemaInstance?.privilegeRoleSchema.updateSupportedPrivs(privileges.current);
|
||||
setErrMsg(selObj.length === 0 ? gettext('Please select any database object.') : '');
|
||||
};
|
||||
}, [selectedRows]);
|
||||
|
||||
const onErrClose = React.useCallback(()=>{
|
||||
setErrMsg('');
|
||||
@ -316,13 +313,15 @@ export default function GrantWizard({ sid, did, nodeInfo, nodeData, onClose }) {
|
||||
<Box className={classes.panelContent}>
|
||||
<PgTable
|
||||
caveTable={false}
|
||||
tableNoBorder={false}
|
||||
className={classes.table}
|
||||
height={window.innerHeight - 450}
|
||||
columns={columns}
|
||||
data={tableData}
|
||||
isSelectRow={true}
|
||||
getSelectedRows={getTableSelectedRows}>
|
||||
</PgTable>
|
||||
hasSelectRow={true}
|
||||
selectedRows={selectedRows}
|
||||
setSelectedRows={setSelectedRows}
|
||||
/>
|
||||
</Box>
|
||||
<FormFooterMessage type={MESSAGE_TYPE.ERROR} message={errMsg} onClose={onErrClose} />
|
||||
</WizardStep>
|
||||
@ -330,17 +329,17 @@ export default function GrantWizard({ sid, did, nodeInfo, nodeData, onClose }) {
|
||||
stepId={1}
|
||||
className={clsx(classes.privilegeStep)}>
|
||||
{privSchemaInstance &&
|
||||
<SchemaView
|
||||
formType={'dialog'}
|
||||
getInitData={() => {/*This is intentional (SonarQube)*/}}
|
||||
viewHelperProps={{ mode: 'create' }}
|
||||
schema={privSchemaInstance}
|
||||
showFooter={false}
|
||||
isTabView={false}
|
||||
onDataChange={(isChanged, changedData) => {
|
||||
setSelectedAcl(changedData);
|
||||
}}
|
||||
/>
|
||||
<SchemaView
|
||||
formType={'dialog'}
|
||||
getInitData={() => {/*This is intentional (SonarQube)*/}}
|
||||
viewHelperProps={{ mode: 'create' }}
|
||||
schema={privSchemaInstance}
|
||||
showFooter={false}
|
||||
isTabView={false}
|
||||
onDataChange={(isChanged, changedData) => {
|
||||
setSelectedAcl(changedData);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
</WizardStep>
|
||||
<WizardStep
|
||||
|
@ -738,7 +738,7 @@ export function ResultGridComponent({ gridData, allRowIds, filterParams, selecte
|
||||
treeDepth={2}
|
||||
enableRowSelect={true}
|
||||
defaultColumnOptions={{
|
||||
resizable: true
|
||||
enableResizing: true
|
||||
}}
|
||||
headerRowHeight={28}
|
||||
rowHeight={28}
|
||||
|
@ -135,7 +135,7 @@ const columns = [
|
||||
},{
|
||||
key: 'path',
|
||||
name: gettext('Object path'),
|
||||
sortable: false,
|
||||
enableSorting: false,
|
||||
formatter: TypePathFormatter,
|
||||
}
|
||||
];
|
||||
@ -411,8 +411,8 @@ export default function SearchObjects({nodeData}) {
|
||||
columns={columns}
|
||||
rows={sortedItems}
|
||||
defaultColumnOptions={{
|
||||
sortable: true,
|
||||
resizable: true
|
||||
enableSorting: true,
|
||||
enableResizing: true
|
||||
}}
|
||||
headerRowHeight={28}
|
||||
rowHeight={28}
|
||||
|
@ -270,7 +270,7 @@ function initialiseColumns(columns, rows, totalRowCount, columnWidthBy) {
|
||||
/* padding 8 on both sides*/
|
||||
rowNumWidth += 16;
|
||||
let rowNumCol = {
|
||||
key: ROWNUM_KEY, name: '', frozen: true, resizable: false,
|
||||
key: ROWNUM_KEY, name: '', frozen: true, enableResizing: false,
|
||||
minWidth: 45, width: rowNumWidth,
|
||||
};
|
||||
rowNumCol.cellClass = cellClassGetter(rowNumCol);
|
||||
|
@ -116,7 +116,7 @@ class UserManagementCollection extends BaseUISchema {
|
||||
return obj.isEditable(state);
|
||||
}
|
||||
}, {
|
||||
id: 'active', label: gettext('Active'), cell: 'switch', width: 60, disableResizing: true,
|
||||
id: 'active', label: gettext('Active'), cell: 'switch', width: 60, enableResizing: false,
|
||||
editable: (state)=> {
|
||||
return obj.isEditable(state);
|
||||
}
|
||||
@ -137,7 +137,7 @@ class UserManagementCollection extends BaseUISchema {
|
||||
return obj.isEditable(state) && state.auth_source == AUTH_METHODS['INTERNAL'];
|
||||
}
|
||||
}, {
|
||||
id: 'locked', label: gettext('Locked'), cell: 'switch', width: 60, disableResizing: true,
|
||||
id: 'locked', label: gettext('Locked'), cell: 'switch', width: 60, enableResizing: false,
|
||||
editable: (state)=> {
|
||||
return state.locked;
|
||||
}
|
||||
|
@ -294,10 +294,11 @@ class PGUtilitiesBackupFeatureTest(BaseFeatureTest):
|
||||
if serv == 'pg' and server_version is not None and \
|
||||
default_binary_path['pg'] != '':
|
||||
path_input = \
|
||||
self.page.find_by_xpath(
|
||||
"//div[span[text()='PostgreSQL {}']]"
|
||||
"/following-sibling::div//div/input".format(
|
||||
server_version))
|
||||
self.page.find_by_css_selector(
|
||||
"div[class='pgrd-row-cell'][title='PostgreSQL {}']"
|
||||
"+div[class='pgrd-row-cell'] input"
|
||||
.format(server_version)
|
||||
)
|
||||
existing_path = path_input.get_property("value")
|
||||
if existing_path != default_binary_path['pg']:
|
||||
path_already_set = False
|
||||
|
@ -54,8 +54,8 @@ def open_process_details(tester):
|
||||
time.sleep(3)
|
||||
tester.page.find_by_css_selector(
|
||||
"div[data-test='processes'] "
|
||||
"div[data-test='row-container']:nth-child(1) "
|
||||
"div[role='row'] div[role='cell']:nth-child(3) button").click()
|
||||
"div[role='row']:nth-child(1) "
|
||||
"div[role='cell']:nth-child(3) button").click()
|
||||
|
||||
tester.page.wait_for_element_to_disappear(
|
||||
lambda driver: driver.find_element(
|
||||
|
@ -3173,6 +3173,44 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tanstack/react-table@npm:^8.16.0":
|
||||
version: 8.16.0
|
||||
resolution: "@tanstack/react-table@npm:8.16.0"
|
||||
dependencies:
|
||||
"@tanstack/table-core": 8.16.0
|
||||
peerDependencies:
|
||||
react: ">=16.8"
|
||||
react-dom: ">=16.8"
|
||||
checksum: 9a80668ba7531b49425d3c08fe34fbd4bbcdf936fbca120114d2d090013242c3ea1b573c1381719289600bc866f2ded9e3e13c7c4923285d2cf4eee1c1d489e7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tanstack/react-virtual@npm:^3.4.0":
|
||||
version: 3.4.0
|
||||
resolution: "@tanstack/react-virtual@npm:3.4.0"
|
||||
dependencies:
|
||||
"@tanstack/virtual-core": 3.4.0
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
checksum: 73c272d4a6242d1e49e37e56cec3985d8ff3108e8cd33d2f0c8c3f6ec3d84fece50813e875ee15ee025de4c47f051fcdf8829b1a9c824fd360ae0a569eddb1f5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tanstack/table-core@npm:8.16.0":
|
||||
version: 8.16.0
|
||||
resolution: "@tanstack/table-core@npm:8.16.0"
|
||||
checksum: c2c33c542c60788eb90806feb8f1f0340aa565ef9bb031bc5562d43598394a4089d61007f55ed6ab1fa16bbe93228cb2673b564ecf90b416bf463f42a56f3d85
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tanstack/virtual-core@npm:3.4.0":
|
||||
version: 3.4.0
|
||||
resolution: "@tanstack/virtual-core@npm:3.4.0"
|
||||
checksum: dee878179e504bb2d62ffb36a97e1c37e777af1802cadb03566b732099b13d7d053590997e3c17ce098bcb2adf527f53f69795d38cb607e308cde21dc809702e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@testing-library/dom@npm:^8.0.0":
|
||||
version: 8.20.1
|
||||
resolution: "@testing-library/dom@npm:8.20.1"
|
||||
@ -13769,15 +13807,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-table@npm:^7.6.3":
|
||||
version: 7.8.0
|
||||
resolution: "react-table@npm:7.8.0"
|
||||
peerDependencies:
|
||||
react: ^16.8.3 || ^17.0.0-0 || ^18.0.0
|
||||
checksum: 44ca0fb848c6869cd793cede8dc33072b38ebb8f8d2833565afe7cf3eac5d1fa455ac5fb9d06838b16fab0523d5d03e3e82f7645032f71245096e67b892313b9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-timer-hook@npm:^3.0.5":
|
||||
version: 3.0.7
|
||||
resolution: "react-timer-hook@npm:3.0.7"
|
||||
@ -13822,7 +13851,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-window@npm:^1.3.1, react-window@npm:^1.8.10, react-window@npm:^1.8.5":
|
||||
"react-window@npm:^1.3.1, react-window@npm:^1.8.10":
|
||||
version: 1.8.10
|
||||
resolution: "react-window@npm:1.8.10"
|
||||
dependencies:
|
||||
@ -14265,6 +14294,8 @@ __metadata:
|
||||
"@simonwep/pickr": ^1.5.1
|
||||
"@svgr/webpack": ^8.1.0
|
||||
"@szhsin/react-menu": ^2.2.0
|
||||
"@tanstack/react-table": ^8.16.0
|
||||
"@tanstack/react-virtual": ^3.4.0
|
||||
"@testing-library/jest-dom": ^6.1.2
|
||||
"@testing-library/react": 12
|
||||
"@testing-library/user-event": ^14.4.3
|
||||
@ -14356,10 +14387,8 @@ __metadata:
|
||||
react-resize-detector: ^9.1.0
|
||||
react-rnd: ^10.3.5
|
||||
react-select: ^5.7.2
|
||||
react-table: ^7.6.3
|
||||
react-timer-hook: ^3.0.5
|
||||
react-virtualized-auto-sizer: ^1.0.6
|
||||
react-window: ^1.8.5
|
||||
resize-observer-polyfill: ^1.5.1
|
||||
shim-loader: ^1.0.1
|
||||
snapsvg-cjs: ^0.0.6
|
||||
|
Loading…
Reference in New Issue
Block a user