????

Your IP : 216.73.216.205


Current Path : C:/opt/pgsql/pgAdmin 4/web/pgadmin/misc/properties/
Upload File :
Current File : C:/opt/pgsql/pgAdmin 4/web/pgadmin/misc/properties/CollectionNodeProperties.jsx

/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import React from 'react';
import getApiInstance from 'sources/api_instance';
import { makeStyles } from '@mui/styles';
import { Box } from '@mui/material';
import { generateCollectionURL } from '../../browser/static/js/node_ajax';
import gettext from 'sources/gettext';
import PgTable from 'sources/components/PgTable';
import Theme from 'sources/Theme';
import PropTypes from 'prop-types';
import { PgButtonGroup, PgIconButton } from '../../static/js/components/Buttons';
import DeleteIcon from '@mui/icons-material/Delete';
import DeleteSweepIcon from '@mui/icons-material/DeleteSweep';
import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
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';

const useStyles = makeStyles((theme) => ({
  emptyPanel: {
    minHeight: '100%',
    minWidth: '100%',
    background: theme.otherVars.emptySpaceBg,
    overflow: 'auto',
    padding: '8px',
    display: 'flex',
  },
  panelIcon: {
    width: '80%',
    margin: '0 auto',
    marginTop: '25px !important',
    position: 'relative',
    textAlign: 'center',
  },
  panelMessage: {
    marginLeft: '0.5rem',
    fontSize: '0.875rem',
  },
  searchPadding: {
    flex: 2.5
  },
  searchInput: {
    flex: 1,
    margin: '4 0 4 0',
    borderLeft: 'none',
    paddingLeft: 5
  },
  propertiesPanel: {
    height: '100%'
  },
  autoResizer: {
    height: '100% !important',
    width: '100% !important',
    background: theme.palette.grey[400],
    padding: '8px',
    overflow: 'hidden !important',
    overflowX: 'auto !important'
  },
}));

