///////////////////////////////////////////////////////////// // // pgAdmin 4 - PostgreSQL Tools // // Copyright (C) 2013 - 2022, The pgAdmin Development Team // This software is released under the PostgreSQL Licence // ////////////////////////////////////////////////////////////// import React from 'react'; import pgAdmin from 'sources/pgadmin'; import getApiInstance from 'sources/api_instance'; import { makeStyles } from '@material-ui/core/styles'; import { Box, Switch } from '@material-ui/core'; import { generateCollectionURL } from '../../browser/static/js/node_ajax'; import Notify from '../../static/js/helpers/Notifier'; import gettext from 'sources/gettext'; import 'wcdocker'; import PgTable from 'sources/components/PgTable'; import Theme from 'sources/Theme'; import PropTypes from 'prop-types'; import { PgIconButton } from '../../static/js/components/Buttons'; import DeleteIcon from '@material-ui/icons/Delete'; import DeleteSweepIcon from '@material-ui/icons/DeleteSweep'; import EmptyPanelMessage from '../../static/js/components/EmptyPanelMessage'; import Loader from 'sources/components/Loader'; 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' }, dropButton: { marginRight: '8px !important' }, readOnlySwitch: { opacity: 0.75, '& .MuiSwitch-track': { opacity: theme.palette.action.disabledOpacity, } } })); export function CollectionNodeView({ node, treeNodeInfo, itemNodeData, item, pgBrowser }) { const classes = useStyles(); const [data, setData] = React.useState([]); const [infoMsg, setInfoMsg] = React.useState('Please select an object in the tree view.'); const [selectedObject, setSelectedObject] = React.useState([]); const [reload, setReload] = React.useState(false); const [loaderText, setLoaderText] = React.useState(''); //Reload the collection node on refresh or change in children count React.useEffect(() => { setReload(!reload); }, [item?._children]); 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 = pgBrowser.tree.selected(), selectedItemData = selItem ? pgBrowser.tree.itemData(selItem) : null, selNode = selectedItemData && pgBrowser.Nodes[selectedItemData._type], url = undefined, msg = undefined, title = undefined; if (selNode && selNode.type && 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.oid); } if (selRows.length === 0) { Notify.alert( gettext('Drop 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 drop all the selected objects and all the objects that depend on them?' ); title = gettext('DROP CASCADE multiple objects?'); } else { url = selNode.generate_url(selItem, 'drop'); msg = gettext('Are you sure you want to drop all the selected objects?'); title = gettext('DROP multiple objects?'); } const api = getApiInstance(); let dropNodeProperties = function () { api .delete(url, { data: JSON.stringify({ ids: selRows }), contentType: 'application/json; charset=utf-8', }) .then(function (res) { if (res.success == 0) { pgBrowser.report_error(res.errormsg, res.info); } pgAdmin.Browser.tree.refresh(selItem); setReload(!reload); }) .catch(function (error) { Notify.alert( gettext('Error dropping %s', selectedItemData._label.toLowerCase()), _.isUndefined(error.response) ? error.message : error.response.data.errormsg ); }); }; if (confirm) { Notify.confirm(title, msg, dropNodeProperties, null); } else { dropNodeProperties(); } }; React.useEffect(() => { if (node){ let nodeObj = pgAdmin.Browser.Nodes[itemNodeData?._type.replace('coll-', '')]; let url = generateCollectionURL.call(nodeObj, item, 'properties'); const api = getApiInstance(); let tableColumns = []; let column = {}; setLoaderText('Loading...'); if (itemNodeData._type.indexOf('coll-') > -1 && !_.isUndefined(nodeObj.getSchema)) { let schema = nodeObj.getSchema?.call(nodeObj, treeNodeInfo, itemNodeData); schema.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, // eslint-disable-next-line react/display-name Cell: ({ value }) => { return (); } }; } 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) => { Notify.alert( gettext('Failed to retrieve data from the server.'), gettext(err.message) ); }); } }, [itemNodeData, node, item, reload]); const CustomHeader = () => { return ( } aria-label="Delete/Drop" title={gettext('Delete/Drop')} onClick={() => { onDrop('drop'); }} disabled={ (selectedObject.length > 0) ? !node.canDrop : true } > } aria-label="Drop Cascade" title={gettext('Drop Cascade')} onClick={() => { onDrop('dropCascade'); }} disabled={ (selectedObject.length > 0) ? !node.canDropCascade : true } > ); }; return ( {data.length > 0 ? ( ) : (
{loaderText ? () : }
) }
); } CollectionNodeView.propTypes = { node: PropTypes.func, itemData: PropTypes.object, itemNodeData: PropTypes.object, treeNodeInfo: PropTypes.object, item: PropTypes.object, pgBrowser: PropTypes.object, preferences: PropTypes.object, sid: PropTypes.number, did: PropTypes.number, row: PropTypes.object, serverConnected: PropTypes.bool, value: PropTypes.bool, };