diff --git a/web/package.json b/web/package.json index b33f84fd5..d0aa9be77 100644 --- a/web/package.json +++ b/web/package.json @@ -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", diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table.ui.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table.ui.js index 4bbb8f667..7fc5b9032 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table.ui.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/foreign_tables/static/js/foreign_table.ui.js @@ -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); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/columns/static/js/column.ui.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/columns/static/js/column.ui.js index ef7c0fd4a..b89d6ac56 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/columns/static/js/column.ui.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/columns/static/js/column.ui.js @@ -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) => { diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/static/js/exclusion_constraint.ui.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/static/js/exclusion_constraint.ui.js index c78268c9b..6fc3caf94 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/static/js/exclusion_constraint.ui.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/exclusion_constraint/static/js/exclusion_constraint.ui.js @@ -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, diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index.ui.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index.ui.js index f7c3edeeb..e56f6a908 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index.ui.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/js/index.ui.js @@ -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}; } diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/partition.utils.ui.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/partition.utils.ui.js index 52c819ae2..12c04b1ff 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/partition.utils.ui.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/partition.utils.ui.js @@ -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) diff --git a/web/pgadmin/dashboard/static/js/Dashboard.jsx b/web/pgadmin/dashboard/static/js/Dashboard.jsx index 6b59d6bba..bc64acb2d 100644 --- a/web/pgadmin/dashboard/static/js/Dashboard.jsx +++ b/web/pgadmin/dashboard/static/js/Dashboard.jsx @@ -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({ @@ -904,6 +897,7 @@ function Dashboard({ @@ -911,6 +905,7 @@ function Dashboard({ diff --git a/web/pgadmin/dashboard/static/js/Replication/index.jsx b/web/pgadmin/dashboard/static/js/Replication/index.jsx index abc772ac6..e13cd3047 100644 --- a/web/pgadmin/dashboard/static/js/Replication/index.jsx +++ b/web/pgadmin/dashboard/static/js/Replication/index.jsx @@ -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(), } ]; diff --git a/web/pgadmin/dashboard/static/js/SystemStats/CPU.jsx b/web/pgadmin/dashboard/static/js/SystemStats/CPU.jsx index b7888d7aa..656ab7434 100644 --- a/web/pgadmin/dashboard/static/js/SystemStats/CPU.jsx +++ b/web/pgadmin/dashboard/static/js/SystemStats/CPU.jsx @@ -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, }, ]; diff --git a/web/pgadmin/dashboard/static/js/SystemStats/Memory.jsx b/web/pgadmin/dashboard/static/js/SystemStats/Memory.jsx index 0f78c7be2..442d25cdf 100644 --- a/web/pgadmin/dashboard/static/js/SystemStats/Memory.jsx +++ b/web/pgadmin/dashboard/static/js/SystemStats/Memory.jsx @@ -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, }, ]; diff --git a/web/pgadmin/dashboard/static/js/SystemStats/Storage.jsx b/web/pgadmin/dashboard/static/js/SystemStats/Storage.jsx index 37b164527..7a44951b2 100644 --- a/web/pgadmin/dashboard/static/js/SystemStats/Storage.jsx +++ b/web/pgadmin/dashboard/static/js/SystemStats/Storage.jsx @@ -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', }, ]; diff --git a/web/pgadmin/dashboard/static/js/components/SectionContainer.jsx b/web/pgadmin/dashboard/static/js/components/SectionContainer.jsx index 6077b00b3..fd920c8d3 100644 --- a/web/pgadmin/dashboard/static/js/components/SectionContainer.jsx +++ b/web/pgadmin/dashboard/static/js/components/SectionContainer.jsx @@ -46,7 +46,7 @@ export default function SectionContainer({title, titleExtras, children, style}) {titleExtras} - + {children} diff --git a/web/pgadmin/misc/bgprocess/static/js/Processes.jsx b/web/pgadmin/misc/bgprocess/static/js/Processes.jsx index 97e15adc7..893155f79 100644 --- a/web/pgadmin/misc/bgprocess/static/js/Processes.jsx +++ b/web/pgadmin/misc/bgprocess/static/js/Processes.jsx @@ -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 ( 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} > } diff --git a/web/pgadmin/misc/dependencies/static/js/Dependencies.jsx b/web/pgadmin/misc/dependencies/static/js/Dependencies.jsx index 97c588613..25e462bfd 100644 --- a/web/pgadmin/misc/dependencies/static/js/Dependencies.jsx +++ b/web/pgadmin/misc/dependencies/static/js/Dependencies.jsx @@ -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, }, ]; diff --git a/web/pgadmin/misc/dependents/static/js/Dependents.jsx b/web/pgadmin/misc/dependents/static/js/Dependents.jsx index 8f3e7511c..a0867aed8 100644 --- a/web/pgadmin/misc/dependents/static/js/Dependents.jsx +++ b/web/pgadmin/misc/dependents/static/js/Dependents.jsx @@ -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, }, ]; diff --git a/web/pgadmin/misc/properties/CollectionNodeProperties.jsx b/web/pgadmin/misc/properties/CollectionNodeProperties.jsx index 6238692c5..8eddec1bb 100644 --- a/web/pgadmin/misc/properties/CollectionNodeProperties.jsx +++ b/web/pgadmin/misc/properties/CollectionNodeProperties.jsx @@ -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 ? ( ) : diff --git a/web/pgadmin/misc/statistics/static/js/Statistics.jsx b/web/pgadmin/misc/statistics/static/js/Statistics.jsx index e50e056a3..7eab4f84b 100644 --- a/web/pgadmin/misc/statistics/static/js/Statistics.jsx +++ b/web/pgadmin/misc/statistics/static/js/Statistics.jsx @@ -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(); diff --git a/web/pgadmin/static/js/SchemaView/DataGridView.jsx b/web/pgadmin/static/js/SchemaView/DataGridView.jsx index 1d5f9dbe8..1b1fb8eb7 100644 --- a/web/pgadmin/static/js/SchemaView/DataGridView.jsx +++ b/web/pgadmin/static/js/SchemaView/DataGridView.jsx @@ -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 ( -
- {headerGroups.map((headerGroup, hi) => ( -
- {headerGroup.headers.map((column, ci) => { - let {modeSupported} = column.field ? getFieldMetaData(column.field, schemaRef.current, {}, viewHelperProps) : {modeSupported: true}; - return( modeSupported && -
-
- {column.render('Header')} - - {column.isSorted ? sortIcon(column.isSortedDesc) : ''} - -
- {!column.disableResizing && -
} -
- ); - })} -
- ))} -
- ); -} - -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(()=> -
- {row.cells.map((cell, ci) => { - let classNames = [classes.tableCell]; + + {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 && -
- {cell.render('Cell', { - reRenderRow: ()=>{setKey((currKey)=>!currKey);} - })} -
+ + {content} + ); })}
-
, depsMap); + , 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
; } }; - 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 } 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 && ; }, }; - 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) && { - setGlobalFilter(value || undefined); + setSearchVal(value || undefined); }} />} -
({style: {minWidth: 'unset'}}))} className={classes.table} data-test="data-grid-view"> - -
+ + + {rows.map((row, i) => { - prepareRow(row); - return - + - {props.canEdit && row.isExpanded && - { - requestAnimationAndFocus(ele); - }}/> + moveRow={moveRow} isHovered={i == hoverIndex} setHoverIndex={setHoverIndex} viewHelperProps={viewHelperProps} + /> + {props.canEdit && + + { + requestAnimationAndFocus(ele); + }}/> + } - ; + ; })} -
-
+ +
); } +export default function DataGridViewMoized({memoDeps, ...props}) { + return useMemo(()=>, memoDeps??[]); +} + +DataGridViewMoized.propTypes = { + memoDeps: PropTypes.array, +}; + DataGridView.propTypes = { label: PropTypes.string, value: PropTypes.array, diff --git a/web/pgadmin/static/js/SchemaView/FormView.jsx b/web/pgadmin/static/js/SchemaView/FormView.jsx index 5506bf07c..41bfa3d4c 100644 --- a/web/pgadmin/static/js/SchemaView/FormView.jsx +++ b/web/pgadmin/static/js/SchemaView/FormView.jsx @@ -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(); + tabs[group].push(); } else { - tabs[group].push(); + tabs[group].push(value[dep]), + ]} />); } } else { /* Its a form control */ diff --git a/web/pgadmin/static/js/SchemaView/MappedControl.jsx b/web/pgadmin/static/js/SchemaView/MappedControl.jsx index 127456d13..fd032406e 100644 --- a/web/pgadmin/static/js/SchemaView/MappedControl.jsx +++ b/web/pgadmin/static/js/SchemaView/MappedControl.jsx @@ -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, diff --git a/web/pgadmin/static/js/SchemaView/index.jsx b/web/pgadmin/static/js/SchemaView/index.jsx index 09d2de52e..a3815b7e7 100644 --- a/web/pgadmin/static/js/SchemaView/index.jsx +++ b/web/pgadmin/static/js/SchemaView/index.jsx @@ -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') { diff --git a/web/pgadmin/static/js/Theme/index.jsx b/web/pgadmin/static/js/Theme/index.jsx index a6e264b7f..52cda3121 100644 --- a/web/pgadmin/static/js/Theme/index.jsx +++ b/web/pgadmin/static/js/Theme/index.jsx @@ -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', diff --git a/web/pgadmin/static/js/components/PgReactTableStyled.jsx b/web/pgadmin/static/js/components/PgReactTableStyled.jsx new file mode 100644 index 000000000..08345a890 --- /dev/null +++ b/web/pgadmin/static/js/components/PgReactTableStyled.jsx @@ -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 ( +
+
{children}
+
+ ); +}); + +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 ( +
+ {children} +
+ ); +}); +PgReactTableRow.displayName = 'PgReactTableRow'; +PgReactTableRow.propTypes = { + children: CustomPropTypes.children, + className: PropTypes.any, +}; + +export const PgReactTableRowContent = forwardRef(({children, className, ...props}, ref)=>{ + return ( +
+ {children} +
+ ); +}); +PgReactTableRowContent.displayName = 'PgReactTableRowContent'; +PgReactTableRowContent.propTypes = { + children: CustomPropTypes.children, + className: PropTypes.any, +}; + + +export function PgReactTableRowExpandContent({row, children}) { + if(!row.getIsExpanded()) { + return <>; + } + return ( +
+ {children} +
+ ); +} +PgReactTableRowExpandContent.propTypes = { + row: PropTypes.object, + children: CustomPropTypes.children, +}; + +export function PgReactTableHeader({table}) { + return ( +
+ {table.getHeaderGroups().map((headerGroup) => ( +
+ {headerGroup.headers.map((header) => ( +
+
+ {flexRender(header.column.columnDef.header, header.getContext())} + {header.column.getCanSort() && header.column.getIsSorted() && + + {header.column.getIsSorted() == 'desc' ? + + : } + } +
+ {header.column.getCanResize() && ( +
header.column.resetSize()} + onMouseDown={header.getResizeHandler()} + onTouchStart={header.getResizeHandler()} + className='pgrt-header-resizer' + /> + )} +
+ ))} +
+ ))} +
+ ); +} +PgReactTableHeader.propTypes = { + table: PropTypes.object, +}; + +export function PgReactTableBody({children, style}) { + return ( +
+ {children} +
+ ); +} +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 ( + +
+ {children} +
+
+ ); +}); +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 ( + + ) : ( + + ) + } + 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 ; + }; + + Cell.displayName = 'SwitchCell'; + Cell.propTypes = { + value: PropTypes.any, + }; + + return Cell; +} diff --git a/web/pgadmin/static/js/components/PgTable.jsx b/web/pgadmin/static/js/components/PgTable.jsx index 9d03e7843..2bb0586b6 100644 --- a/web/pgadmin/static/js/components/PgTable.jsx +++ b/web/pgadmin/static/js/components/PgTable.jsx @@ -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 ( + + + {row.getVisibleCells().map((cell) => { + const content = flexRender(cell.column.columnDef.cell, cell.getContext()); + + return ( + + {content} + + ); + })} + + + Promise.resolve(row.original)} + viewHelperProps={{ mode: 'properties' }} + schema={schema} + showFooter={false} + onDataChange={() => { setExpandComplete(true); }} + /> + + + ); +} +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 ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
+ +
+ ), + 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 ( + + + {rows.length == 0 ? + : + + {virtualizer.getVirtualItems().map((virtualRow) => { + const row = rows[virtualRow.index]; + return ; + })} + } + + ); +} +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 ( - - ); - }, -); - -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 ? : ; - } - 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 ( -
-
-
- {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 ( -
- {cell.render('Cell')} -
- ); - })} -
- {!_.isUndefined(row) && row.isExpanded && ( - - {schema && Promise.resolve(row.original)} - viewHelperProps={{ mode: 'properties' }} - schema={schema[row.id]??schema} - showFooter={false} - onDataChange={()=>{setExpandComplete(true);}} - />} - {ExpandedComponent && setExpandComplete(true)}/>} - - )} -
-
- ); -} -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( -
- -
- );}, - // The cell can use the individual row's getToggleRowSelectedProps method - // to the render a checkbox - Cell: ({ row }) => ( -
- -
- ), - 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 ( - - - {props.CustomHeader && ( )} + + + {props.CustomHeader && ( )} { setSearchVal(val); @@ -492,149 +252,16 @@ export default function PgTable({ columns, data, isSelectRow, caveTable=true, sc /> -
-
-
- {headerGroups.map((headerGroup) => ( -
({ - style: { - ...column.style, - height: '40px', - } - }))}> - {headerGroup.headers.map((column) => ( -
-
- {column.render('Header')} - - - -
- {column.resizable && ( -
- )} -
- ))} -
- ))} -
- { - data.length > 0 ? ( -
- - {({ height }) => ( - - {({index, style})=>( - - )} - )} - -
- ) : ( - - ) - } -
+
+ - + ); } 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 ( - - ) : ( - - ) - } - 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 ; - }; - - Cell.displayName = 'SwitchCell'; - Cell.propTypes = { - value: PropTypes.any, - }; - - return Cell; -} +}; \ No newline at end of file diff --git a/web/pgadmin/tools/grant_wizard/static/js/GrantWizard.jsx b/web/pgadmin/tools/grant_wizard/static/js/GrantWizard.jsx index 8804e4798..5ee44910f 100644 --- a/web/pgadmin/tools/grant_wizard/static/js/GrantWizard.jsx +++ b/web/pgadmin/tools/grant_wizard/static/js/GrantWizard.jsx @@ -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 }) { - + hasSelectRow={true} + selectedRows={selectedRows} + setSelectedRows={setSelectedRows} + /> @@ -330,17 +329,17 @@ export default function GrantWizard({ sid, did, nodeInfo, nodeData, onClose }) { stepId={1} className={clsx(classes.privilegeStep)}> {privSchemaInstance && - {/*This is intentional (SonarQube)*/}} - viewHelperProps={{ mode: 'create' }} - schema={privSchemaInstance} - showFooter={false} - isTabView={false} - onDataChange={(isChanged, changedData) => { - setSelectedAcl(changedData); - }} - /> + {/*This is intentional (SonarQube)*/}} + viewHelperProps={{ mode: 'create' }} + schema={privSchemaInstance} + showFooter={false} + isTabView={false} + onDataChange={(isChanged, changedData) => { + setSelectedAcl(changedData); + }} + /> } { 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; } diff --git a/web/regression/feature_tests/pg_utilities_backup_restore_test.py b/web/regression/feature_tests/pg_utilities_backup_restore_test.py index 943ab3701..888848679 100644 --- a/web/regression/feature_tests/pg_utilities_backup_restore_test.py +++ b/web/regression/feature_tests/pg_utilities_backup_restore_test.py @@ -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 diff --git a/web/regression/python_test_utils/test_gui_helper.py b/web/regression/python_test_utils/test_gui_helper.py index f6b5d6321..8d5440e25 100644 --- a/web/regression/python_test_utils/test_gui_helper.py +++ b/web/regression/python_test_utils/test_gui_helper.py @@ -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( diff --git a/web/yarn.lock b/web/yarn.lock index db63c1f1e..7bb0c4854 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -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