export default function CollectionNodeProperties({
  node,
  treeNodeInfo,
  nodeData,
  nodeItem,
  isActive,
  isStale,
  setIsStale
}) {
  const classes = useStyles();
  const pgAdmin = usePgAdmin();

  const [data, setData] = React.useState([]);
  const [infoMsg, setInfoMsg] = React.useState('Please select an object in the tree view.');
  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: 'value',
      accessor: 'value',
      sortable: true,
      resizable: true,
      disableGlobalFilter: false,
    },
  ]);

  const getTableSelectedRows = (selRows) => {
    setSelectedObject(selRows);
  };

  const onDrop = (type) => {
    let selRowModels = selectedObject,
      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]);
    }

    if (selRows.length === 0) {
      pgAdmin.Browser.notifier.alert(
        gettext('Delete Multiple'),
        gettext('Please select at least one object to delete.')
      );
      return;
    }

    if (!selNode) return;

    if (type === 'dropCascade') {
      url = selNode.generate_url(selItem, 'delete');
      msg = gettext(
        'Are you sure you want to delete all the selected objects and all the objects that depend on them?'
      );
      title = gettext('Delete CASCADE multiple objects?');
    } else if (type === 'dropForce') {
      url = selNode.generate_url(selItem, 'delete');
      msg = gettext(
        'Delete databases with the force option will attempt to terminate all the existing connections to the selected databases. Are you sure you want to proceed?'
      );
      title = gettext('Delete FORCE multiple objects?');
    } else {
      url = selNode.generate_url(selItem, 'drop');
      msg = gettext('Are you sure you want to delete all the selected objects?');
      title = gettext('Delete multiple objects?');
    }

    const api = getApiInstance();
    let dropNodeProperties = function () {
      setLoaderText(gettext('Deleting Objects...'));
      api
        .delete(url, {
          data: JSON.stringify({ ids: selRows }),
          contentType: 'application/json; charset=utf-8',
        })
        .then(function (res) {
          if (res.success == 0) {
            pgAdmin.Browser.notifier.alert(res.errormsg, res.info);
          }
          pgAdmin.Browser.tree.refresh(selItem);
          setIsStale(true);
        })
        .catch(function (error) {
          pgAdmin.Browser.notifier.alert(
            gettext('Error deleting %s', selectedItemData._label.toLowerCase()),
            _.isUndefined(error.response) ? error.message : error.response.data.errormsg
          );
        })
        .then(()=>{
          setLoaderText('');
        });
    };

    if (confirm) {
      pgAdmin.Browser.notifier.confirm(title, msg, dropNodeProperties, null);
    } else {
      dropNodeProperties();
    }
  };

  React.useEffect(() => {
    if (node) {

      let nodeObj =
      pgAdmin.Browser.Nodes[nodeData?._type.replace('coll-', '')];

      let url = generateCollectionURL.call(nodeObj, nodeItem, 'properties');

      const api = getApiInstance();

      let tableColumns = [];
      let column = {};
      if(!isStale || !isActive) {
        return;
      }

      setLoaderText(gettext('Loading...'));

      if (!_.isUndefined(nodeObj.getSchema)) {
        schemaRef.current = nodeObj.getSchema?.(treeNodeInfo, nodeData);
        schemaRef.current?.fields.forEach((field) => {
          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()
              };
            } else {
              column = {
                Header: field.label,
                accessor: field.id,
                sortable: true,
                resizable: true,
                disableGlobalFilter: false,
                minWidth: 0,
              };
            }
            tableColumns.push(column);
          }
        });
      }else{
        node.columns.forEach((field) => {
          column = {
            Header: field,
            accessor: field,
            sortable: true,
            resizable: true,
            disableGlobalFilter: false,
            minWidth: 0,
          };
          tableColumns.push(column);
        });
      }


      api({
        url: url,
        type: 'GET',
      })
        .then((res) => {
          res.data.forEach((element) => {
            element['icon'] = '';
          });
          setPgTableColumns(tableColumns);
          setData(res.data);
          setInfoMsg('No properties are available for the selected object.');
          setLoaderText('');
        })
        .catch((err) => {
          pgAdmin.Browser.notifier.alert(
            gettext('Failed to retrieve data from the server.'),
            gettext(err.message)
          );
        });
      setIsStale(false);
    }
  }, [nodeData, node, nodeItem, isStale, isActive]);

  const CustomHeader = () => {
    const canDrop = evalFunc(node, node.canDrop, nodeData, nodeItem, treeNodeInfo);
    const canDropCascade = evalFunc(node, node.canDropCascade, nodeData, nodeItem, treeNodeInfo);
    const canDropForce = evalFunc(node, node.canDropForce, nodeData, nodeItem, treeNodeInfo);
    return (
      <Box >
        <PgButtonGroup size="small">
          <PgIconButton
            icon={<DeleteIcon style={{height: '1.35rem'}}/>}
            aria-label="Delete"
            title={gettext('Delete')}
            onClick={() => {
              onDrop('drop');
            }}
            disabled={
              (selectedObject.length > 0)
                ? !canDrop
                : true
            }
          ></PgIconButton>
          {node.type !== 'coll-database' ? <PgIconButton
            icon={<DeleteSweepIcon style={{height: '1.5rem'}} />}
            aria-label="Delete Cascade"
            title={gettext('Delete (Cascade)')}
            onClick={() => {
              onDrop('dropCascade');
            }}
            disabled={
              (selectedObject.length > 0)
                ? !canDropCascade
                : true
            }
          ></PgIconButton> :
            <PgIconButton
              icon={<DeleteForeverIcon style={{height: '1.4rem'}} />}
              aria-label="Delete Force"
              title={gettext('Delete (Force)')}
              onClick={() => {
                onDrop('dropForce');
              }}
              disabled={
                (selectedObject.length > 0)
                  ? !canDropForce
                  : true
              }
            ></PgIconButton>}
        </PgButtonGroup>
      </Box>);
  };

  return (
    <Theme className='obj_properties'>
      <Loader message={loaderText}/>
      <Box className={classes.propertiesPanel}>
        {data.length > 0 ?
          (
            <PgTable
              isSelectRow={!('catalog' in treeNodeInfo) && (nodeData.label !== 'Catalogs') && _.isUndefined(node?.canSelect)}
              CustomHeader={CustomHeader}
              className={classes.autoResizer}
              columns={pgTableColumns}
              data={data}
              type={'panel'}
              isSearch={false}
              getSelectedRows={getTableSelectedRows}
            />
          )
          :
          (
            <div className={classes.emptyPanel}>
              <EmptyPanelMessage text={gettext(infoMsg)}/>
            </div>
          )
        }
      </Box>
    </Theme>
  );
}

CollectionNodeProperties.propTypes = {
  node: PropTypes.func,
  nodeData: PropTypes.object,
  treeNodeInfo: PropTypes.object,
  nodeItem: PropTypes.object,
  isActive: PropTypes.bool,
  isStale: PropTypes.bool,
  setIsStale: PropTypes.func,
};